rdl 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rails_types.rb +1 -0
  3. data/lib/rdl.rb +56 -0
  4. data/lib/rdl/config.rb +121 -0
  5. data/lib/rdl/contracts/and.rb +29 -0
  6. data/lib/rdl/contracts/contract.rb +7 -0
  7. data/lib/rdl/contracts/flat.rb +31 -0
  8. data/lib/rdl/contracts/or.rb +25 -0
  9. data/lib/rdl/contracts/proc.rb +24 -0
  10. data/lib/rdl/switch.rb +20 -0
  11. data/lib/rdl/types/annotated_arg.rb +41 -0
  12. data/lib/rdl/types/finitehash.rb +81 -0
  13. data/lib/rdl/types/generic.rb +100 -0
  14. data/lib/rdl/types/intersection.rb +66 -0
  15. data/lib/rdl/types/lexer.rex +39 -0
  16. data/lib/rdl/types/lexer.rex.rb +148 -0
  17. data/lib/rdl/types/method.rb +219 -0
  18. data/lib/rdl/types/nil.rb +50 -0
  19. data/lib/rdl/types/nominal.rb +80 -0
  20. data/lib/rdl/types/optional.rb +54 -0
  21. data/lib/rdl/types/parser.racc +150 -0
  22. data/lib/rdl/types/parser.tab.rb +654 -0
  23. data/lib/rdl/types/singleton.rb +62 -0
  24. data/lib/rdl/types/structural.rb +72 -0
  25. data/lib/rdl/types/top.rb +50 -0
  26. data/lib/rdl/types/tuple.rb +61 -0
  27. data/lib/rdl/types/type.rb +24 -0
  28. data/lib/rdl/types/type_inferencer.rb +73 -0
  29. data/lib/rdl/types/union.rb +74 -0
  30. data/lib/rdl/types/var.rb +51 -0
  31. data/lib/rdl/types/vararg.rb +54 -0
  32. data/lib/rdl/types/wild.rb +26 -0
  33. data/lib/rdl/util.rb +56 -0
  34. data/lib/rdl/wrap.rb +505 -0
  35. data/lib/rdl_types.rb +2 -0
  36. data/types/other/chronic.rb +5 -0
  37. data/types/other/paperclip_attachment.rb +7 -0
  38. data/types/other/securerandom.rb +4 -0
  39. data/types/rails-4.2.1/fixnum.rb +3 -0
  40. data/types/rails-4.2.1/string.rb +3 -0
  41. data/types/rails-tmp/action_dispatch.rb +406 -0
  42. data/types/rails-tmp/active_record.rb +406 -0
  43. data/types/rails-tmp/devise_contracts.rb +216 -0
  44. data/types/ruby-2.2.0/_aliases.rb +4 -0
  45. data/types/ruby-2.2.0/abbrev.rb +5 -0
  46. data/types/ruby-2.2.0/array.rb +137 -0
  47. data/types/ruby-2.2.0/base64.rb +10 -0
  48. data/types/ruby-2.2.0/basic_object.rb +13 -0
  49. data/types/ruby-2.2.0/benchmark.rb +11 -0
  50. data/types/ruby-2.2.0/bigdecimal.rb +15 -0
  51. data/types/ruby-2.2.0/bigmath.rb +12 -0
  52. data/types/ruby-2.2.0/class.rb +17 -0
  53. data/types/ruby-2.2.0/complex.rb +42 -0
  54. data/types/ruby-2.2.0/coverage.rb +6 -0
  55. data/types/ruby-2.2.0/csv.rb +5 -0
  56. data/types/ruby-2.2.0/date.rb +6 -0
  57. data/types/ruby-2.2.0/dir.rb +38 -0
  58. data/types/ruby-2.2.0/encoding.rb +23 -0
  59. data/types/ruby-2.2.0/enumerable.rb +98 -0
  60. data/types/ruby-2.2.0/enumerator.rb +26 -0
  61. data/types/ruby-2.2.0/exception.rb +15 -0
  62. data/types/ruby-2.2.0/file.rb +124 -0
  63. data/types/ruby-2.2.0/fileutils.rb +6 -0
  64. data/types/ruby-2.2.0/fixnum.rb +45 -0
  65. data/types/ruby-2.2.0/float.rb +54 -0
  66. data/types/ruby-2.2.0/gem.rb +245 -0
  67. data/types/ruby-2.2.0/hash.rb +72 -0
  68. data/types/ruby-2.2.0/integer.rb +31 -0
  69. data/types/ruby-2.2.0/io.rb +103 -0
  70. data/types/ruby-2.2.0/kernel.rb +89 -0
  71. data/types/ruby-2.2.0/marshal.rb +5 -0
  72. data/types/ruby-2.2.0/matchdata.rb +26 -0
  73. data/types/ruby-2.2.0/math.rb +53 -0
  74. data/types/ruby-2.2.0/numeric.rb +46 -0
  75. data/types/ruby-2.2.0/object.rb +73 -0
  76. data/types/ruby-2.2.0/pathname.rb +106 -0
  77. data/types/ruby-2.2.0/process.rb +127 -0
  78. data/types/ruby-2.2.0/random.rb +15 -0
  79. data/types/ruby-2.2.0/range.rb +38 -0
  80. data/types/ruby-2.2.0/rational.rb +31 -0
  81. data/types/ruby-2.2.0/regexp.rb +30 -0
  82. data/types/ruby-2.2.0/set.rb +58 -0
  83. data/types/ruby-2.2.0/string.rb +145 -0
  84. data/types/ruby-2.2.0/strscan.rb +7 -0
  85. data/types/ruby-2.2.0/symbol.rb +29 -0
  86. data/types/ruby-2.2.0/time.rb +68 -0
  87. data/types/ruby-2.2.0/uri.rb +18 -0
  88. data/types/ruby-2.2.0/yaml.rb +5 -0
  89. metadata +131 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e35c7df4fa4c7c7852d44fb9b82226bc58f75feb
