rdl 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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