seafoam 0.11 → 0.14
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/bin/bgv2isabelle +3 -2
- data/bin/bgv2json +3 -2
- data/bin/seafoam +3 -2
- data/lib/seafoam/bgv/bgv_parser.rb +58 -50
- data/lib/seafoam/binary/io_binary_reader.rb +19 -17
- data/lib/seafoam/colors.rb +17 -11
- data/lib/seafoam/commands.rb +210 -202
- data/lib/seafoam/formatters/base.rb +2 -0
- data/lib/seafoam/formatters/formatters.rb +5 -3
- data/lib/seafoam/formatters/json.rb +8 -5
- data/lib/seafoam/formatters/text.rb +7 -4
- data/lib/seafoam/graal/graph_description.rb +10 -3
- data/lib/seafoam/graal/pi.rb +10 -6
- data/lib/seafoam/graal/source.rb +13 -9
- data/lib/seafoam/graph.rb +15 -9
- data/lib/seafoam/graphviz_writer.rb +129 -99
- data/lib/seafoam/isabelle_writer.rb +9 -7
- data/lib/seafoam/json_writer.rb +17 -13
- data/lib/seafoam/markdown_writer.rb +18 -0
- data/lib/seafoam/mermaid_writer.rb +52 -0
- data/lib/seafoam/passes/fallback.rb +10 -6
- data/lib/seafoam/passes/graal.rb +189 -161
- data/lib/seafoam/passes/truffle.rb +120 -22
- data/lib/seafoam/passes.rb +36 -29
- data/lib/seafoam/spotlight.rb +4 -2
- data/lib/seafoam/version.rb +3 -1
- data/lib/seafoam.rb +22 -20
- metadata +9 -40
- data/bin/cfg2asm +0 -20
- data/lib/seafoam/cfg/cfg_parser.rb +0 -93
- data/lib/seafoam/cfg/disassembler.rb +0 -70
data/lib/seafoam/commands.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "set"
|
3
5
|
|
4
6
|
module Seafoam
|
5
7
|
# Implementations of the command-line commands that you can run in Seafoam.
|
@@ -12,7 +14,7 @@ module Seafoam
|
|
12
14
|
def seafoam(*args)
|
13
15
|
first, *args = args
|
14
16
|
|
15
|
-
if first ==
|
17
|
+
if first == "--json"
|
16
18
|
formatter_module = Seafoam::Formatters::Json
|
17
19
|
first, *args = args
|
18
20
|
else
|
@@ -20,11 +22,11 @@ module Seafoam
|
|
20
22
|
end
|
21
23
|
|
22
24
|
case first
|
23
|
-
when nil,
|
24
|
-
raise ArgumentError, "unexpected arguments #{args.join(
|
25
|
+
when nil, "help", "-h", "--help", "-help"
|
26
|
+
raise ArgumentError, "unexpected arguments #{args.join(" ")}" unless args.empty?
|
25
27
|
|
26
28
|
help(*args)
|
27
|
-
when
|
29
|
+
when "version", "-v", "-version", "--version"
|
28
30
|
version(*args)
|
29
31
|
else
|
30
32
|
name = first
|
@@ -32,24 +34,24 @@ module Seafoam
|
|
32
34
|
case command
|
33
35
|
when nil
|
34
36
|
help(*args)
|
35
|
-
when
|
36
|
-
info
|
37
|
-
when
|
38
|
-
list
|
39
|
-
when
|
40
|
-
search
|
41
|
-
when
|
42
|
-
edges
|
43
|
-
when
|
44
|
-
props
|
45
|
-
when
|
46
|
-
source
|
47
|
-
when
|
48
|
-
render
|
49
|
-
when
|
50
|
-
debug
|
51
|
-
when
|
52
|
-
describe
|
37
|
+
when "info"
|
38
|
+
info(name, formatter_module, *args)
|
39
|
+
when "list"
|
40
|
+
list(name, formatter_module, *args)
|
41
|
+
when "search"
|
42
|
+
search(name, *args)
|
43
|
+
when "edges"
|
44
|
+
edges(name, formatter_module, *args)
|
45
|
+
when "props"
|
46
|
+
props(name, *args)
|
47
|
+
when "source"
|
48
|
+
source(name, formatter_module, *args)
|
49
|
+
when "render"
|
50
|
+
render(name, *args)
|
51
|
+
when "debug"
|
52
|
+
debug(name, *args)
|
53
|
+
when "describe"
|
54
|
+
describe(name, formatter_module, *args)
|
53
55
|
else
|
54
56
|
raise ArgumentError, "unknown command #{command}"
|
55
57
|
end
|
@@ -59,14 +61,14 @@ module Seafoam
|
|
59
61
|
# Run the bgv2isabelle command.
|
60
62
|
def bgv2isabelle(*args)
|
61
63
|
case args.first
|
62
|
-
when nil,
|
64
|
+
when nil, "help", "-h", "--help", "-help"
|
63
65
|
args = args.drop(1)
|
64
|
-
raise ArgumentError, "unexpected arguments #{args.join(
|
66
|
+
raise ArgumentError, "unexpected arguments #{args.join(" ")}" unless args.empty?
|
65
67
|
|
66
|
-
@out.puts
|
67
|
-
@out.puts
|
68
|
-
@out.puts
|
69
|
-
when
|
68
|
+
@out.puts "bgv2isabelle file.bgv..."
|
69
|
+
@out.puts " --help"
|
70
|
+
@out.puts " --version"
|
71
|
+
when "version", "-v", "-version", "--version"
|
70
72
|
args = args.drop(1)
|
71
73
|
version(*args)
|
72
74
|
else
|
@@ -74,10 +76,10 @@ module Seafoam
|
|
74
76
|
|
75
77
|
until args.empty?
|
76
78
|
arg = args.shift
|
77
|
-
if arg.start_with?(
|
79
|
+
if arg.start_with?("-")
|
78
80
|
raise ArgumentError, "unknown option #{arg}"
|
79
81
|
else
|
80
|
-
files.push
|
82
|
+
files.push(arg)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
@@ -96,7 +98,7 @@ module Seafoam
|
|
96
98
|
name = parser.graph_name(graph_header)
|
97
99
|
graph = parser.read_graph
|
98
100
|
|
99
|
-
writer.write
|
101
|
+
writer.write(index, name, graph)
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
@@ -104,14 +106,14 @@ module Seafoam
|
|
104
106
|
|
105
107
|
def bgv2json(*args)
|
106
108
|
case args.first
|
107
|
-
when nil,
|
109
|
+
when nil, "help", "-h", "--help", "-help"
|
108
110
|
args = args.drop(1)
|
109
|
-
raise ArgumentError, "unexpected arguments #{args.join(
|
111
|
+
raise ArgumentError, "unexpected arguments #{args.join(" ")}" unless args.empty?
|
110
112
|
|
111
|
-
@out.puts
|
112
|
-
@out.puts
|
113
|
-
@out.puts
|
114
|
-
when
|
113
|
+
@out.puts "bgv2json file.bgv..."
|
114
|
+
@out.puts " --help"
|
115
|
+
@out.puts " --version"
|
116
|
+
when "version", "-v", "-version", "--version"
|
115
117
|
args = args.drop(1)
|
116
118
|
version(*args)
|
117
119
|
else
|
@@ -119,10 +121,10 @@ module Seafoam
|
|
119
121
|
|
120
122
|
until args.empty?
|
121
123
|
arg = args.shift
|
122
|
-
if arg.start_with?(
|
124
|
+
if arg.start_with?("-")
|
123
125
|
raise ArgumentError, "unknown option #{arg}"
|
124
126
|
else
|
125
|
-
files.push
|
127
|
+
files.push(arg)
|
126
128
|
end
|
127
129
|
end
|
128
130
|
|
@@ -141,53 +143,9 @@ module Seafoam
|
|
141
143
|
name = parser.graph_name(graph_header)
|
142
144
|
graph = parser.read_graph
|
143
145
|
|
144
|
-
writer.write
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def cfg2asm(*args)
|
151
|
-
case args.first
|
152
|
-
when nil, 'help', '-h', '--help', '-help'
|
153
|
-
args = args.drop(1)
|
154
|
-
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
155
|
-
|
156
|
-
@out.puts 'cfg2asm file.bgv...'
|
157
|
-
@out.puts ' --no-comments'
|
158
|
-
@out.puts ' --help'
|
159
|
-
@out.puts ' --version'
|
160
|
-
when 'version', '-v', '-version', '--version'
|
161
|
-
args = args.drop(1)
|
162
|
-
version(*args)
|
163
|
-
else
|
164
|
-
comments = true
|
165
|
-
files = []
|
166
|
-
|
167
|
-
until args.empty?
|
168
|
-
arg = args.shift
|
169
|
-
if arg.start_with?('-')
|
170
|
-
case arg
|
171
|
-
when '--no-comments'
|
172
|
-
comments = false
|
173
|
-
else
|
174
|
-
raise ArgumentError, "unknown option #{arg}"
|
175
|
-
end
|
176
|
-
else
|
177
|
-
files.push arg
|
146
|
+
writer.write(name, graph)
|
178
147
|
end
|
179
148
|
end
|
180
|
-
|
181
|
-
files.each_with_index do |file, n|
|
182
|
-
parser = Seafoam::CFG::CFGParser.new(@out, file)
|
183
|
-
parser.skip_over_cfg 'After code installation'
|
184
|
-
nmethod = parser.read_nmethod
|
185
|
-
|
186
|
-
disassembler = Seafoam::CFG::Disassembler.new(@out)
|
187
|
-
@out.puts if n.positive?
|
188
|
-
@out.puts "[#{file}]"
|
189
|
-
disassembler.disassemble(nmethod, comments)
|
190
|
-
end
|
191
149
|
end
|
192
150
|
end
|
193
151
|
|
@@ -196,8 +154,8 @@ module Seafoam
|
|
196
154
|
# seafoam file.bgv info
|
197
155
|
def info(name, formatter_module, *args)
|
198
156
|
file, *rest = parse_name(name)
|
199
|
-
raise ArgumentError,
|
200
|
-
raise ArgumentError,
|
157
|
+
raise ArgumentError, "info only works with a file" unless rest == [nil, nil, nil]
|
158
|
+
raise ArgumentError, "info does not take arguments" unless args.empty?
|
201
159
|
|
202
160
|
parser = BGV::BGVParser.new(file)
|
203
161
|
major, minor = parser.read_file_header(version_check: false)
|
@@ -209,8 +167,8 @@ module Seafoam
|
|
209
167
|
# seafoam file.bgv list
|
210
168
|
def list(name, formatter_module, *args)
|
211
169
|
file, *rest = parse_name(name)
|
212
|
-
raise ArgumentError,
|
213
|
-
raise ArgumentError,
|
170
|
+
raise ArgumentError, "list only works with a file" unless rest == [nil, nil, nil]
|
171
|
+
raise ArgumentError, "list does not take arguments" unless args.empty?
|
214
172
|
|
215
173
|
parser = BGV::BGVParser.new(file)
|
216
174
|
parser.read_file_header
|
@@ -232,7 +190,7 @@ module Seafoam
|
|
232
190
|
# seafoam file.bgv:n... search term...
|
233
191
|
def search(name, *terms)
|
234
192
|
file, graph_index, node_id, = parse_name(name)
|
235
|
-
raise ArgumentError,
|
193
|
+
raise ArgumentError, "search only works with a file or graph" if node_id
|
236
194
|
|
237
195
|
parser = BGV::BGVParser.new(file)
|
238
196
|
parser.read_file_header
|
@@ -243,13 +201,13 @@ module Seafoam
|
|
243
201
|
|
244
202
|
if !graph_index || index == graph_index
|
245
203
|
header = parser.read_graph_header
|
246
|
-
search_object
|
204
|
+
search_object("#{file}:#{index}", header, terms)
|
247
205
|
graph = parser.read_graph
|
248
206
|
graph.nodes.each_value do |node|
|
249
|
-
search_object
|
207
|
+
search_object("#{file}:#{index}:#{node.id}", node.props, terms)
|
250
208
|
end
|
251
209
|
graph.edges.each do |edge|
|
252
|
-
search_object
|
210
|
+
search_object("#{file}:#{index}:#{edge.from.id}-#{edge.to.id}", edge.props, terms)
|
253
211
|
end
|
254
212
|
else
|
255
213
|
parser.skip_graph_header
|
@@ -275,8 +233,8 @@ module Seafoam
|
|
275
233
|
highlight_on = "\033[1m"
|
276
234
|
highlight_off = "\033[0m"
|
277
235
|
else
|
278
|
-
highlight_on =
|
279
|
-
highlight_off =
|
236
|
+
highlight_on = ""
|
237
|
+
highlight_off = ""
|
280
238
|
end
|
281
239
|
@out.puts "#{tag} ...#{before}#{highlight_on}#{match}#{highlight_off}#{after}..."
|
282
240
|
start = index + t.size
|
@@ -287,8 +245,8 @@ module Seafoam
|
|
287
245
|
# seafoam file.bgv:n... edges
|
288
246
|
def edges(name, formatter_module, *args)
|
289
247
|
file, graph_index, node_id, edge_id = parse_name(name)
|
290
|
-
raise ArgumentError,
|
291
|
-
raise ArgumentError,
|
248
|
+
raise ArgumentError, "edges needs at least a graph" unless graph_index
|
249
|
+
raise ArgumentError, "edges does not take arguments" unless args.empty?
|
292
250
|
|
293
251
|
entry = nil
|
294
252
|
|
@@ -296,16 +254,16 @@ module Seafoam
|
|
296
254
|
parser.read_graph_header
|
297
255
|
graph = parser.read_graph
|
298
256
|
if node_id
|
299
|
-
Passes.apply
|
257
|
+
Passes.apply(graph)
|
300
258
|
node = graph.nodes[node_id]
|
301
|
-
raise ArgumentError,
|
259
|
+
raise ArgumentError, "node not found" unless node
|
302
260
|
|
303
261
|
if edge_id
|
304
262
|
to = graph.nodes[edge_id]
|
305
|
-
raise ArgumentError,
|
263
|
+
raise ArgumentError, "edge node not found" unless to
|
306
264
|
|
307
265
|
edges = node.outputs.select { |edge| edge.to == to }
|
308
|
-
raise ArgumentError,
|
266
|
+
raise ArgumentError, "edge not found" if edges.empty?
|
309
267
|
|
310
268
|
entry = formatter_module::EdgesFormatter::EdgesEntry.new(edges)
|
311
269
|
else
|
@@ -323,8 +281,10 @@ module Seafoam
|
|
323
281
|
|
324
282
|
# seafoam file.bgv... props
|
325
283
|
def props(name, *args)
|
284
|
+
# rubocop:disable Metrics/BlockNesting
|
285
|
+
|
326
286
|
file, graph_index, node_id, edge_id = parse_name(name)
|
327
|
-
raise ArgumentError,
|
287
|
+
raise ArgumentError, "props does not take arguments" unless args.empty?
|
328
288
|
|
329
289
|
if graph_index
|
330
290
|
with_graph(file, graph_index) do |parser|
|
@@ -332,29 +292,29 @@ module Seafoam
|
|
332
292
|
if node_id
|
333
293
|
graph = parser.read_graph
|
334
294
|
node = graph.nodes[node_id]
|
335
|
-
raise ArgumentError,
|
295
|
+
raise ArgumentError, "node not found" unless node
|
336
296
|
|
337
297
|
if edge_id
|
338
298
|
to = graph.nodes[edge_id]
|
339
|
-
raise ArgumentError,
|
299
|
+
raise ArgumentError, "edge node not found" unless to
|
340
300
|
|
341
301
|
edges = node.outputs.select { |edge| edge.to == to }
|
342
|
-
raise ArgumentError,
|
302
|
+
raise ArgumentError, "edge not found" if edges.empty?
|
343
303
|
|
344
304
|
if edges.size > 1
|
345
305
|
edges.each do |edge|
|
346
|
-
pretty_print
|
306
|
+
pretty_print(edge.props)
|
347
307
|
@out.puts
|
348
308
|
end
|
349
309
|
else
|
350
|
-
pretty_print
|
310
|
+
pretty_print(edges.first.props)
|
351
311
|
end
|
352
312
|
else
|
353
|
-
pretty_print
|
313
|
+
pretty_print(node.props)
|
354
314
|
end
|
355
315
|
break
|
356
316
|
else
|
357
|
-
pretty_print
|
317
|
+
pretty_print(graph_header)
|
358
318
|
parser.skip_graph
|
359
319
|
end
|
360
320
|
end
|
@@ -362,24 +322,26 @@ module Seafoam
|
|
362
322
|
parser = BGV::BGVParser.new(file)
|
363
323
|
parser.read_file_header
|
364
324
|
document_props = parser.read_document_props
|
365
|
-
pretty_print
|
325
|
+
pretty_print(document_props || {})
|
366
326
|
end
|
327
|
+
|
328
|
+
# rubocop:enable Metrics/BlockNesting
|
367
329
|
end
|
368
330
|
|
369
331
|
# seafoam file.bgv:n:n source
|
370
332
|
def source(name, formatter_module, *args)
|
371
333
|
file, graph_index, node_id, edge_id = parse_name(name)
|
372
|
-
raise ArgumentError,
|
373
|
-
raise ArgumentError,
|
374
|
-
raise ArgumentError,
|
334
|
+
raise ArgumentError, "source needs a node" unless node_id
|
335
|
+
raise ArgumentError, "source only works with a node" if edge_id
|
336
|
+
raise ArgumentError, "source does not take arguments" unless args.empty?
|
375
337
|
|
376
338
|
with_graph(file, graph_index) do |parser|
|
377
339
|
parser.read_graph_header
|
378
340
|
graph = parser.read_graph
|
379
341
|
node = graph.nodes[node_id]
|
380
|
-
raise ArgumentError,
|
342
|
+
raise ArgumentError, "node not found" unless node
|
381
343
|
|
382
|
-
formatter = formatter_module::SourceFormatter.new(node.props[
|
344
|
+
formatter = formatter_module::SourceFormatter.new(node.props["nodeSourcePosition"])
|
383
345
|
@out.puts formatter.format
|
384
346
|
end
|
385
347
|
end
|
@@ -389,9 +351,9 @@ module Seafoam
|
|
389
351
|
file, graph_index, *rest = parse_name(name)
|
390
352
|
|
391
353
|
if graph_index.nil? || !rest.all?(&:nil?)
|
392
|
-
raise ArgumentError,
|
354
|
+
raise ArgumentError, "describe only works with a graph"
|
393
355
|
end
|
394
|
-
raise ArgumentError,
|
356
|
+
raise ArgumentError, "describe does not take arguments" unless args.empty?
|
395
357
|
|
396
358
|
parser = BGV::BGVParser.new(file)
|
397
359
|
parser.read_file_header
|
@@ -412,19 +374,23 @@ module Seafoam
|
|
412
374
|
description = Seafoam::Graal::GraphDescription.new
|
413
375
|
|
414
376
|
graph.nodes.each_value do |node|
|
415
|
-
node_class = node.
|
377
|
+
node_class = node.node_class
|
378
|
+
|
379
|
+
simple_node_class = node_class[/([^.]+)$/, 1]
|
380
|
+
description.node_counts[simple_node_class] += 1
|
381
|
+
|
416
382
|
case node_class
|
417
|
-
when
|
383
|
+
when "org.graalvm.compiler.nodes.IfNode"
|
418
384
|
description.branches = true
|
419
|
-
when
|
385
|
+
when "org.graalvm.compiler.nodes.LoopBeginNode"
|
420
386
|
description.loops = true
|
421
|
-
when
|
387
|
+
when "org.graalvm.compiler.nodes.InvokeNode", "org.graalvm.compiler.nodes.InvokeWithExceptionNode"
|
422
388
|
description.calls = true
|
423
389
|
end
|
424
390
|
end
|
425
391
|
|
426
392
|
description.deopts = graph.nodes[0].outputs.map(&:to)
|
427
|
-
|
393
|
+
.all? { |t| t.node_class == "org.graalvm.compiler.nodes.DeoptimizeNode" }
|
428
394
|
|
429
395
|
formatter = formatter_module::DescribeFormatter.new(graph, description)
|
430
396
|
@out.puts formatter.format
|
@@ -436,15 +402,18 @@ module Seafoam
|
|
436
402
|
# seafoam file.bgv:n render options...
|
437
403
|
def render(name, *args)
|
438
404
|
file, graph_index, *rest = parse_name(name)
|
439
|
-
raise ArgumentError,
|
440
|
-
raise ArgumentError,
|
405
|
+
raise ArgumentError, "render needs at least a graph" unless graph_index
|
406
|
+
raise ArgumentError, "render only works with a graph" unless rest == [nil, nil]
|
441
407
|
|
442
408
|
pass_options = {
|
443
409
|
simplify_truffle_args: true,
|
444
410
|
hide_frame_state: true,
|
445
411
|
hide_pi: true,
|
412
|
+
hide_begin_end: true,
|
446
413
|
hide_floating: false,
|
447
|
-
reduce_edges: true
|
414
|
+
reduce_edges: true,
|
415
|
+
simplify_alloc: true,
|
416
|
+
hide_null_fields: true,
|
448
417
|
}
|
449
418
|
spotlight_nodes = nil
|
450
419
|
args = args.dup
|
@@ -454,85 +423,119 @@ module Seafoam
|
|
454
423
|
until args.empty?
|
455
424
|
arg = args.shift
|
456
425
|
case arg
|
457
|
-
when
|
426
|
+
when "--out"
|
458
427
|
out_file = args.shift
|
459
428
|
explicit_out_file = true
|
460
|
-
raise ArgumentError,
|
461
|
-
|
429
|
+
raise ArgumentError, "no file for --out" unless out_file
|
430
|
+
|
431
|
+
out_ext = File.extname(out_file).downcase
|
432
|
+
case out_ext
|
433
|
+
when ".pdf"
|
434
|
+
out_format = :pdf
|
435
|
+
when ".svg"
|
436
|
+
out_format = :svg
|
437
|
+
when ".png"
|
438
|
+
out_format = :png
|
439
|
+
when ".dot"
|
440
|
+
out_format = :dot
|
441
|
+
when ".mmd"
|
442
|
+
out_format = :mmd
|
443
|
+
when ".md"
|
444
|
+
out_format = :md
|
445
|
+
else
|
446
|
+
raise ArgumentError, "unknown render format #{out_ext}"
|
447
|
+
end
|
448
|
+
when "--md"
|
449
|
+
out_file = @out
|
450
|
+
out_format = :md
|
451
|
+
explicit_out_file = true
|
452
|
+
when "--spotlight"
|
462
453
|
spotlight_arg = args.shift
|
463
|
-
raise ArgumentError,
|
454
|
+
raise ArgumentError, "no list for --spotlight" unless spotlight_arg
|
464
455
|
|
465
|
-
spotlight_nodes = spotlight_arg.split(
|
466
|
-
when
|
456
|
+
spotlight_nodes = spotlight_arg.split(",").map { |n| Integer(n) }
|
457
|
+
when "--full-truffle-args"
|
467
458
|
pass_options[:simplify_truffle_args] = false
|
468
|
-
when
|
459
|
+
when "--no-simplify-alloc"
|
460
|
+
pass_options[:simplify_alloc] = false
|
461
|
+
when "--show-null-fields"
|
462
|
+
pass_options[:hide_null_fields] = false
|
463
|
+
when "--show-frame-state"
|
469
464
|
pass_options[:hide_frame_state] = false
|
470
|
-
when
|
465
|
+
when "--show-pi"
|
471
466
|
pass_options[:hide_pi] = false
|
472
|
-
when
|
467
|
+
when "--show-begin-end"
|
468
|
+
pass_options[:hide_begin_end] = false
|
469
|
+
when "--hide-floating"
|
473
470
|
pass_options[:hide_floating] = true
|
474
|
-
when
|
471
|
+
when "--no-reduce-edges"
|
475
472
|
pass_options[:reduce_edges] = false
|
476
|
-
when
|
473
|
+
when "--draw-blocks"
|
477
474
|
draw_blocks = true
|
478
|
-
when
|
475
|
+
when "--option"
|
479
476
|
key = args.shift
|
480
|
-
raise ArgumentError,
|
477
|
+
raise ArgumentError, "no key for --option" unless key
|
481
478
|
|
482
479
|
value = args.shift
|
483
480
|
raise ArgumentError, "no value for --option #{key}" unless out_file
|
484
481
|
|
485
|
-
value = {
|
482
|
+
value = { "true" => true, "false" => "false" }.fetch(key, value)
|
486
483
|
pass_options[key.to_sym] = value
|
487
484
|
else
|
488
485
|
raise ArgumentError, "unexpected option #{arg}"
|
489
486
|
end
|
490
487
|
end
|
491
|
-
out_file ||=
|
492
|
-
|
493
|
-
case out_ext
|
494
|
-
when '.pdf'
|
495
|
-
out_format = :pdf
|
496
|
-
when '.svg'
|
497
|
-
out_format = :svg
|
498
|
-
when '.png'
|
499
|
-
out_format = :png
|
500
|
-
when '.dot'
|
501
|
-
out_format = :dot
|
502
|
-
else
|
503
|
-
raise ArgumentError, "unknown render format #{out_ext}"
|
504
|
-
end
|
505
|
-
|
488
|
+
out_file ||= "graph.pdf"
|
489
|
+
out_format ||= :pdf
|
506
490
|
with_graph(file, graph_index) do |parser|
|
507
491
|
parser.skip_graph_header
|
508
492
|
graph = parser.read_graph
|
509
|
-
Passes.apply
|
493
|
+
Passes.apply(graph, pass_options)
|
510
494
|
if spotlight_nodes
|
511
495
|
spotlight = Spotlight.new(graph)
|
512
496
|
spotlight_nodes.each do |node_id|
|
513
497
|
node = graph.nodes[node_id]
|
514
|
-
raise ArgumentError,
|
498
|
+
raise ArgumentError, "node not found" unless node
|
515
499
|
|
516
|
-
spotlight.light
|
500
|
+
spotlight.light(node)
|
517
501
|
end
|
518
502
|
spotlight.shade
|
519
503
|
end
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
504
|
+
case out_format
|
505
|
+
when :dot, :mmd, :md
|
506
|
+
action = lambda do |stream|
|
507
|
+
case out_format
|
508
|
+
when :dot
|
509
|
+
writer = GraphvizWriter.new(stream)
|
510
|
+
writer.write_graph(graph, false, draw_blocks)
|
511
|
+
when :mmd
|
512
|
+
writer = MermaidWriter.new(stream)
|
513
|
+
writer.write_graph(graph, false, draw_blocks)
|
514
|
+
when :md
|
515
|
+
writer = MarkdownWriter.new(stream)
|
516
|
+
writer.write_graph(graph)
|
517
|
+
else
|
518
|
+
raise
|
519
|
+
end
|
520
|
+
end
|
521
|
+
if out_file.is_a?(String)
|
522
|
+
File.open(out_file, "w") do |stream|
|
523
|
+
action.call(stream)
|
524
|
+
end
|
525
|
+
else
|
526
|
+
action.call(out_file)
|
524
527
|
end
|
525
528
|
else
|
526
529
|
begin
|
527
|
-
IO.popen([
|
530
|
+
IO.popen(["dot", "-T#{out_format}", "-o", out_file], "w") do |stream|
|
528
531
|
writer = GraphvizWriter.new(stream)
|
529
532
|
hidpi = out_format == :png
|
530
|
-
writer.write_graph
|
533
|
+
writer.write_graph(graph, hidpi, draw_blocks)
|
531
534
|
end
|
532
535
|
rescue Errno::ENOENT
|
533
|
-
raise
|
536
|
+
raise "Could not run Graphviz - is it installed?"
|
534
537
|
end
|
535
|
-
autoopen
|
538
|
+
autoopen(out_file) unless explicit_out_file
|
536
539
|
end
|
537
540
|
end
|
538
541
|
end
|
@@ -540,12 +543,12 @@ module Seafoam
|
|
540
543
|
# seafoam file.bgv debug options...
|
541
544
|
def debug(name, *args)
|
542
545
|
file, *rest = parse_name(name)
|
543
|
-
raise ArgumentError,
|
546
|
+
raise ArgumentError, "debug only works with a file" unless rest == [nil, nil, nil]
|
544
547
|
|
545
548
|
skip = false
|
546
549
|
args.each do |arg|
|
547
550
|
case arg
|
548
|
-
when
|
551
|
+
when "--skip"
|
549
552
|
skip = true
|
550
553
|
else
|
551
554
|
raise ArgumentError, "unexpected option #{arg}"
|
@@ -555,10 +558,10 @@ module Seafoam
|
|
555
558
|
File.open(file) do |stream|
|
556
559
|
parser = BGVDebugParser.new(@out, stream)
|
557
560
|
begin
|
558
|
-
pretty_print
|
561
|
+
pretty_print(parser.read_file_header)
|
559
562
|
document_props = parser.read_document_props
|
560
563
|
if document_props
|
561
|
-
pretty_print
|
564
|
+
pretty_print(document_props)
|
562
565
|
end
|
563
566
|
loop do
|
564
567
|
index, id = parser.read_graph_preheader
|
@@ -569,8 +572,8 @@ module Seafoam
|
|
569
572
|
parser.skip_graph_header
|
570
573
|
parser.skip_graph
|
571
574
|
else
|
572
|
-
pretty_print
|
573
|
-
pretty_print
|
575
|
+
pretty_print(parser.read_graph_header)
|
576
|
+
pretty_print(parser.read_graph)
|
574
577
|
end
|
575
578
|
end
|
576
579
|
rescue StandardError => e
|
@@ -613,53 +616,58 @@ module Seafoam
|
|
613
616
|
parser.skip_graph
|
614
617
|
end
|
615
618
|
end
|
616
|
-
raise ArgumentError,
|
619
|
+
raise ArgumentError, "graph not found" unless graph_found
|
617
620
|
end
|
618
621
|
|
619
622
|
# Prints help.
|
620
623
|
def help(*_args)
|
621
|
-
@out.puts
|
622
|
-
@out.puts
|
623
|
-
@out.puts
|
624
|
-
@out.puts
|
625
|
-
@out.puts
|
626
|
-
@out.puts
|
627
|
-
@out.puts
|
628
|
-
@out.puts
|
629
|
-
@out.puts
|
630
|
-
@out.puts
|
631
|
-
@out.puts
|
632
|
-
@out.puts
|
633
|
-
@out.puts
|
634
|
-
@out.puts
|
635
|
-
@out.puts
|
636
|
-
@out.puts
|
637
|
-
@out.puts
|
638
|
-
@out.puts
|
639
|
-
@out.puts
|
640
|
-
@out.puts
|
641
|
-
@out.puts
|
642
|
-
@out.puts
|
624
|
+
@out.puts "seafoam file.bgv info"
|
625
|
+
@out.puts " file.bgv list"
|
626
|
+
@out.puts " file.bgv[:graph][:node[-edge]] search term..."
|
627
|
+
@out.puts " file.bgv[:graph][:node[-edge]] edges"
|
628
|
+
@out.puts " file.bgv[:graph][:node[-edge]] props"
|
629
|
+
@out.puts " file.bgv:graph:node source"
|
630
|
+
@out.puts " file.bgv:graph describe"
|
631
|
+
@out.puts " file.bgv:graph render"
|
632
|
+
@out.puts " --spotlight n,n,n..."
|
633
|
+
@out.puts " --out graph.pdf"
|
634
|
+
@out.puts " graph.svg"
|
635
|
+
@out.puts " graph.png"
|
636
|
+
@out.puts " graph.dot (Graphviz)"
|
637
|
+
@out.puts " graph.mmd (Mermaid)"
|
638
|
+
@out.puts " graph.md (Markdown)"
|
639
|
+
@out.puts " --full-truffle-args"
|
640
|
+
@out.puts " --show-frame-state"
|
641
|
+
@out.puts " --no-simplify-alloc"
|
642
|
+
@out.puts " --show-null-fields"
|
643
|
+
@out.puts " --show-pi"
|
644
|
+
@out.puts " --show-begin-end"
|
645
|
+
@out.puts " --hide-floating"
|
646
|
+
@out.puts " --no-reduce-edges"
|
647
|
+
@out.puts " --draw-blocks"
|
648
|
+
@out.puts " --option key value"
|
649
|
+
@out.puts " --help"
|
650
|
+
@out.puts " --version"
|
643
651
|
end
|
644
652
|
|
645
653
|
# Prints the version.
|
646
654
|
def version(*args)
|
647
|
-
raise ArgumentError, "unexpected arguments #{args.join(
|
655
|
+
raise ArgumentError, "unexpected arguments #{args.join(" ")}" unless args.empty?
|
648
656
|
|
649
657
|
@out.puts "seafoam #{VERSION}"
|
650
658
|
end
|
651
659
|
|
652
660
|
# Parse a name like file.bgv:g:n-e to [file.bgv, g, n, e].
|
653
661
|
def parse_name(name)
|
654
|
-
*pre, post = name.split(
|
662
|
+
*pre, post = name.split(".")
|
655
663
|
|
656
|
-
file_ext, graph, node, *rest = post.split(
|
664
|
+
file_ext, graph, node, *rest = post.split(":")
|
657
665
|
raise ArgumentError, "too many parts to .ext:g:n-e in #{name}" unless rest.empty?
|
658
666
|
|
659
|
-
file = [*pre, file_ext].join(
|
667
|
+
file = [*pre, file_ext].join(".")
|
660
668
|
|
661
669
|
if node
|
662
|
-
node, edge, *rest = node.split(
|
670
|
+
node, edge, *rest = node.split("-")
|
663
671
|
raise ArgumentError, "too many parts to edge name in #{name}" unless rest.empty?
|
664
672
|
else
|
665
673
|
node = nil
|
@@ -679,9 +687,9 @@ module Seafoam
|
|
679
687
|
|
680
688
|
case RUBY_PLATFORM
|
681
689
|
when /darwin/
|
682
|
-
system
|
690
|
+
system("open", file)
|
683
691
|
when /linux/
|
684
|
-
system
|
692
|
+
system("xdg-open", file)
|
685
693
|
end
|
686
694
|
# Don't worry if it fails.
|
687
695
|
end
|