4
+ data.tar.gz: c6c470102b2c15ff9df545c7f824fd3a354534f9
5
+ SHA512:
6
+ metadata.gz: 4093b03c90a84370d15a225f9b527786b72886bf605674673ce72a4c9fbccac39bb19b64f2b51ba087816a314b65358380f75e1f2274ec20778c5f669fff5201
7
+ data.tar.gz: 1d31f30746169d02e578e49e39f973f4828c6793f1a77f3cea0b623c7323e84949d94649b6f5d501136519f78238f4092bcfc9ab9f112adc98b571dae1d78020
@@ -0,0 +1 @@
1
+ require_rel "../types/rails-#{Rails::VERSION::STRING}/*.rb"
data/lib/rdl.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'set'
2
+ require 'delegate'
3
+ require 'require_all'
4
+
5
+ module RDL
6
+ end
7
+
8
+ require_relative 'rdl/config.rb'
9
+ def RDL.config
10
+ yield(RDL::Config.instance)
11
+ end
12
+
13
+ # Hash from class name to method name to :pre/:post/:type to array of contracts
14
+ # class names are strings (because they need to be manipulated in case they include ::)
15
+ # (class names may have Util.add_singleton_marker applied to them to indicate they're singleton classes.)
16
+ # method names are symbols
17
+ $__rdl_contracts = Hash.new
18
+
19
+ # Map from full_method_name to number of times called when wrapped
20
+ $__rdl_wrapped_calls = Hash.new 0
21
+
22
+ # Hash from class name to array of symbols that are the class's type parameters
23
+ $__rdl_type_params = Hash.new
24
+
25
+ # Hash from class name to method name to its alias method name
26
+ # class names are strings
27
+ # method names are symbols
28
+ $__rdl_aliases = Hash.new
29
+
30
+ # Set of [class, method] pairs to wrap.
31
+ # class is a string
32
+ # method is a symbol
33
+ $__rdl_to_wrap = Set.new
34
+
35
+ # List of contracts that should be applied to the next method definition
36
+ $__rdl_deferred = []
37
+
38
+ # Create switches to control whether wrapping happens and whether
39
+ # contracts are checked. These need to be created before rdl/wrap.rb
40
+ # is loaded.
41
+ require_rel 'rdl/switch.rb'
42
+ $__rdl_wrap_switch = RDL::Switch.new
43
+ $__rdl_contract_switch = RDL::Switch.new
44
+
45
+ require_rel 'rdl/types/*.rb'
46
+ require_rel 'rdl/contracts/*.rb'
47
+ require_rel 'rdl/util.rb'
48
+ require_rel 'rdl/wrap.rb'
49
+ #require_rel 'rdl/stats.rb'
50
+
51
+ $__rdl_parser = RDL::Type::Parser.new
52
+
53
+ # Hash from special type names to their values
54
+ $__rdl_special_types = {'%any' => RDL::Type::TopType.new,
55
+ '%bool' => RDL::Type::UnionType.new(RDL::Type::NominalType.new(TrueClass),
56
+ RDL::Type::NominalType.new(FalseClass)) }
data/lib/rdl/config.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'singleton'
2
+
3
+ class RDL::Config
4
+ include Singleton
5
+
6
+ attr_accessor :nowrap
7
+ attr_accessor :gather_stats
8
+
9
+ def initialize
10
+ @nowrap = Set.new
11
+ @gather_stats = true
12
+ end
13
+
14
+ def add_nowrap(*klasses)
15
+ klasses.each { |klass| @nowrap.add klass }
16
+ end
17
+
18
+ def remove_nowrap(*klasses)
19
+ klasses.each { |klass| @nowrap.delete klass }
20
+ end
21
+
22
+ # To use, copy these 3 lines to the test file of a gem
23
+ =begin
24
+ require_relative '../rdl3/rdl/lib/rdl.rb'
25
+ require_relative '../rdl3/rdl/lib/rdl_types.rb'
26
+ RDL::Config.instance.profile_stats
27
+ =end
28
+ def profile_stats(outname="/#{__FILE__[0...-3]}",outdir="")
29
+ require 'profile'
30
+ Profiler__.stop_profile # Leave setup out of stats
31
+
32
+ at_exit do
33
+ Profiler__.stop_profile
34
+ $__rdl_contract_switch.off {
35
+ puts "START."
36
+ puts "Performing Profile Analysis"
37
+ # Class Name => [Times Contract Called | Times Called | Time | Time | Class Profile]
38
+ # Implications:
39
+ # [nil] -> Method exists in object space, but not used
40
+ # [-1] -> Contract exists for method, but method not profiled
41
+ # [-1, ...] -> Method profiled, but no contract exists
42
+ totals = {}
43
+
44
+ puts "Retrieving Profiler Data"
45
+ Profiler__.class_variable_get(:@@maps).values.each do |threadmap|
46
+ threadmap.each do |key, data|
47
+ total_data = (totals[key.to_s] ||= [-1, 0, 0.0, 0.0, key])
48
+ total_data[1] += data[0]
49
+ total_data[2] += data[1]
50
+ total_data[3] += data[2]
51
+ end
52
+ end
53
+
54
+ puts "Scanning Object Space"
55
+ kls = []
56
+ ObjectSpace.each_object { |obj|
57
+ if kls.include? obj.class then
58
+ next
59
+ end
60
+ kls << obj.class
61
+ mthds = obj.public_methods(false) + obj.private_methods(false) + obj.protected_methods(false)
62
+ puts "Class #{obj.class}"
63
+ mthds.each{ |mthd|
64
+ puts " :#{mthd}"
65
+ totals["#{obj.class}::#{mthd.to_s}".gsub('::','#')] ||= [nil] unless mthd.to_s =~ /new/
66
+ }
67
+ }
68
+
69
+ p "Scanning RDL Contract Log"
70
+ $__rdl_wrapped_calls.each{ |mname,ct|
71
+ if (totals[mname]) then
72
+ if (totals[mname][0]) then
73
+ totals[mname][0] = ct
74
+ else
75
+ totals[mname][0] = -1
76
+ end
77
+ end
78
+ }
79
+
80
+ puts "Analyzing Statistics"
81
+ filtered = {}
82
+ totals.each{ |k,v|
83
+ if (not (k=~/(rdl)|(RDL)/)) and (v[0].nil? or v[0]==-1) then filtered[k]=v end
84
+ }
85
+
86
+ puts "Writing Output"
87
+ require 'json'
88
+ fpath = "#{outdir}/#{outname}_rdlstat.json".gsub('//','')
89
+ File.open(fpath,'w') do |file|
90
+ file.puts "POTENTIAL PROBLEMS"
91
+ filtered.each{ |k,v|
92
+ begin
93
+ k =~ /((?:.+\#)*)(.+)/
94
+ x = $1[0...-1] # Store $1 and $2 before overriden
95
+ y = $2
96
+ if (x =~ /\<Class\:/) then
97
+ puts "Cannot evaluate user-defined class #{x}"
98
+ else
99
+ kls = eval x.gsub('#','::')
100
+ mthds = kls.public_methods(false) + kls.protected_methods(false) # Ignoring private methods
101
+ if (mthds.include? y.to_sym)
102
+ file.printf "%-20s %-s", k, v.to_s
103
+ file.puts ""
104
+ else
105
+ puts "Ignoring inheritance problem for #{k}"
106
+ end
107
+ end
108
+ rescue
109
+ end
110
+ }
111
+ file.puts " "
112
+ file.puts "JSON OBJECT"
113
+ file.puts totals.to_json
114
+ end
115
+ puts "DONE."
116
+ }
117
+ end
118
+
119
+ Profiler__.start_profile # Restart profiler after setup
120
+ end
121
+ end
@@ -0,0 +1,29 @@
1
+ module RDL::Contract
2
+ class AndContract < Contract
3
+ attr_reader :contracts
4
+
5
+ def initialize(*contracts)
6
+ @contracts = contracts
7
+ end
8
+
9
+ # [:slf:] is bound to self when the contracts are checked
10
+ def check(slf, *v, &blk)
11
+ AndContract.check_array(@contracts, slf, *v, &blk)
12
+ end
13
+
14
+ # Check an array of contracts a
15
+ # [:slf:] is bound to self when the contracts are checked
16
+ def self.check_array(a, slf, *v, &blk)
17
+ # All contracts must be satisfied
18
+ a.all? { |c| c.check(slf, *v, &blk) }
19
+ end
20
+
21
+ def to_s
22
+ AndContract.array_to_s(@contracts)
23
+ end
24
+
25
+ def self.array_to_s(a)
26
+ a.join(' && ')
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module RDL::Contract
2
+ class ContractError < StandardError
3
+ end
4
+
5
+ class Contract
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module RDL::Contract
2
+ class FlatContract < Contract
3
+ attr_accessor :desc
4
+
5
+ def initialize(desc="No Description", &blk)
6
+ @pred = blk
7
+ @desc = desc
8
+ end
9
+
10
+ def check(slf, *v, &blk)
11
+ $__rdl_contract_switch.off {
12
+ if (@pred &&
13
+ ((@pred.arity < 0) ? (@pred.arity.abs - 1) <= v.size : @pred.arity == v.size)) then
14
+ unless blk ? slf.instance_exec(*v, blk, &@pred) : slf.instance_exec(*v, &@pred) # TODO: Fix blk
15
+ # unless blk ? pred.call(*v, &blk) : pred.call(*v)
16
+ raise ContractError,
17
+ "#{v.inspect} does not satisfy #{self.to_s}"
18
+ end
19
+ else
20
+ raise ContractError,
21
+ "Invalid number of arguments: Expecting #{@pred.arity}, got #{v.size}"
22
+ end
23
+ }
24
+ return true
25
+ end
26
+
27
+ def to_s
28
+ @desc
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ module RDL::Contract
2
+ class OrContract < Contract
3
+ attr_reader :contracts
4
+
5
+ def initialize(*contracts)
6
+ @contracts = contracts
7
+ end
8
+
9
+ def check(slf, *v, &blk)
10
+ # All contracts must be satisfied
11
+ @contracts.each { |c|
12
+ begin
13
+ c.check(slf, *v, &blk)
14
+ return true
15
+ rescue ContractError
16
+ end
17
+ }
18
+ raise ContractError, "#{v.inspect} does not satisfy #{self}"
19
+ end
20
+
21
+ def to_s
22
+ @contracts.join(' && ')
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ module RDL::Contract
2
+ class ProcContract < Contract
3
+ attr_accessor :pre_cond, :post_cond
4
+
5
+ def initialize(pre_cond:nil, post_cond:nil)
6
+ @pre_cond = pre_cond
7
+ @post_cond = post_cond
8
+ end
9
+
10
+ def wrap(slf, &blk)
11
+ Proc.new {|*v, &other_blk|
12
+ @pre_cond.check(slf, *v, &other_blk)
13
+ tmp = other_blk ? slf.instance_exec(*v, other_blk, &blk) : slf.instance_exec(*v, &blk) # TODO fix blk
14
+ # tmp = blk.call(*v, &other_blk) # TODO: Instance eval with self
15
+ @post_cond.check(slf, tmp, *v, &other_blk)
16
+ tmp
17
+ }
18
+ end
19
+
20
+ def to_s
21
+ "(#{@pre_cond}) -> (#{@post_cond})"
22
+ end
23
+ end
24
+ end
data/lib/rdl/switch.rb ADDED
@@ -0,0 +1,20 @@
1
+ class RDL::Switch
2
+ def initialize
3
+ @switch = true
4
+ end
5
+ def off?()
6
+ return not(@switch)
7
+ end
8
+ def off()
9
+ return unless @switch
10
+ tmp = @switch
11
+ @switch = false
12
+ begin
13
+ ret = yield
14
+ ensure
15
+ @switch = tmp
16
+ end
17
+ return ret
18
+ end
19
+ end
20
+
@@ -0,0 +1,41 @@
1
+ require_relative 'type'
2
+
3
+ module RDL::Type
4
+ class AnnotatedArgType < Type
5
+ attr_reader :name
6
+ attr_reader :type
7
+
8
+ # Note: Named argument types aren't hashconsed.
9
+
10
+ def initialize(name, type)
11
+ @name = name
12
+ @type = type
13
+ raise RuntimeError, "Attempt to create vararg type with non-type" unless type.is_a? Type
14
+ super()
15
+ end
16
+
17
+ def to_s
18
+ "#{@type.to_s} #{@name}"
19
+ end
20
+
21
+ def eql?(other)
22
+ self == other
23
+ end
24
+
25
+ def ==(other) # :nodoc:
26
+ return (other.instance_of? AnnotatedArgType) && (other.name == @name) && (other.type == @type)
27
+ end
28
+
29
+ def hash # :nodoc:
30
+ return (57 + @name.hash) * @type.hash
31
+ end
32
+
33
+ def member?(obj, *args)
34
+ @type.member?(obj, *args)
35
+ end
36
+
37
+ def instantiate(inst)
38
+ return AnnotatedArgType.new(@name, @type.instantiate(inst))
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'type'
2
+
3
+ module RDL::Type
4
+ # A specialized GenericType for fixed-sized maps from values to
5
+ # types, for "named" arguments in Ruby. Values are compared with ==
6
+ # to see if they match.
7
+ class FiniteHashType < Type
8
+ attr_reader :elts
9
+
10
+ @@cache = {}
11
+
12
+ class << self
13
+ alias :__new__ :new
14
+ end
15
+
16
+ def self.new(elts)
17
+ t = @@cache[elts]
18
+ return t if t
19
+ t = FiniteHashType.__new__(elts)
20
+ return (@@cache[elts] = t) # assignment evaluates to t
21
+ end
22
+
23
+ def initialize(elts)
24
+ elts.each { |k, t|
25
+ raise RuntimeError, "Got #{t.inspect} where Type expected" unless t.is_a? Type
26
+ raise RuntimeError, "Type may not be annotated or vararg" if (t.instance_of? AnnotatedArgType) || (t.instance_of? VarargType)
27
+ }
28
+ @elts = elts
29
+ super()
30
+ end
31
+
32
+ def to_s
33
+ "{" + @elts.map { |k, t| k.to_s + ": " + t.to_s }.join(', ') + "}"
34
+ end
35
+
36
+ def eql?(other)
37
+ self == other
38
+ end
39
+
40
+ def ==(other) # :nodoc:
41
+ return (other.instance_of? FiniteHashType) && (other.elts == @elts)
42
+ end
43
+
44
+ def <=(other)
45
+ return true if other.instance_of? TopType
46
+ return self == other
47
+ # Subtyping with Hash not allowed
48
+ # All positions of HashTuple are invariant since tuples are mutable
49
+ end
50
+
51
+ def member?(obj, *args)
52
+ t = RDL::Util.rdl_type obj
53
+ return t <= self if t
54
+ rest = @elts.clone # shallow copy
55
+
56
+ return false unless obj.instance_of? Hash
57
+
58
+ # Check that every mapping in obj exists in @map and matches the type
59
+ obj.each_pair { |k, v|
60
+ return false unless @elts.has_key?(k)
61
+ t = @elts[k]
62
+ t = t.type if t.instance_of? OptionalType
63
+ return false unless t.member?(v)
64
+ rest.delete(k)
65
+ }
66
+
67
+ # Check that any remaining types are optional
68
+ rest.each_pair { |k, t|
69
+ return false unless t.instance_of? OptionalType
70
+ }
71
+ end
72
+
73
+ def instantiate(inst)
74
+ FiniteHashType.new(Hash[@elts.map { |k, t| [k, t.instantiate(inst)] }])
75
+ end
76
+
77
+ def hash
78
+ h = 229 * @elts.hash
79
+ end
80
+ end
81
+ end