typeprof 0.15.3 → 0.20.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/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +4 -4
- data/exe/typeprof +5 -1
- data/lib/typeprof/analyzer.rb +228 -54
- data/lib/typeprof/arguments.rb +1 -0
- data/lib/typeprof/builtin.rb +23 -23
- data/lib/typeprof/cli.rb +22 -2
- data/lib/typeprof/code-range.rb +177 -0
- data/lib/typeprof/config.rb +43 -18
- data/lib/typeprof/container-type.rb +3 -0
- data/lib/typeprof/export.rb +191 -15
- data/lib/typeprof/import.rb +25 -4
- data/lib/typeprof/iseq.rb +218 -16
- data/lib/typeprof/lsp.rb +865 -0
- data/lib/typeprof/method.rb +15 -11
- data/lib/typeprof/type.rb +46 -38
- data/lib/typeprof/utils.rb +18 -1
- data/lib/typeprof/version.rb +1 -1
- data/lib/typeprof.rb +3 -0
- data/typeprof-lsp +3 -0
- data/typeprof.gemspec +1 -1
- data/vscode/.gitignore +5 -0
- data/vscode/.vscode/launch.json +16 -0
- data/vscode/.vscodeignore +7 -0
- data/vscode/README.md +22 -0
- data/vscode/development.md +31 -0
- data/vscode/package-lock.json +2211 -0
- data/vscode/package.json +71 -0
- data/vscode/sandbox/test.rb +24 -0
- data/vscode/src/extension.ts +285 -0
- data/vscode/tsconfig.json +15 -0
- metadata +18 -5
data/lib/typeprof/builtin.rb
CHANGED
@@ -624,7 +624,7 @@ module TypeProf
|
|
624
624
|
end
|
625
625
|
|
626
626
|
def self.file_load(path, ep, env, scratch, &ctn)
|
627
|
-
iseq = ISeq.compile(path)
|
627
|
+
iseq, = ISeq.compile(path)
|
628
628
|
callee_ep, callee_env = TypeProf.starting_state(iseq)
|
629
629
|
scratch.merge_env(callee_ep, callee_env)
|
630
630
|
|
@@ -793,27 +793,27 @@ module TypeProf
|
|
793
793
|
|
794
794
|
Import.import_builtin(scratch)
|
795
795
|
|
796
|
-
Type::Builtin[:vmcore] = scratch.new_class(klass_obj, :VMCore, [], klass_obj, nil)
|
797
|
-
Type::Builtin[:int] = scratch.get_constant(klass_obj, :Integer)
|
798
|
-
Type::Builtin[:float] = scratch.get_constant(klass_obj, :Float)
|
799
|
-
Type::Builtin[:rational] = scratch.get_constant(klass_obj, :Rational)
|
800
|
-
Type::Builtin[:complex] = scratch.get_constant(klass_obj, :Complex)
|
801
|
-
Type::Builtin[:sym] = scratch.get_constant(klass_obj, :Symbol)
|
802
|
-
Type::Builtin[:str] = scratch.get_constant(klass_obj, :String)
|
803
|
-
Type::Builtin[:struct] = scratch.get_constant(klass_obj, :Struct)
|
804
|
-
Type::Builtin[:ary] = scratch.get_constant(klass_obj, :Array)
|
805
|
-
Type::Builtin[:hash] = scratch.get_constant(klass_obj, :Hash)
|
806
|
-
Type::Builtin[:io] = scratch.get_constant(klass_obj, :IO)
|
807
|
-
Type::Builtin[:proc] = scratch.get_constant(klass_obj, :Proc)
|
808
|
-
Type::Builtin[:range] = scratch.get_constant(klass_obj, :Range)
|
809
|
-
Type::Builtin[:regexp] = scratch.get_constant(klass_obj, :Regexp)
|
810
|
-
Type::Builtin[:matchdata] = scratch.get_constant(klass_obj, :MatchData)
|
811
|
-
Type::Builtin[:class] = scratch.get_constant(klass_obj, :Class)
|
812
|
-
Type::Builtin[:module] = scratch.get_constant(klass_obj, :Module)
|
813
|
-
Type::Builtin[:exc] = scratch.get_constant(klass_obj, :Exception)
|
814
|
-
Type::Builtin[:encoding] = scratch.get_constant(klass_obj, :Encoding)
|
815
|
-
Type::Builtin[:enumerator] = scratch.get_constant(klass_obj, :Enumerator)
|
816
|
-
Type::Builtin[:kernel] = scratch.get_constant(klass_obj, :Kernel)
|
796
|
+
Type::Builtin[:vmcore] , = scratch.new_class(klass_obj, :VMCore, [], klass_obj, nil)
|
797
|
+
Type::Builtin[:int] , = scratch.get_constant(klass_obj, :Integer)
|
798
|
+
Type::Builtin[:float] , = scratch.get_constant(klass_obj, :Float)
|
799
|
+
Type::Builtin[:rational] , = scratch.get_constant(klass_obj, :Rational)
|
800
|
+
Type::Builtin[:complex] , = scratch.get_constant(klass_obj, :Complex)
|
801
|
+
Type::Builtin[:sym] , = scratch.get_constant(klass_obj, :Symbol)
|
802
|
+
Type::Builtin[:str] , = scratch.get_constant(klass_obj, :String)
|
803
|
+
Type::Builtin[:struct] , = scratch.get_constant(klass_obj, :Struct)
|
804
|
+
Type::Builtin[:ary] , = scratch.get_constant(klass_obj, :Array)
|
805
|
+
Type::Builtin[:hash] , = scratch.get_constant(klass_obj, :Hash)
|
806
|
+
Type::Builtin[:io] , = scratch.get_constant(klass_obj, :IO)
|
807
|
+
Type::Builtin[:proc] , = scratch.get_constant(klass_obj, :Proc)
|
808
|
+
Type::Builtin[:range] , = scratch.get_constant(klass_obj, :Range)
|
809
|
+
Type::Builtin[:regexp] , = scratch.get_constant(klass_obj, :Regexp)
|
810
|
+
Type::Builtin[:matchdata] , = scratch.get_constant(klass_obj, :MatchData)
|
811
|
+
Type::Builtin[:class] , = scratch.get_constant(klass_obj, :Class)
|
812
|
+
Type::Builtin[:module] , = scratch.get_constant(klass_obj, :Module)
|
813
|
+
Type::Builtin[:exc] , = scratch.get_constant(klass_obj, :Exception)
|
814
|
+
Type::Builtin[:encoding] , = scratch.get_constant(klass_obj, :Encoding)
|
815
|
+
Type::Builtin[:enumerator] , = scratch.get_constant(klass_obj, :Enumerator)
|
816
|
+
Type::Builtin[:kernel] , = scratch.get_constant(klass_obj, :Kernel)
|
817
817
|
|
818
818
|
klass_vmcore = Type::Builtin[:vmcore]
|
819
819
|
klass_ary = Type::Builtin[:ary]
|
@@ -881,7 +881,7 @@ module TypeProf
|
|
881
881
|
# ENV: Hash[String, String]
|
882
882
|
str_ty = Type::Instance.new(Type::Builtin[:str])
|
883
883
|
env_ty = Type.gen_hash {|h| h[str_ty] = Type.optional(str_ty) }
|
884
|
-
scratch.add_constant(klass_obj, :ENV, env_ty,
|
884
|
+
scratch.add_constant(klass_obj, :ENV, env_ty, nil)
|
885
885
|
|
886
886
|
scratch.search_method(Type::Builtin[:kernel], false, :sprintf) do |mdefs,|
|
887
887
|
mdefs.each do |mdef|
|
data/lib/typeprof/cli.rb
CHANGED
@@ -18,11 +18,15 @@ module TypeProf
|
|
18
18
|
verbose = 1
|
19
19
|
|
20
20
|
options = {}
|
21
|
+
lsp_options = {}
|
21
22
|
dir_filter = nil
|
22
23
|
gem_rbs_features = []
|
23
24
|
gem_repo_dirs = []
|
24
25
|
show_version = false
|
25
26
|
max_sec = max_iter = nil
|
27
|
+
collection_path = RBS::Collection::Config::PATH
|
28
|
+
|
29
|
+
load_path_ext = []
|
26
30
|
|
27
31
|
opt.separator ""
|
28
32
|
opt.separator "Options:"
|
@@ -30,9 +34,12 @@ module TypeProf
|
|
30
34
|
opt.on("-q", "--quiet", "Do not display progress indicator") { options[:show_indicator] = false }
|
31
35
|
opt.on("-v", "--verbose", "Alias to --show-errors") { options[:show_errors] = true }
|
32
36
|
opt.on("--version", "Display typeprof version") { show_version = true }
|
33
|
-
opt.on("-I DIR", "Add DIR to the load/require path") {|v|
|
37
|
+
opt.on("-I DIR", "Add DIR to the load/require path") {|v| load_path_ext << v }
|
34
38
|
opt.on("-r FEATURE", "Require RBS of the FEATURE gem") {|v| gem_rbs_features << v }
|
35
39
|
opt.on("--repo DIR", "Add DIR to the RBS repository") {|v| gem_repo_dirs << v }
|
40
|
+
opt.on("--collection PATH", "File path of collection configuration") { |v| collection_path = v }
|
41
|
+
opt.on("--no-collection", "Ignore collection configuration") { collection_path = nil }
|
42
|
+
opt.on("--lsp", "LSP mode") {|v| options[:lsp] = true }
|
36
43
|
|
37
44
|
opt.separator ""
|
38
45
|
opt.separator "Analysis output options:"
|
@@ -68,8 +75,14 @@ module TypeProf
|
|
68
75
|
opt.on("--debug", "Display analysis log (for debugging purpose)") { verbose = 2 }
|
69
76
|
opt.on("--[no-]stackprof MODE", /\Acpu|wall|object\z/, "Enable stackprof (for debugging purpose)") {|v| options[:stackprof] = v.to_sym }
|
70
77
|
|
78
|
+
opt.separator ""
|
79
|
+
opt.separator "LSP options:"
|
80
|
+
opt.on("--port PORT", Integer, "Specify a port number to listen for requests on") {|v| lsp_options[:port] = v }
|
81
|
+
|
71
82
|
opt.parse!(argv)
|
72
83
|
|
84
|
+
$LOAD_PATH.unshift(*load_path_ext)
|
85
|
+
|
73
86
|
dir_filter ||= ConfigData::DEFAULT_DIR_FILTER
|
74
87
|
rb_files = []
|
75
88
|
rbs_files = []
|
@@ -82,22 +95,29 @@ module TypeProf
|
|
82
95
|
end
|
83
96
|
|
84
97
|
puts "typeprof #{ VERSION }" if show_version
|
85
|
-
if rb_files.empty?
|
98
|
+
if rb_files.empty? && !options[:lsp]
|
86
99
|
exit if show_version
|
87
100
|
raise OptionParser::InvalidOption.new("no input files")
|
88
101
|
end
|
89
102
|
|
103
|
+
if !options[:lsp] && !lsp_options.empty?
|
104
|
+
exit if show_version
|
105
|
+
raise OptionParser::InvalidOption.new("lsp options with non-lsp mode")
|
106
|
+
end
|
107
|
+
|
90
108
|
ConfigData.new(
|
91
109
|
rb_files: rb_files,
|
92
110
|
rbs_files: rbs_files,
|
93
111
|
output: output,
|
94
112
|
gem_rbs_features: gem_rbs_features,
|
95
113
|
gem_repo_dirs: gem_repo_dirs,
|
114
|
+
collection_path: collection_path,
|
96
115
|
verbose: verbose,
|
97
116
|
dir_filter: dir_filter,
|
98
117
|
max_sec: max_sec,
|
99
118
|
max_iter: max_iter,
|
100
119
|
options: options,
|
120
|
+
lsp_options: lsp_options,
|
101
121
|
)
|
102
122
|
|
103
123
|
rescue OptionParser::InvalidOption
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module TypeProf
|
2
|
+
class CodeLocation
|
3
|
+
# In Ruby, lineno is 1-origin, and column is 0-origin
|
4
|
+
def initialize(lineno, column)
|
5
|
+
@lineno = lineno
|
6
|
+
@column = column
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"(%d,%d)" % [@lineno, @column]
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :lineno, :column
|
14
|
+
|
15
|
+
def self.from_lsp(lsp_loc)
|
16
|
+
# In the Language Server Protocol, lineno and column are both 0-origin
|
17
|
+
CodeLocation.new(lsp_loc[:line] + 1, lsp_loc[:character])
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_lsp
|
21
|
+
{ line: @lineno - 1, character: @column }
|
22
|
+
end
|
23
|
+
|
24
|
+
def advance_cursor(offset, source_text)
|
25
|
+
new_lineno = @lineno
|
26
|
+
new_column = @column
|
27
|
+
while offset > 0
|
28
|
+
line_text = source_text.lines[new_lineno - 1]
|
29
|
+
if new_column + offset >= line_text.length
|
30
|
+
advanced = line_text.length - new_column
|
31
|
+
offset -= advanced
|
32
|
+
new_lineno += 1
|
33
|
+
new_column = 0
|
34
|
+
else
|
35
|
+
new_column += offset
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
CodeLocation.new(new_lineno, new_column)
|
40
|
+
end
|
41
|
+
|
42
|
+
def <=>(other)
|
43
|
+
ret = @lineno <=> other.lineno
|
44
|
+
return ret if ret != 0
|
45
|
+
@column <=> other.column
|
46
|
+
end
|
47
|
+
|
48
|
+
include Comparable
|
49
|
+
end
|
50
|
+
|
51
|
+
class CodeRange
|
52
|
+
def initialize(first, last)
|
53
|
+
@first, @last = first, last
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
"%p-%p" % [@first, @last]
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :first
|
61
|
+
attr_reader :last
|
62
|
+
|
63
|
+
def self.from_lsp(lsp_range)
|
64
|
+
CodeRange.new(CodeLocation.from_lsp(lsp[:start]), CodeLocation.from_lsp(lsp[:end]))
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.from_rbs(rbs_loc)
|
68
|
+
CodeRange.new(
|
69
|
+
CodeLocation.new(rbs_loc.start_line, rbs_loc.start_column),
|
70
|
+
CodeLocation.new(rbs_loc.end_line, rbs_loc.end_column),
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_lsp
|
75
|
+
{ start: @first.to_lsp, end: @last.to_lsp }
|
76
|
+
end
|
77
|
+
|
78
|
+
def contain_loc?(loc)
|
79
|
+
@first <= loc && loc < @last
|
80
|
+
end
|
81
|
+
|
82
|
+
def contain?(other)
|
83
|
+
@first <= other.first && other.last <= @last
|
84
|
+
end
|
85
|
+
|
86
|
+
def overlap?(other)
|
87
|
+
if @first <= other.first
|
88
|
+
return @last > other.first
|
89
|
+
else
|
90
|
+
return @first < other.last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class CodeRangeTable
|
96
|
+
Entry = Struct.new(:range, :value, :children)
|
97
|
+
|
98
|
+
class Entry
|
99
|
+
def inspect
|
100
|
+
"[%p, %p, %p]" % [range, value, children]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize(list = [])
|
105
|
+
@list = list # Array[Entry]
|
106
|
+
end
|
107
|
+
|
108
|
+
def []=(range, value)
|
109
|
+
i_b = @list.bsearch_index {|e| e.range.last > range.first } || @list.size
|
110
|
+
i_e = @list.bsearch_index {|e| e.range.first >= range.last } || @list.size
|
111
|
+
if i_b < i_e
|
112
|
+
# for all i in i_b...i_e, @list[i] overlaps with the range
|
113
|
+
if i_e - i_b == 1
|
114
|
+
if range.contain?(@list[i_b].range)
|
115
|
+
@list[i_b] = Entry[range, value, CodeRangeTable.new(@list[i_b, 1])]
|
116
|
+
elsif @list[i_b].range.contain?(range)
|
117
|
+
@list[i_b].children[range] = value
|
118
|
+
else
|
119
|
+
raise
|
120
|
+
end
|
121
|
+
else
|
122
|
+
if range.contain?(@list[i_b].range) && range.contain?(@list[i_e - 1].range)
|
123
|
+
@list[i_b...i_e] = [Entry[range, value, CodeRangeTable.new(@list[i_b...i_e])]]
|
124
|
+
else
|
125
|
+
raise
|
126
|
+
end
|
127
|
+
end
|
128
|
+
else
|
129
|
+
@list[i_b, 0] = [Entry[range, value, CodeRangeTable.new]]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def [](loc)
|
134
|
+
e = @list.bsearch {|e| e.range.last > loc }
|
135
|
+
if e && e.range.contain_loc?(loc)
|
136
|
+
return e.children[loc] || e.value
|
137
|
+
end
|
138
|
+
return nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
if $0 == __FILE__
|
144
|
+
include TypeProf
|
145
|
+
cr1 = CodeRange.new(CodeLocation.new(1, 0), CodeLocation.new(1, 2))
|
146
|
+
cr2 = CodeRange.new(CodeLocation.new(1, 2), CodeLocation.new(1, 4))
|
147
|
+
cr3 = CodeRange.new(CodeLocation.new(2, 0), CodeLocation.new(2, 2))
|
148
|
+
cr4 = CodeRange.new(CodeLocation.new(2, 3), CodeLocation.new(2, 5))
|
149
|
+
cr1and2 = CodeRange.new(CodeLocation.new(1, 0), CodeLocation.new(1, 5))
|
150
|
+
cr3and4 = CodeRange.new(CodeLocation.new(2, 0), CodeLocation.new(2, 5))
|
151
|
+
[[cr1, "A"], [cr2, "B"], [cr3, "C"], [cr4, "D"], [cr1and2, "AB"], [cr3and4, "CD"]].permutation do |ary|
|
152
|
+
tbl = CodeRangeTable.new
|
153
|
+
ary.each do |cr, v|
|
154
|
+
tbl[cr] = v
|
155
|
+
end
|
156
|
+
values = []
|
157
|
+
[1, 2].each do |lineno|
|
158
|
+
(0..5).each do |column|
|
159
|
+
values << tbl[CodeLocation.new(lineno, column)]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
raise if values != ["A", "A", "B", "B", "AB", nil, "C", "C", "CD", "D", "D", nil]
|
163
|
+
end
|
164
|
+
|
165
|
+
source = <<~EOS
|
166
|
+
AB
|
167
|
+
CDE
|
168
|
+
F
|
169
|
+
EOS
|
170
|
+
a_loc = CodeLocation.new(1, 0)
|
171
|
+
b_loc = a_loc.advance_cursor(1, source)
|
172
|
+
raise unless b_loc.inspect == "(1,1)"
|
173
|
+
c_loc = a_loc.advance_cursor(3, source)
|
174
|
+
raise unless c_loc.inspect == "(2,0)"
|
175
|
+
f_loc = c_loc.advance_cursor(4, source)
|
176
|
+
raise unless f_loc.inspect == "(3,0)"
|
177
|
+
end
|
data/lib/typeprof/config.rb
CHANGED
@@ -7,11 +7,14 @@ module TypeProf
|
|
7
7
|
:output,
|
8
8
|
:gem_rbs_features,
|
9
9
|
:gem_repo_dirs,
|
10
|
+
:collection_path,
|
10
11
|
:verbose,
|
11
12
|
:dir_filter,
|
12
13
|
:max_iter,
|
13
14
|
:max_sec,
|
14
15
|
:options,
|
16
|
+
:lsp_options,
|
17
|
+
:lsp,
|
15
18
|
keyword_init: true
|
16
19
|
)
|
17
20
|
|
@@ -43,6 +46,9 @@ module TypeProf
|
|
43
46
|
union_width_limit: 10,
|
44
47
|
stackprof: nil,
|
45
48
|
}.merge(opt[:options])
|
49
|
+
opt[:lsp_options] = {
|
50
|
+
port: 0,
|
51
|
+
}.merge(opt[:lsp_options] || {})
|
46
52
|
super(**opt)
|
47
53
|
end
|
48
54
|
|
@@ -61,29 +67,39 @@ module TypeProf
|
|
61
67
|
]
|
62
68
|
end
|
63
69
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
TypeProf.send(:remove_const, :Config)
|
70
|
+
module Config
|
71
|
+
def self.current
|
72
|
+
Thread.current[:typeprof_config]
|
68
73
|
end
|
69
|
-
TypeProf.const_set(:Config, config)
|
70
74
|
|
71
|
-
|
75
|
+
def self.set_current(config)
|
76
|
+
Thread.current[:typeprof_config] = config
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.analyze(config, cancel_token = nil)
|
81
|
+
# Deploy the config to the TypeProf::Config (Note: This is thread local)
|
82
|
+
Config.set_current(config)
|
83
|
+
|
84
|
+
if Config.current.options[:stackprof]
|
72
85
|
require "stackprof"
|
73
|
-
out = "typeprof-stackprof-#{ Config.options[:stackprof] }.dump"
|
74
|
-
StackProf.start(mode: Config.options[:stackprof], out: out, raw: true)
|
86
|
+
out = "typeprof-stackprof-#{ Config.current.options[:stackprof] }.dump"
|
87
|
+
StackProf.start(mode: Config.current.options[:stackprof], out: out, raw: true)
|
75
88
|
end
|
76
89
|
|
77
90
|
scratch = Scratch.new
|
78
91
|
Builtin.setup_initial_global_env(scratch)
|
79
92
|
|
80
|
-
Config.gem_rbs_features.each do |feature|
|
93
|
+
Config.current.gem_rbs_features.each do |feature|
|
81
94
|
Import.import_library(scratch, feature)
|
82
95
|
end
|
83
96
|
|
97
|
+
collection_path = config.collection_path
|
98
|
+
Import.import_rbs_collection(scratch, collection_path) if collection_path&.exist?
|
99
|
+
|
84
100
|
rbs_files = []
|
85
101
|
rbs_codes = []
|
86
|
-
Config.rbs_files.each do |rbs|
|
102
|
+
Config.current.rbs_files.each do |rbs|
|
87
103
|
if rbs.is_a?(Array) # [String name, String content]
|
88
104
|
rbs_codes << rbs
|
89
105
|
else
|
@@ -95,30 +111,39 @@ module TypeProf
|
|
95
111
|
Import.import_rbs_code(scratch, name, content)
|
96
112
|
end
|
97
113
|
|
98
|
-
|
114
|
+
def_code_range_table = nil
|
115
|
+
caller_code_range_table = nil
|
116
|
+
Config.current.rb_files.each do |rb|
|
99
117
|
if rb.is_a?(Array) # [String name, String content]
|
100
|
-
iseq = ISeq.compile_str(*rb.reverse)
|
118
|
+
iseq, def_tbl, caller_tbl = ISeq.compile_str(*rb.reverse)
|
119
|
+
def_code_range_table ||= def_tbl
|
120
|
+
caller_code_range_table ||= caller_tbl
|
101
121
|
else
|
102
122
|
iseq = rb
|
103
123
|
end
|
104
124
|
scratch.add_entrypoint(iseq)
|
105
125
|
end
|
106
126
|
|
107
|
-
result = scratch.type_profile
|
127
|
+
result = scratch.type_profile(cancel_token)
|
128
|
+
|
129
|
+
if Config.current.options[:lsp]
|
130
|
+
return scratch.report_lsp, def_code_range_table, caller_code_range_table
|
131
|
+
end
|
108
132
|
|
109
|
-
if Config.output.respond_to?(:write)
|
110
|
-
scratch.report(result, Config.output)
|
133
|
+
if Config.current.output.respond_to?(:write)
|
134
|
+
scratch.report(result, Config.current.output)
|
111
135
|
else
|
112
|
-
open(Config.output, "w") do |output|
|
136
|
+
open(Config.current.output, "w") do |output|
|
113
137
|
scratch.report(result, output)
|
114
138
|
end
|
115
139
|
end
|
116
140
|
|
117
141
|
rescue TypeProfError => exc
|
118
|
-
exc.report(Config.output)
|
142
|
+
exc.report(Config.current.output)
|
119
143
|
|
144
|
+
return nil
|
120
145
|
ensure
|
121
|
-
if Config.options[:stackprof] && defined?(StackProf)
|
146
|
+
if Config.current.options[:stackprof] && defined?(StackProf)
|
122
147
|
StackProf.stop
|
123
148
|
StackProf.results
|
124
149
|
end
|