typeprof 0.2.0 → 0.3.0
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 +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
|
|