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.
- checksums.yaml +7 -0
- data/lib/rails_types.rb +1 -0
- data/lib/rdl.rb +56 -0
- data/lib/rdl/config.rb +121 -0
- data/lib/rdl/contracts/and.rb +29 -0
- data/lib/rdl/contracts/contract.rb +7 -0
- data/lib/rdl/contracts/flat.rb +31 -0
- data/lib/rdl/contracts/or.rb +25 -0
- data/lib/rdl/contracts/proc.rb +24 -0
- data/lib/rdl/switch.rb +20 -0
- data/lib/rdl/types/annotated_arg.rb +41 -0
- data/lib/rdl/types/finitehash.rb +81 -0
- data/lib/rdl/types/generic.rb +100 -0
- data/lib/rdl/types/intersection.rb +66 -0
- data/lib/rdl/types/lexer.rex +39 -0
- data/lib/rdl/types/lexer.rex.rb +148 -0
- data/lib/rdl/types/method.rb +219 -0
- data/lib/rdl/types/nil.rb +50 -0
- data/lib/rdl/types/nominal.rb +80 -0
- data/lib/rdl/types/optional.rb +54 -0
- data/lib/rdl/types/parser.racc +150 -0
- data/lib/rdl/types/parser.tab.rb +654 -0
- data/lib/rdl/types/singleton.rb +62 -0
- data/lib/rdl/types/structural.rb +72 -0
- data/lib/rdl/types/top.rb +50 -0
- data/lib/rdl/types/tuple.rb +61 -0
- data/lib/rdl/types/type.rb +24 -0
- data/lib/rdl/types/type_inferencer.rb +73 -0
- data/lib/rdl/types/union.rb +74 -0
- data/lib/rdl/types/var.rb +51 -0
- data/lib/rdl/types/vararg.rb +54 -0
- data/lib/rdl/types/wild.rb +26 -0
- data/lib/rdl/util.rb +56 -0
- data/lib/rdl/wrap.rb +505 -0
- data/lib/rdl_types.rb +2 -0
- data/types/other/chronic.rb +5 -0
- data/types/other/paperclip_attachment.rb +7 -0
- data/types/other/securerandom.rb +4 -0
- data/types/rails-4.2.1/fixnum.rb +3 -0
- data/types/rails-4.2.1/string.rb +3 -0
- data/types/rails-tmp/action_dispatch.rb +406 -0
- data/types/rails-tmp/active_record.rb +406 -0
- data/types/rails-tmp/devise_contracts.rb +216 -0
- data/types/ruby-2.2.0/_aliases.rb +4 -0
- data/types/ruby-2.2.0/abbrev.rb +5 -0
- data/types/ruby-2.2.0/array.rb +137 -0
- data/types/ruby-2.2.0/base64.rb +10 -0
- data/types/ruby-2.2.0/basic_object.rb +13 -0
- data/types/ruby-2.2.0/benchmark.rb +11 -0
- data/types/ruby-2.2.0/bigdecimal.rb +15 -0
- data/types/ruby-2.2.0/bigmath.rb +12 -0
- data/types/ruby-2.2.0/class.rb +17 -0
- data/types/ruby-2.2.0/complex.rb +42 -0
- data/types/ruby-2.2.0/coverage.rb +6 -0
- data/types/ruby-2.2.0/csv.rb +5 -0
- data/types/ruby-2.2.0/date.rb +6 -0
- data/types/ruby-2.2.0/dir.rb +38 -0
- data/types/ruby-2.2.0/encoding.rb +23 -0
- data/types/ruby-2.2.0/enumerable.rb +98 -0
- data/types/ruby-2.2.0/enumerator.rb +26 -0
- data/types/ruby-2.2.0/exception.rb +15 -0
- data/types/ruby-2.2.0/file.rb +124 -0
- data/types/ruby-2.2.0/fileutils.rb +6 -0
- data/types/ruby-2.2.0/fixnum.rb +45 -0
- data/types/ruby-2.2.0/float.rb +54 -0
- data/types/ruby-2.2.0/gem.rb +245 -0
- data/types/ruby-2.2.0/hash.rb +72 -0
- data/types/ruby-2.2.0/integer.rb +31 -0
- data/types/ruby-2.2.0/io.rb +103 -0
- data/types/ruby-2.2.0/kernel.rb +89 -0
- data/types/ruby-2.2.0/marshal.rb +5 -0
- data/types/ruby-2.2.0/matchdata.rb +26 -0
- data/types/ruby-2.2.0/math.rb +53 -0
- data/types/ruby-2.2.0/numeric.rb +46 -0
- data/types/ruby-2.2.0/object.rb +73 -0
- data/types/ruby-2.2.0/pathname.rb +106 -0
- data/types/ruby-2.2.0/process.rb +127 -0
- data/types/ruby-2.2.0/random.rb +15 -0
- data/types/ruby-2.2.0/range.rb +38 -0
- data/types/ruby-2.2.0/rational.rb +31 -0
- data/types/ruby-2.2.0/regexp.rb +30 -0
- data/types/ruby-2.2.0/set.rb +58 -0
- data/types/ruby-2.2.0/string.rb +145 -0
- data/types/ruby-2.2.0/strscan.rb +7 -0
- data/types/ruby-2.2.0/symbol.rb +29 -0
- data/types/ruby-2.2.0/time.rb +68 -0
- data/types/ruby-2.2.0/uri.rb +18 -0
- data/types/ruby-2.2.0/yaml.rb +5 -0
- 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
|
data/lib/rails_types.rb
ADDED
|
@@ -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,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
|