typeprof 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +9 -20
- data/LICENSE +21 -0
- data/doc/doc.ja.md +2 -1
- data/doc/doc.md +3 -2
- data/exe/typeprof +2 -1
- data/lib/typeprof.rb +1 -0
- data/lib/typeprof/analyzer.rb +163 -84
- data/lib/typeprof/builtin.rb +20 -19
- data/lib/typeprof/cli.rb +51 -98
- data/lib/typeprof/config.rb +114 -0
- data/lib/typeprof/container-type.rb +125 -21
- data/lib/typeprof/export.rb +20 -8
- data/lib/typeprof/import.rb +43 -20
- data/lib/typeprof/iseq.rb +17 -1
- data/lib/typeprof/method.rb +33 -9
- data/lib/typeprof/type.rb +52 -24
- data/lib/typeprof/utils.rb +4 -18
- data/lib/typeprof/version.rb +3 -0
- data/smoke/arguments2.rb +55 -0
- data/smoke/hash4.rb +1 -1
- data/smoke/keyword3.rb +1 -2
- data/smoke/keyword4.rb +1 -1
- data/smoke/module4.rb +2 -0
- data/smoke/optional1.rb +1 -1
- data/smoke/optional2.rb +1 -1
- data/smoke/rbs-extend.rb +9 -0
- data/smoke/rbs-extend.rbs +7 -0
- data/smoke/rbs-interface.rb +24 -0
- data/smoke/rbs-interface.rbs +12 -0
- data/smoke/rbs-tyvar.rb +18 -0
- data/smoke/rbs-tyvar.rbs +5 -0
- data/smoke/rbs-tyvar2.rb +20 -0
- data/smoke/rbs-tyvar2.rbs +9 -0
- data/smoke/rest1.rb +1 -1
- data/smoke/rest3.rb +1 -1
- data/smoke/rest6.rb +1 -1
- data/smoke/retry1.rb +1 -1
- data/smoke/step.rb +1 -1
- data/smoke/user-demo.rb +15 -0
- data/smoke/wrong-extend.rb +1 -0
- data/typeprof.gemspec +4 -2
- metadata +17 -5
- data/run.sh +0 -3
data/lib/typeprof/builtin.rb
CHANGED
@@ -65,10 +65,14 @@ module TypeProf
|
|
65
65
|
|
66
66
|
def object_s_new(recv, mid, aargs, ep, env, scratch, &ctn)
|
67
67
|
ty = Type::Instance.new(recv)
|
68
|
+
if recv.type_params.size >= 1
|
69
|
+
ty = Type::Cell.new([Type.bot] * recv.type_params.size, ty)
|
70
|
+
env, ty = scratch.localize_type(ty, env, ep, AllocationSite.new(ep).add_id(:object_s_new))
|
71
|
+
end
|
68
72
|
meths = scratch.get_method(recv, false, :initialize)
|
69
73
|
meths.flat_map do |meth|
|
70
74
|
meth.do_send(ty, :initialize, aargs, ep, env, scratch) do |ret_ty, ep, env|
|
71
|
-
ctn[
|
75
|
+
ctn[ty, ep, env]
|
72
76
|
end
|
73
77
|
end
|
74
78
|
end
|
@@ -118,7 +122,7 @@ module TypeProf
|
|
118
122
|
else
|
119
123
|
mid_ty = aargs.rest_ty
|
120
124
|
end
|
121
|
-
aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.
|
125
|
+
aargs = ActualArguments.new(aargs.lead_tys[1..-1], aargs.rest_ty, aargs.kw_tys, aargs.blk_ty)
|
122
126
|
found = false
|
123
127
|
mid_ty.each_child do |mid|
|
124
128
|
if mid.is_a?(Type::Symbol)
|
@@ -138,7 +142,7 @@ module TypeProf
|
|
138
142
|
ctn[type.any, ep, env]
|
139
143
|
return
|
140
144
|
end
|
141
|
-
naargs = ActualArguments.new([recv], Type.nil,
|
145
|
+
naargs = ActualArguments.new([recv], Type.nil, {}, Type.nil)
|
142
146
|
given_block = env.static_env.blk_ty == recv
|
143
147
|
scratch.do_invoke_block(given_block, aargs.blk_ty, naargs, ep, env, replace_recv_ty: recv) do |_ret_ty, ep|
|
144
148
|
ctn[recv, ep, scratch.return_envs[ep]]
|
@@ -244,7 +248,10 @@ module TypeProf
|
|
244
248
|
def array_aset(recv, mid, aargs, ep, env, scratch, &ctn)
|
245
249
|
return ctn[Type.any, ep, env] unless recv.is_a?(Type::LocalArray)
|
246
250
|
|
247
|
-
|
251
|
+
if aargs.lead_tys.size != 2
|
252
|
+
#raise NotImplementedError # XXX
|
253
|
+
ctn[Type.any, ep, env]
|
254
|
+
end
|
248
255
|
|
249
256
|
idx = aargs.lead_tys.first
|
250
257
|
if idx.is_a?(Type::Literal)
|
@@ -316,14 +323,12 @@ module TypeProf
|
|
316
323
|
|
317
324
|
def struct_initialize(recv, mid, aargs, ep, env, scratch, &ctn)
|
318
325
|
recv = Type::Instance.new(recv)
|
319
|
-
scratch.
|
320
|
-
|
321
|
-
ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
|
326
|
+
scratch.add_ivar_read!(recv, :_members, ep) do |member_ary_ty, ep|
|
327
|
+
member_ary_ty.elems.lead_tys.zip(aargs.lead_tys) do |sym, ty|
|
322
328
|
ty ||= Type.nil
|
323
329
|
scratch.set_instance_variable(recv, sym.sym, ty, ep, env)
|
324
330
|
end
|
325
331
|
end
|
326
|
-
#scratch.set_instance_variable(recv, , ty, ep, env)
|
327
332
|
ctn[recv, ep, env]
|
328
333
|
end
|
329
334
|
|
@@ -337,9 +342,8 @@ module TypeProf
|
|
337
342
|
return
|
338
343
|
end
|
339
344
|
if struct_klass != recv
|
340
|
-
scratch.
|
341
|
-
|
342
|
-
scratch.set_instance_variable(Type::Instance.new(recv), :_members, ty, ep, env)
|
345
|
+
scratch.add_ivar_read!(Type::Instance.new(struct_klass), :_members, ep) do |ty, ep|
|
346
|
+
scratch.add_ivar_write!(Type::Instance.new(recv), :_members, ty, ep)
|
343
347
|
end
|
344
348
|
end
|
345
349
|
meths = scratch.get_method(recv, false, :initialize)
|
@@ -364,7 +368,7 @@ module TypeProf
|
|
364
368
|
fields = fields.map {|field| Type::Symbol.new(field, Type::Instance.new(Type::Builtin[:sym])) }
|
365
369
|
base_ty = Type::Instance.new(Type::Builtin[:ary])
|
366
370
|
fields = Type::Array.new(Type::Array::Elements.new(fields), base_ty)
|
367
|
-
scratch.
|
371
|
+
scratch.add_ivar_write!(Type::Instance.new(struct_klass), :_members, fields, ep)
|
368
372
|
#set_singleton_custom_method(struct_klass, :members, Builtin.method(:...))
|
369
373
|
|
370
374
|
ctn[struct_klass, ep, env]
|
@@ -372,10 +376,10 @@ module TypeProf
|
|
372
376
|
|
373
377
|
def self.file_load(path, ep, env, scratch, &ctn)
|
374
378
|
iseq = ISeq.compile(path)
|
375
|
-
callee_ep, callee_env =
|
379
|
+
callee_ep, callee_env = TypeProf.starting_state(iseq)
|
376
380
|
scratch.merge_env(callee_ep, callee_env)
|
377
381
|
|
378
|
-
scratch.add_callsite!(callee_ep.ctx,
|
382
|
+
scratch.add_callsite!(callee_ep.ctx, ep, env) do |_ret_ty, ep|
|
379
383
|
ret_ty = Type::Instance.new(Type::Builtin[:true])
|
380
384
|
ctn[ret_ty, ep, env]
|
381
385
|
end
|
@@ -482,8 +486,8 @@ module TypeProf
|
|
482
486
|
def self.setup_initial_global_env(scratch)
|
483
487
|
klass_basic_obj = scratch.new_class(nil, :BasicObject, [], :__root__, nil) # cbase, name, superclass
|
484
488
|
klass_obj = scratch.new_class(nil, :Object, [], klass_basic_obj, nil)
|
485
|
-
scratch.add_constant(klass_obj,
|
486
|
-
scratch.add_constant(klass_obj,
|
489
|
+
scratch.add_constant(klass_obj, :Object, klass_obj)
|
490
|
+
scratch.add_constant(klass_obj, :BasicObject, klass_basic_obj)
|
487
491
|
|
488
492
|
Type::Builtin[:basic_obj] = klass_basic_obj
|
489
493
|
Type::Builtin[:obj] = klass_obj
|
@@ -550,9 +554,6 @@ module TypeProf
|
|
550
554
|
scratch.set_custom_method(klass_obj, :require, Builtin.method(:kernel_require))
|
551
555
|
scratch.set_custom_method(klass_obj, :require_relative, Builtin.method(:kernel_require_relative))
|
552
556
|
scratch.set_custom_method(klass_obj, :Array, Builtin.method(:kernel_Array))
|
553
|
-
|
554
|
-
fargs, ret_ty = FormalArguments.new([], [], nil, [], nil, nil, Type.any), Type.any
|
555
|
-
scratch.add_method(klass_obj, :initialize, false, TypedMethodDef.new([[fargs, ret_ty]], nil))
|
556
557
|
end
|
557
558
|
end
|
558
559
|
end
|
data/lib/typeprof/cli.rb
CHANGED
@@ -1,64 +1,62 @@
|
|
1
1
|
require "optparse"
|
2
|
-
require "rbconfig"
|
3
2
|
|
4
3
|
module TypeProf
|
5
|
-
|
6
|
-
|
7
|
-
[:include],
|
8
|
-
[:exclude, RbConfig::CONFIG["prefix"]],
|
9
|
-
[:exclude, Gem.dir],
|
10
|
-
[:exclude, Gem.user_dir],
|
11
|
-
]
|
4
|
+
module CLI
|
5
|
+
module_function
|
12
6
|
|
13
|
-
def
|
7
|
+
def parse(argv)
|
14
8
|
opt = OptionParser.new
|
15
9
|
|
16
|
-
|
10
|
+
output = nil
|
17
11
|
|
18
12
|
# Verbose level:
|
19
13
|
# * 0: no output
|
20
14
|
# * 1: show indicator
|
21
15
|
# * 2: debug print
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
opt.on("-
|
33
|
-
opt.on("
|
34
|
-
opt.on("-
|
16
|
+
verbose = 1
|
17
|
+
|
18
|
+
options = {}
|
19
|
+
dir_filter = nil
|
20
|
+
gem_rbs_features = []
|
21
|
+
version = false
|
22
|
+
max_sec = max_iter = nil
|
23
|
+
|
24
|
+
opt.on("-o OUTFILE") {|v| output = v }
|
25
|
+
opt.on("-q", "--quiet") { verbose = 0 }
|
26
|
+
opt.on("-v", "--verbose") { options[:show_errors] = true }
|
27
|
+
opt.on("--version") { version = true }
|
28
|
+
opt.on("-d", "--debug") { verbose = 2 }
|
35
29
|
opt.on("-I DIR") {|v| $LOAD_PATH << v }
|
36
|
-
opt.on("-r FEATURE") {|v|
|
30
|
+
opt.on("-r FEATURE") {|v| gem_rbs_features << v }
|
31
|
+
opt.on("--max-second SECOND", Float) {|v| max_sec = v }
|
32
|
+
opt.on("--max-iteration TIMES", Integer) {|v| max_iter = v }
|
37
33
|
|
38
34
|
opt.on("--include-dir DIR") do |dir|
|
39
35
|
# When `--include-dir` option is specified as the first directory option,
|
40
36
|
# typeprof will exclude any files by default unless a file path matches the explicit option
|
41
|
-
|
42
|
-
|
37
|
+
dir_filter ||= [[:exclude]]
|
38
|
+
dir_filter << [:include, File.expand_path(dir)]
|
43
39
|
end
|
44
40
|
opt.on("--exclude-dir DIR") do |dir|
|
45
41
|
# When `--exclude-dir` option is specified as the first directory option,
|
46
42
|
# typeprof will include any files by default, except Ruby's install directory and Gem directories
|
47
|
-
|
48
|
-
|
43
|
+
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
|
44
|
+
dir_filter << [:exclude, File.expand_path(dir)]
|
49
45
|
end
|
50
46
|
|
51
47
|
opt.on("-f OPTION") do |v|
|
52
48
|
key, args = v.split("=", 2)
|
53
49
|
case key
|
54
50
|
when "type-depth-limit"
|
55
|
-
|
51
|
+
options[:type_depth_limit] = Integer(args)
|
56
52
|
when "pedantic-output"
|
57
|
-
|
53
|
+
options[:pedantic_output] = true
|
58
54
|
when "show-errors"
|
59
|
-
|
55
|
+
options[:show_errors] = true
|
60
56
|
when "show-container-raw-elements"
|
61
|
-
|
57
|
+
options[:show_container_raw_elements] = true
|
58
|
+
when "stackprof"
|
59
|
+
options[:stackprof] = args ? args.to_sym : :cpu
|
62
60
|
else
|
63
61
|
raise OptionParser::InvalidOption.new("unknown option: #{ key }")
|
64
62
|
end
|
@@ -66,83 +64,38 @@ module TypeProf
|
|
66
64
|
|
67
65
|
opt.parse!(argv)
|
68
66
|
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
|
68
|
+
rb_files = []
|
69
|
+
rbs_files = []
|
72
70
|
argv.each do |path|
|
73
71
|
if File.extname(path) == ".rbs"
|
74
|
-
|
72
|
+
rbs_files << path
|
75
73
|
else
|
76
|
-
|
74
|
+
rb_files << path
|
77
75
|
end
|
78
76
|
end
|
79
77
|
|
80
|
-
|
78
|
+
puts "typeprof #{ VERSION }" if version
|
79
|
+
if rb_files.empty?
|
80
|
+
exit if version
|
81
|
+
raise OptionParser::InvalidOption.new("no input files")
|
82
|
+
end
|
81
83
|
|
82
|
-
|
84
|
+
ConfigData.new(
|
85
|
+
rb_files: rb_files,
|
86
|
+
rbs_files: rbs_files,
|
87
|
+
output: output,
|
88
|
+
gem_rbs_features: gem_rbs_features,
|
89
|
+
verbose: verbose,
|
90
|
+
dir_filter: dir_filter,
|
91
|
+
max_sec: max_sec,
|
92
|
+
max_iter: max_iter,
|
93
|
+
options: options,
|
94
|
+
)
|
83
95
|
|
84
96
|
rescue OptionParser::InvalidOption
|
85
97
|
puts $!
|
86
98
|
exit
|
87
99
|
end
|
88
|
-
|
89
|
-
attr_reader :verbose, :options, :dir_filter
|
90
|
-
attr_accessor :output
|
91
|
-
|
92
|
-
def check_dir_filter(path)
|
93
|
-
@dir_filter.reverse_each do |cond, dir|
|
94
|
-
return cond unless dir
|
95
|
-
return cond if path.start_with?(dir)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def run
|
100
|
-
scratch = Scratch.new
|
101
|
-
Builtin.setup_initial_global_env(scratch)
|
102
|
-
|
103
|
-
@rbs_features_to_load.each do |feature|
|
104
|
-
Import.import_library(scratch, feature)
|
105
|
-
end
|
106
|
-
|
107
|
-
prologue_ctx = Context.new(nil, nil, nil)
|
108
|
-
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
109
|
-
prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
|
110
|
-
|
111
|
-
@rb_files.each do |path|
|
112
|
-
if path == "-"
|
113
|
-
iseq = ISeq.compile_str($<.read)
|
114
|
-
else
|
115
|
-
iseq = ISeq.compile(path)
|
116
|
-
end
|
117
|
-
ep, env = CLI.starting_state(iseq)
|
118
|
-
scratch.merge_env(ep, env)
|
119
|
-
scratch.add_callsite!(ep.ctx, nil, prologue_ep, prologue_env) {|ty, ep| }
|
120
|
-
end
|
121
|
-
|
122
|
-
@rbs_files.each do |path|
|
123
|
-
Import.import_rbs_file(scratch, path)
|
124
|
-
end
|
125
|
-
|
126
|
-
result = scratch.type_profile
|
127
|
-
|
128
|
-
if @output
|
129
|
-
open(@output, "w") do |output|
|
130
|
-
scratch.report(result, output)
|
131
|
-
end
|
132
|
-
else
|
133
|
-
scratch.report(result, $stdout)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def self.starting_state(iseq)
|
138
|
-
cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
|
139
|
-
recv = Type::Instance.new(Type::Builtin[:obj])
|
140
|
-
ctx = Context.new(iseq, cref, nil)
|
141
|
-
ep = ExecutionPoint.new(ctx, 0, nil)
|
142
|
-
locals = [Type.nil] * iseq.locals.size
|
143
|
-
env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
|
144
|
-
|
145
|
-
return ep, env
|
146
|
-
end
|
147
100
|
end
|
148
101
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "rbconfig"
|
2
|
+
|
3
|
+
module TypeProf
|
4
|
+
ConfigData = Struct.new(
|
5
|
+
:rb_files,
|
6
|
+
:rbs_files,
|
7
|
+
:output,
|
8
|
+
:gem_rbs_features,
|
9
|
+
:verbose,
|
10
|
+
:dir_filter,
|
11
|
+
:max_iter,
|
12
|
+
:max_sec,
|
13
|
+
:options,
|
14
|
+
keyword_init: true
|
15
|
+
)
|
16
|
+
|
17
|
+
class ConfigData
|
18
|
+
def initialize(**opt)
|
19
|
+
opt[:output] ||= $stdout
|
20
|
+
opt[:gem_rbs_features] ||= []
|
21
|
+
opt[:dir_filter] ||= DEFAULT_DIR_FILTER
|
22
|
+
opt[:verbose] ||= 0
|
23
|
+
opt[:options] ||= {}
|
24
|
+
opt[:options] = {
|
25
|
+
type_depth_limit: 5,
|
26
|
+
pedantic_output: false,
|
27
|
+
show_errors: false,
|
28
|
+
stackprof: nil,
|
29
|
+
}.merge(opt[:options])
|
30
|
+
super(**opt)
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_dir_filter(path)
|
34
|
+
dir_filter.reverse_each do |cond, dir|
|
35
|
+
return cond unless dir
|
36
|
+
return cond if path.start_with?(dir)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
DEFAULT_DIR_FILTER = [
|
41
|
+
[:include],
|
42
|
+
[:exclude, RbConfig::CONFIG["prefix"]],
|
43
|
+
[:exclude, Gem.dir],
|
44
|
+
[:exclude, Gem.user_dir],
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.analyze(config)
|
49
|
+
# Deploy the config to the TypeProf::Config (Note: This is thread unsafe)
|
50
|
+
if TypeProf.const_defined?(:Config)
|
51
|
+
TypeProf.send(:remove_const, :Config)
|
52
|
+
end
|
53
|
+
TypeProf.const_set(:Config, config)
|
54
|
+
|
55
|
+
if Config.options[:stackprof]
|
56
|
+
require "stackprof"
|
57
|
+
out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
|
58
|
+
StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
|
59
|
+
end
|
60
|
+
|
61
|
+
scratch = Scratch.new
|
62
|
+
Builtin.setup_initial_global_env(scratch)
|
63
|
+
|
64
|
+
Config.gem_rbs_features.each do |feature|
|
65
|
+
Import.import_library(scratch, feature)
|
66
|
+
end
|
67
|
+
|
68
|
+
prologue_ctx = Context.new(nil, nil, nil)
|
69
|
+
prologue_ep = ExecutionPoint.new(prologue_ctx, -1, nil)
|
70
|
+
prologue_env = Env.new(StaticEnv.new(:top, Type.nil, false), [], [], Utils::HashWrapper.new({}))
|
71
|
+
|
72
|
+
Config.rb_files.each do |file|
|
73
|
+
if file.respond_to?(:read)
|
74
|
+
iseq = ISeq.compile_str(file.read, file.to_s)
|
75
|
+
else
|
76
|
+
iseq = ISeq.compile(file)
|
77
|
+
end
|
78
|
+
ep, env = TypeProf.starting_state(iseq)
|
79
|
+
scratch.merge_env(ep, env)
|
80
|
+
scratch.add_callsite!(ep.ctx, prologue_ep, prologue_env) {|ty, ep| }
|
81
|
+
end
|
82
|
+
|
83
|
+
Config.rbs_files.each do |path|
|
84
|
+
Import.import_rbs_file(scratch, path)
|
85
|
+
end
|
86
|
+
|
87
|
+
result = scratch.type_profile
|
88
|
+
|
89
|
+
if Config.output.respond_to?(:write)
|
90
|
+
scratch.report(result, Config.output)
|
91
|
+
else
|
92
|
+
open(Config.output, "w") do |output|
|
93
|
+
scratch.report(result, output)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
ensure
|
98
|
+
if Config.options[:stackprof] && defined?(StackProf)
|
99
|
+
StackProf.stop
|
100
|
+
StackProf.results
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.starting_state(iseq)
|
105
|
+
cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
|
106
|
+
recv = Type::Instance.new(Type::Builtin[:obj])
|
107
|
+
ctx = Context.new(iseq, cref, nil)
|
108
|
+
ep = ExecutionPoint.new(ctx, 0, nil)
|
109
|
+
locals = [Type.nil] * iseq.locals.size
|
110
|
+
env = Env.new(StaticEnv.new(recv, Type.nil, false), locals, [], Utils::HashWrapper.new({}))
|
111
|
+
|
112
|
+
return ep, env
|
113
|
+
end
|
114
|
+
end
|
@@ -6,22 +6,123 @@ module TypeProf
|
|
6
6
|
raise if !val.is_a?(Utils::StructuralEquality) && !val.is_a?(Integer) && !val.is_a?(Symbol)
|
7
7
|
@val = val
|
8
8
|
@parent = parent
|
9
|
-
@_hash ||= (@val.hash ^ @parent.hash)
|
10
9
|
end
|
11
10
|
|
12
11
|
attr_reader :val, :parent
|
13
12
|
|
14
|
-
def hash
|
15
|
-
@_hash
|
16
|
-
end
|
17
|
-
|
18
13
|
def add_id(val)
|
19
14
|
AllocationSite.new(val, self)
|
20
15
|
end
|
21
16
|
end
|
22
17
|
|
23
18
|
class Type # or AbstractValue
|
24
|
-
#
|
19
|
+
# Cell, Array, and Hash are types for global interface, e.g., TypedISeq.
|
20
|
+
# Do not push such types to local environment, stack, etc.
|
21
|
+
|
22
|
+
# The most basic container type for default type parameter class
|
23
|
+
class Cell < Type
|
24
|
+
def initialize(elems, base_type)
|
25
|
+
@elems = elems # Array[Symbol]
|
26
|
+
raise unless base_type
|
27
|
+
@base_type = base_type
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :elems, :base_type
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"Type::Cell[#{ @elems.inspect }, base_type: #{ @base_type.inspect }]"
|
34
|
+
end
|
35
|
+
|
36
|
+
def screen_name(scratch)
|
37
|
+
"#{ @base_type.screen_name(scratch) }[#{ @elems.map {|elem| elem.screen_name(scratch) }.join(", ") }]"
|
38
|
+
end
|
39
|
+
|
40
|
+
def localize(env, alloc_site, depth)
|
41
|
+
return env, Type.any if depth <= 0
|
42
|
+
alloc_site = alloc_site.add_id(:cell)
|
43
|
+
elems = @elems.map.with_index do |elem, idx|
|
44
|
+
env, elem = elem.localize(env, alloc_site.add_id(idx), depth - 1)
|
45
|
+
elem
|
46
|
+
end
|
47
|
+
env.deploy_cell_type(alloc_site, elems, @base_type)
|
48
|
+
end
|
49
|
+
|
50
|
+
def limit_size(limit)
|
51
|
+
return Type.any if limit <= 0
|
52
|
+
Cell.new(@elems.map {|elem| elem.limit_size(limit - 1) }, @base_type)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_method(mid, scratch)
|
56
|
+
raise
|
57
|
+
end
|
58
|
+
|
59
|
+
def consistent?(other, subst)
|
60
|
+
case other
|
61
|
+
when Type::Any then true
|
62
|
+
when Type::Var then other.add_subst!(self, subst)
|
63
|
+
when Type::Union
|
64
|
+
other.types.each do |ty2|
|
65
|
+
return true if consistent?(ty2, subst)
|
66
|
+
end
|
67
|
+
return false
|
68
|
+
when Type::Cell
|
69
|
+
@elems.size == other.elems.size &&
|
70
|
+
@base_type.consistent?(other.base_type, subst) &&
|
71
|
+
@elems.zip(other.elems).all? {|elem1, elem2| elem1..consistent?(elem2, subst) }
|
72
|
+
else
|
73
|
+
self == other
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def substitute(subst, depth)
|
78
|
+
return Type.any if depth <= 0
|
79
|
+
elems = @elems.map {|elem| elem.substitute(subst, depth - 1) }
|
80
|
+
Cell.new(elems, @base_type)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class LocalCell < Type
|
85
|
+
def initialize(id, base_type)
|
86
|
+
@id = id
|
87
|
+
raise unless base_type
|
88
|
+
@base_type = base_type
|
89
|
+
end
|
90
|
+
|
91
|
+
attr_reader :id, :base_type
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"Type::LocalCell[#{ @id }, base_type: #{ @base_type.inspect }]"
|
95
|
+
end
|
96
|
+
|
97
|
+
def screen_name(scratch)
|
98
|
+
#raise "LocalArray must not be included in signature"
|
99
|
+
"LocalCell!"
|
100
|
+
end
|
101
|
+
|
102
|
+
def globalize(env, visited, depth)
|
103
|
+
if visited[self] || depth <= 0
|
104
|
+
Type.any
|
105
|
+
else
|
106
|
+
visited[self] = true
|
107
|
+
elems = env.get_container_elem_types(@id)
|
108
|
+
if elems
|
109
|
+
elems = elems.map {|elem| elem.globalize(env, visited, depth - 1) }
|
110
|
+
else
|
111
|
+
elems = [] # XXX
|
112
|
+
end
|
113
|
+
Cell.new(elems, @base_type)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def get_method(mid, scratch)
|
118
|
+
@base_type.get_method(mid, scratch)
|
119
|
+
end
|
120
|
+
|
121
|
+
def consistent?(other, subst)
|
122
|
+
raise "must not be used"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
25
126
|
# Do not insert Array type to local environment, stack, etc.
|
26
127
|
class Array < Type
|
27
128
|
def initialize(elems, base_type)
|
@@ -29,7 +130,6 @@ module TypeProf
|
|
29
130
|
@elems = elems # Array::Elements
|
30
131
|
raise unless base_type
|
31
132
|
@base_type = base_type
|
32
|
-
# XXX: need infinite recursion
|
33
133
|
end
|
34
134
|
|
35
135
|
attr_reader :elems, :base_type
|
@@ -47,13 +147,6 @@ module TypeProf
|
|
47
147
|
str
|
48
148
|
end
|
49
149
|
|
50
|
-
def globalize(env, visited, depth)
|
51
|
-
return Type.any if depth <= 0
|
52
|
-
elems = @elems.globalize(env, visited, depth - 1)
|
53
|
-
base_ty = @base_type.globalize(env, visited, depth - 1)
|
54
|
-
Array.new(elems, base_ty)
|
55
|
-
end
|
56
|
-
|
57
150
|
def localize(env, alloc_site, depth)
|
58
151
|
return env, Type.any if depth <= 0
|
59
152
|
alloc_site = alloc_site.add_id(:ary)
|
@@ -78,6 +171,7 @@ module TypeProf
|
|
78
171
|
other.types.each do |ty2|
|
79
172
|
return true if consistent?(ty2, subst)
|
80
173
|
end
|
174
|
+
return false
|
81
175
|
when Type::Array
|
82
176
|
@base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
|
83
177
|
else
|
@@ -365,13 +459,6 @@ module TypeProf
|
|
365
459
|
@elems.screen_name(scratch)
|
366
460
|
end
|
367
461
|
|
368
|
-
def globalize(env, visited, depth)
|
369
|
-
return Type.any if depth <= 0
|
370
|
-
elems = @elems.globalize(env, visited, depth - 1)
|
371
|
-
base_ty = @base_type.globalize(env, visited, depth - 1)
|
372
|
-
Hash.new(elems, base_ty)
|
373
|
-
end
|
374
|
-
|
375
462
|
def localize(env, alloc_site, depth)
|
376
463
|
return env, Type.any if depth <= 0
|
377
464
|
alloc_site = alloc_site.add_id(:hash)
|
@@ -396,6 +483,7 @@ module TypeProf
|
|
396
483
|
other.types.each do |ty2|
|
397
484
|
return true if consistent?(ty2, subst)
|
398
485
|
end
|
486
|
+
return false
|
399
487
|
when Type::Hash
|
400
488
|
@base_type.consistent?(other.base_type, subst) && @elems.consistent?(other.elems, subst)
|
401
489
|
else
|
@@ -579,6 +667,22 @@ module TypeProf
|
|
579
667
|
|
580
668
|
Elements.new(map_tys)
|
581
669
|
end
|
670
|
+
|
671
|
+
def to_keywords
|
672
|
+
kw_tys = {}
|
673
|
+
@map_tys.each do |key_ty, val_ty|
|
674
|
+
if key_ty.is_a?(Type::Symbol)
|
675
|
+
kw_tys[key_ty.sym] = val_ty
|
676
|
+
else
|
677
|
+
all_val_ty = Type.bot
|
678
|
+
@map_tys.each do |_key_ty, val_ty|
|
679
|
+
all_val_ty = all_val_ty.union(val_ty)
|
680
|
+
end
|
681
|
+
return { nil => all_val_ty }
|
682
|
+
end
|
683
|
+
end
|
684
|
+
kw_tys
|
685
|
+
end
|
582
686
|
end
|
583
687
|
end
|
584
688
|
|