seafoam 0.3 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/workflows.yml +40 -0
  3. data/.gitignore +0 -1
  4. data/.rubocop.yml +1 -1
  5. data/.ruby-version +1 -1
  6. data/Gemfile.lock +59 -0
  7. data/README.md +76 -3
  8. data/bin/bgv2isabelle +16 -45
  9. data/bin/bgv2json +18 -36
  10. data/bin/cfg2asm +24 -0
  11. data/bin/seafoam +1 -1
  12. data/demos/box-unbox-stats +65 -0
  13. data/docs/bgv.md +2 -1
  14. data/docs/getting-graphs.md +8 -0
  15. data/examples/Fib.java +1 -1
  16. data/examples/fib-java.bgv.gz +0 -0
  17. data/examples/fib.js +1 -1
  18. data/examples/java/JavaExamples.java +1 -1
  19. data/examples/ruby/clamps.rb +20 -0
  20. data/examples/ruby/graal.patch +15 -0
  21. data/examples/ruby/ruby_examples.rb +278 -0
  22. data/lib/seafoam.rb +5 -1
  23. data/lib/seafoam/annotators/graal.rb +1 -1
  24. data/lib/seafoam/bgv/bgv_parser.rb +10 -2
  25. data/lib/seafoam/cfg/cfg_parser.rb +93 -0
  26. data/lib/seafoam/cfg/disassembler.rb +70 -0
  27. data/lib/seafoam/commands.rb +190 -30
  28. data/lib/seafoam/graal/source.rb +23 -0
  29. data/lib/seafoam/graph.rb +25 -1
  30. data/lib/seafoam/graphviz_writer.rb +21 -2
  31. data/lib/seafoam/isabelle_writer.rb +46 -0
  32. data/lib/seafoam/json_writer.rb +58 -0
  33. data/lib/seafoam/version.rb +1 -1
  34. data/seafoam.gemspec +4 -2
  35. data/spec/seafoam/annotators/graal_spec.rb +7 -7
  36. data/spec/seafoam/bgv/bgv_parser_spec.rb +13 -3
  37. data/spec/seafoam/cfg/cfg_parser_spec.rb +21 -0
  38. data/spec/seafoam/cfg/disassembler_spec.rb +32 -0
  39. data/spec/seafoam/command_spec.rb +86 -40
  40. data/spec/seafoam/json_writer_spec.rb +14 -0
  41. data/spec/seafoam/spec_helpers.rb +4 -0
  42. data/spec/seafoam/spotlight_spec.rb +1 -1
  43. data/tools/render-all +2 -2
  44. metadata +33 -96
  45. data/.github/workflows/rubocop.yml +0 -10
  46. data/.github/workflows/specs.yml +0 -19
  47. data/examples/fib-java.bgv +0 -0
  48. data/examples/fib-js.bgv +0 -0
  49. data/examples/fib-ruby.bgv +0 -0
  50. data/examples/identity.bgv +0 -0
  51. data/examples/java/exampleArithOperator.bgv +0 -0
  52. data/examples/java/exampleArithOperator.cfg +0 -925
  53. data/examples/java/exampleArrayAllocation.bgv +0 -0
  54. data/examples/java/exampleArrayAllocation.cfg +0 -5268
  55. data/examples/java/exampleArrayRead.bgv +0 -0
  56. data/examples/java/exampleArrayRead.cfg +0 -2263
  57. data/examples/java/exampleArrayWrite.bgv +0 -0
  58. data/examples/java/exampleArrayWrite.cfg +0 -2315
  59. data/examples/java/exampleCatch.bgv +0 -0
  60. data/examples/java/exampleCatch.cfg +0 -4150
  61. data/examples/java/exampleCompareOperator.bgv +0 -0
  62. data/examples/java/exampleCompareOperator.cfg +0 -1109
  63. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  64. data/examples/java/exampleDoubleSynchronized.cfg +0 -26497
  65. data/examples/java/exampleExactArith.bgv +0 -0
  66. data/examples/java/exampleExactArith.cfg +0 -1888
  67. data/examples/java/exampleFieldRead.bgv +0 -0
  68. data/examples/java/exampleFieldRead.cfg +0 -1228
  69. data/examples/java/exampleFieldWrite.bgv +0 -0
  70. data/examples/java/exampleFieldWrite.cfg +0 -1102
  71. data/examples/java/exampleFor.bgv +0 -0
  72. data/examples/java/exampleFor.cfg +0 -3936
  73. data/examples/java/exampleFullEscape.bgv +0 -0
  74. data/examples/java/exampleFullEscape.cfg +0 -5893
  75. data/examples/java/exampleIf.bgv +0 -0
  76. data/examples/java/exampleIf.cfg +0 -2462
  77. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  78. data/examples/java/exampleIfNeverTaken.cfg +0 -2476
  79. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  80. data/examples/java/exampleInstanceOfManyImpls.cfg +0 -6391
  81. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  82. data/examples/java/exampleInstanceOfOneImpl.cfg +0 -2604
  83. data/examples/java/exampleIntSwitch.bgv +0 -0
  84. data/examples/java/exampleIntSwitch.cfg +0 -3121
  85. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  86. data/examples/java/exampleInterfaceCallManyImpls.cfg +0 -1358
  87. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  88. data/examples/java/exampleInterfaceCallOneImpl.cfg +0 -3859
  89. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  90. data/examples/java/exampleLocalInstanceOf.cfg +0 -5276
  91. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  92. data/examples/java/exampleLocalSynchronized.cfg +0 -1364
  93. data/examples/java/exampleLocalVariables.bgv +0 -0
  94. data/examples/java/exampleLocalVariables.cfg +0 -1195
  95. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  96. data/examples/java/exampleLocalVariablesState.cfg +0 -1673
  97. data/examples/java/exampleNestedWhile.bgv +0 -0
  98. data/examples/java/exampleNestedWhile.cfg +0 -15499
  99. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  100. data/examples/java/exampleNestedWhileBreak.cfg +0 -11162
  101. data/examples/java/exampleNoEscape.bgv +0 -0
  102. data/examples/java/exampleNoEscape.cfg +0 -974
  103. data/examples/java/exampleObjectAllocation.bgv +0 -0
  104. data/examples/java/exampleObjectAllocation.cfg +0 -5287
  105. data/examples/java/examplePartialEscape.bgv +0 -0
  106. data/examples/java/examplePartialEscape.cfg +0 -7042
  107. data/examples/java/examplePhi.bgv +0 -0
  108. data/examples/java/examplePhi.cfg +0 -3227
  109. data/examples/java/exampleReducible.bgv +0 -0
  110. data/examples/java/exampleReducible.cfg +0 -5578
  111. data/examples/java/exampleSimpleCall.bgv +0 -0
  112. data/examples/java/exampleSimpleCall.cfg +0 -1435
  113. data/examples/java/exampleStamp.bgv +0 -0
  114. data/examples/java/exampleStamp.cfg +0 -913
  115. data/examples/java/exampleStaticCall.bgv +0 -0
  116. data/examples/java/exampleStaticCall.cfg +0 -1154
  117. data/examples/java/exampleStringSwitch.bgv +0 -0
  118. data/examples/java/exampleStringSwitch.cfg +0 -15377
  119. data/examples/java/exampleSynchronized.bgv +0 -0
  120. data/examples/java/exampleSynchronized.cfg +0 -26027
  121. data/examples/java/exampleThrow.bgv +0 -0
  122. data/examples/java/exampleThrow.cfg +0 -780
  123. data/examples/java/exampleThrowCatch.bgv +0 -0
  124. data/examples/java/exampleThrowCatch.cfg +0 -744
  125. data/examples/java/exampleUnsafeRead.bgv +0 -0
  126. data/examples/java/exampleUnsafeRead.cfg +0 -912
  127. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  128. data/examples/java/exampleUnsafeWrite.cfg +0 -962
  129. data/examples/java/exampleWhile.bgv +0 -0
  130. data/examples/java/exampleWhile.cfg +0 -3936
  131. data/examples/java/exampleWhileBreak.bgv +0 -0
  132. data/examples/java/exampleWhileBreak.cfg +0 -5963
  133. data/examples/matmult-java.bgv +0 -0
  134. data/examples/matmult-ruby.bgv +0 -0
  135. data/examples/overflow.bgv +0 -0
  136. data/spec/seafoam/bgv/fixtures/not.bgv +0 -1
  137. data/spec/seafoam/bgv/fixtures/unsupported.bgv +0 -1
@@ -0,0 +1,46 @@
1
+ module Seafoam
2
+ # Write graphs in the Isabelle file format.
3
+ class IsabelleWriter
4
+ def initialize(out)
5
+ @out = out
6
+ end
7
+
8
+ def write(index, name, graph)
9
+ # definition eg_short_cut_or1 :: IRGraph where
10
+ # "eg_short_cut_or1 =
11
+ # (add_node 14 ReturnNode [13] []
12
+ # (add_node 13 PhiNode [10, 11, 12] []
13
+ # (add_node 12 (ConstantNode 0) [] []
14
+ # (add_node 11 (ConstantNode 42) [] []
15
+ # (add_node 10 MergeNode [7, 9] [14]
16
+ # (add_node 9 EndNode [] []
17
+ # (add_node 8 BeginNode [] [9]
18
+ # (add_node 7 EndNode [] []
19
+ # (add_node 6 BeginNode [] [7]
20
+ # (add_node 5 IfNode [3] [6, 8]
21
+ # (add_node 3 (ShortCircuitOrNode False False) [1, 2] []
22
+ # (add_node 2 (ParameterNode 1) [] []
23
+ # (add_node 1 (ParameterNode 0) [] []
24
+ # (add_node 0 StartNode [] [5]
25
+ # empty_graph))))))))))))))"
26
+
27
+ @out.puts "graph#{index} = # #{name}"
28
+
29
+ graph.nodes.each_value do |node|
30
+ node_class = node.props[:node_class][:node_class]
31
+ case node_class
32
+ when 'org.graalvm.compiler.nodes.ConstantNode'
33
+ desc = "(ConstantNode #{node.props['rawvalue']})"
34
+ when 'org.graalvm.compiler.nodes.ParameterNode'
35
+ desc = "(ParameterNode #{node.props['index']})"
36
+ else
37
+ desc = node_class.split('.').last
38
+ end
39
+ inputs = node.inputs.map(&:from).map(&:id)
40
+ outputs = node.outputs.map(&:to).map(&:id)
41
+ @out.puts " (add_node #{node.id} #{desc} #{inputs.inspect} #{outputs.inspect}"
42
+ end
43
+ @out.puts ' empty_graph' + (')' * graph.nodes.size)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,58 @@
1
+ require 'json'
2
+
3
+ module Seafoam
4
+ # Write files in a JSON format.
5
+ class JSONWriter
6
+ def initialize(out)
7
+ @out = out
8
+ end
9
+
10
+ def write(name, graph)
11
+ nodes = []
12
+ edges = []
13
+
14
+ graph.nodes.each_value do |node|
15
+ nodes.push(
16
+ id: node.id,
17
+ props: node.props
18
+ )
19
+
20
+ node.outputs.each do |edge|
21
+ edges.push(
22
+ from: edge.from.id,
23
+ to: edge.to.id,
24
+ props: edge.props
25
+ )
26
+ end
27
+ end
28
+
29
+ object = {
30
+ name: name,
31
+ props: graph.props,
32
+ nodes: nodes,
33
+ edges: edges
34
+ }
35
+
36
+ @out.puts JSON.pretty_generate(prepare_json(object))
37
+ end
38
+
39
+ private
40
+
41
+ def prepare_json(object)
42
+ case object
43
+ when Float
44
+ if object.nan?
45
+ '[NaN]'
46
+ else
47
+ object
48
+ end
49
+ when Array
50
+ object.map { |o| prepare_json(o) }
51
+ when Hash
52
+ object.transform_values { |v| prepare_json(v) }
53
+ else
54
+ object
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  module Seafoam
2
2
  MAJOR_VERSION = 0
3
- MINOR_VERSION = 3
3
+ MINOR_VERSION = 8
4
4
  VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
5
5
  end
data/seafoam.gemspec CHANGED
@@ -8,12 +8,14 @@ Gem::Specification.new do |spec|
8
8
  spec.homepage = 'https://github.com/Shopify/seafoam'
9
9
  spec.license = 'MIT'
10
10
 
11
- spec.files = `git ls-files`.split("\n")
11
+ spec.files = `git ls-files`.split("\n").reject { |f| f.end_with?('.bgv') || f.end_with?('.cfg') }
12
12
  spec.bindir = 'bin'
13
- spec.executables = %w[seafoam bgv2json bgv2isabelle]
13
+ spec.executables = %w[seafoam bgv2json bgv2isabelle cfg2asm]
14
14
 
15
15
  spec.required_ruby_version = '>= 2.5.8'
16
16
 
17
+ spec.add_dependency 'crabstone', '~> 4.0'
18
+
17
19
  spec.add_development_dependency 'benchmark-ips', '~> 2.7'
18
20
  spec.add_development_dependency 'rspec', '~> 3.8'
19
21
  spec.add_development_dependency 'rubocop', '~> 0.74'
@@ -20,7 +20,7 @@ describe Seafoam::Annotators::GraalAnnotator do
20
20
  describe 'when run' do
21
21
  describe 'without options' do
22
22
  before :all do
23
- @graph = Seafoam::SpecHelpers.example_graph('matmult-ruby', 2)
23
+ @graph = Seafoam::SpecHelpers.example_graph('matmult-ruby', 8)
24
24
  annotator = Seafoam::Annotators::GraalAnnotator.new({})
25
25
  annotator.annotate @graph
26
26
  end
@@ -37,14 +37,14 @@ describe Seafoam::Annotators::GraalAnnotator do
37
37
  expect(@graph.edges.any? { |e| e.props[:kind].nil? }).to be_falsey
38
38
  end
39
39
 
40
- it 'annotates negated GuardNodes with "Guard not, else ..."' do
41
- expect(@graph.nodes[561].props['negated']).to be true
42
- expect(@graph.nodes[561].props[:label]).to start_with 'Guard not, else'
40
+ it 'annotates not negated GuardNodes with "Guard, else ..."' do
41
+ expect(@graph.nodes[7101].props['negated']).to be false
42
+ expect(@graph.nodes[7101].props[:label]).to start_with 'Guard, else'
43
43
  end
44
44
 
45
- it 'annotates not negated GuardNodes with "Guard, else ..."' do
46
- expect(@graph.nodes[559].props['negated']).to be false
47
- expect(@graph.nodes[559].props[:label]).to start_with 'Guard, else'
45
+ it 'annotates negated GuardNodes with "Guard not, else ..."' do
46
+ expect(@graph.nodes[7102].props['negated']).to be true
47
+ expect(@graph.nodes[7102].props[:label]).to start_with 'Guard not, else'
48
48
  end
49
49
  end
50
50
 
@@ -52,6 +52,16 @@ describe Seafoam::BGV::BGVParser do
52
52
  end
53
53
  end
54
54
 
55
+ it 'can read basic blocks' do
56
+ parser = Seafoam::BGV::BGVParser.new(File.expand_path('../../../examples/fib-java.bgv', __dir__))
57
+ parser.read_file_header
58
+ parser.skip_document_props
59
+ parser.read_graph_preheader
60
+ parser.skip_graph_header
61
+ graph = parser.read_graph
62
+ expect(graph.blocks.size).to eq 3
63
+ end
64
+
55
65
  it 'can alternate skipping and reading full files' do
56
66
  Seafoam::SpecHelpers::SAMPLE_BGV.each do |file|
57
67
  parser = Seafoam::BGV::BGVParser.new(file)
@@ -77,7 +87,7 @@ describe Seafoam::BGV::BGVParser do
77
87
  describe '#read_file_header' do
78
88
  it 'produces a version' do
79
89
  parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
80
- expect(parser.read_file_header).to eq [6, 1]
90
+ expect(parser.read_file_header).to eq [7, 0]
81
91
  end
82
92
 
83
93
  it 'raises an error for files which are not BGV' do
@@ -108,7 +118,7 @@ describe Seafoam::BGV::BGVParser do
108
118
  parser = Seafoam::BGV::BGVParser.new(@fib_java_bgv)
109
119
  parser.read_file_header
110
120
  parser.skip_document_props
111
- 51.times do
121
+ 56.times do
112
122
  expect(parser.read_graph_preheader).to_not be_nil
113
123
  parser.skip_graph_header
114
124
  parser.skip_graph
@@ -150,7 +160,7 @@ describe Seafoam::BGV::BGVParser do
150
160
  parser.read_graph_preheader
151
161
  parser.read_graph_header
152
162
  graph = parser.read_graph
153
- expect(graph.nodes.size).to eq 22
163
+ expect(graph.nodes.size).to eq 21
154
164
  expect(graph.edges.size).to eq 30
155
165
  end
156
166
  end
@@ -0,0 +1,21 @@
1
+ require 'seafoam'
2
+
3
+ require 'rspec'
4
+
5
+ require_relative '../spec_helpers'
6
+
7
+ describe Seafoam::CFG::CFGParser do
8
+ before :all do
9
+ @example_cfg = File.expand_path('../../../examples/java/exampleIf.cfg', __dir__)
10
+ end
11
+
12
+ it 'correctly parses information from an nmethod' do
13
+ parser = Seafoam::CFG::CFGParser.new($stdout, @example_cfg)
14
+ parser.skip_over_cfg 'After code installation'
15
+ nmethod = parser.read_nmethod
16
+ expect(nmethod.code.arch).to eq 'AMD64'
17
+ expect(nmethod.code.arch_width).to eq '64'
18
+ expect(nmethod.code.base).to eq 0x1183037a0
19
+ expect(nmethod.comments.size).to eq 25
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ require 'seafoam'
2
+
3
+ require 'rspec'
4
+
5
+ require_relative '../spec_helpers'
6
+
7
+ describe Seafoam::CFG::Disassembler do
8
+ if Seafoam::SpecHelpers.dependencies_installed?
9
+ it 'can start Capstone Disassembler disassemble an nmethod with and without comments' do
10
+ @example_cfg = File.expand_path('../../../examples/java/exampleIf.cfg', __dir__)
11
+ @file = File.open('tempfile_disassembler_spec.txt', 'w')
12
+ parser = Seafoam::CFG::CFGParser.new(@file, @example_cfg)
13
+ parser.skip_over_cfg 'After code installation'
14
+ @nmethod = parser.read_nmethod
15
+
16
+ disassembler = Seafoam::CFG::Disassembler.new(@file)
17
+ disassembler.disassemble(@nmethod, 0)
18
+ @file.close
19
+
20
+ expect(`wc -l 'tempfile_disassembler_spec.txt'`.to_i).to eq 46
21
+
22
+ @file = File.open('tempfile_disassembler_spec.txt', 'w')
23
+ disassembler = Seafoam::CFG::Disassembler.new(@file)
24
+ disassembler.disassemble(@nmethod, 2)
25
+ @file.close
26
+
27
+ expect(`wc -l 'tempfile_disassembler_spec.txt'`.to_i).to eq 46
28
+
29
+ File.delete(@file)
30
+ end
31
+ end
32
+ end
@@ -8,6 +8,7 @@ require 'rspec'
8
8
  describe Seafoam::Commands do
9
9
  before :all do
10
10
  @fib_java = File.expand_path('../../examples/fib-java.bgv', __dir__)
11
+ @fib_ruby = File.expand_path('../../examples/fib-ruby.bgv', __dir__)
11
12
  end
12
13
 
13
14
  before :each do
@@ -19,7 +20,7 @@ describe Seafoam::Commands do
19
20
  it 'prints format and version' do
20
21
  @commands.send :info, @fib_java
21
22
  lines = @out.string.lines.map(&:rstrip)
22
- expect(lines.first).to eq 'BGV 6.1'
23
+ expect(lines.first).to eq 'BGV 7.0'
23
24
  end
24
25
 
25
26
  it 'does not work on a graph' do
@@ -32,18 +33,18 @@ describe Seafoam::Commands do
32
33
  @commands.send :list, @fib_java
33
34
  lines = @out.string.lines.map(&:rstrip)
34
35
  expect(lines.take(5)).to eq [
35
- "#{@fib_java}:0 2:Fib.fib(int)/After phase org.graalvm.compiler.java.GraphBuilderPhase",
36
- "#{@fib_java}:1 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.PhaseSuite",
37
- "#{@fib_java}:2 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.DeadCodeEliminationPhase",
38
- "#{@fib_java}:3 2:Fib.fib(int)/After parsing",
39
- "#{@fib_java}:4 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.CanonicalizerPhase"
36
+ "#{@fib_java}:0 17:Fib.fib(int)/After phase org.graalvm.compiler.java.GraphBuilderPhase",
37
+ "#{@fib_java}:1 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.PhaseSuite",
38
+ "#{@fib_java}:2 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.DeadCodeEliminationPhase",
39
+ "#{@fib_java}:3 17:Fib.fib(int)/After parsing",
40
+ "#{@fib_java}:4 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.CanonicalizerPhase"
40
41
  ]
41
42
  expect(lines.drop(lines.length - 5)).to eq [
42
- "#{@fib_java}:46 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase",
43
- "#{@fib_java}:47 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.InsertMembarsPhase",
44
- "#{@fib_java}:48 2:Fib.fib(int)/After phase org.graalvm.compiler.phases.schedule.SchedulePhase",
45
- "#{@fib_java}:49 2:Fib.fib(int)/After phase org.graalvm.compiler.core.phases.LowTier",
46
- "#{@fib_java}:50 2:Fib.fib(int)/After low tier"
43
+ "#{@fib_java}:51 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.DeadCodeEliminationPhase",
44
+ "#{@fib_java}:52 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.common.PropagateDeoptimizeProbabilityPhase",
45
+ "#{@fib_java}:53 17:Fib.fib(int)/After phase org.graalvm.compiler.phases.schedule.SchedulePhase",
46
+ "#{@fib_java}:54 17:Fib.fib(int)/After phase org.graalvm.compiler.core.phases.LowTier",
47
+ "#{@fib_java}:55 17:Fib.fib(int)/After low tier"
47
48
  ]
48
49
  end
49
50
 
@@ -95,7 +96,7 @@ describe Seafoam::Commands do
95
96
  it 'prints the number of edges and nodes for a graph' do
96
97
  @commands.send :edges, "#{@fib_java}:0"
97
98
  lines = @out.string.lines.map(&:rstrip)
98
- expect(lines.first).to eq '22 nodes, 30 edges'
99
+ expect(lines.first).to eq '21 nodes, 30 edges'
99
100
  end
100
101
 
101
102
  it 'prints the edges for a node' do
@@ -127,20 +128,18 @@ describe Seafoam::Commands do
127
128
 
128
129
  describe '#props' do
129
130
  it 'prints properties for a file' do
130
- @commands.send :props, @fib_java.to_s
131
- expect(@out.string.gsub(/\n\n/, "\n")).to eq "{\n}\n"
131
+ @commands.send :props, @fib_java
132
+ expect(@out.string.gsub(/\n\n/, "\n")).to eq "{\n \"vm.uuid\": \"94468\"\n}\n"
132
133
  end
133
134
 
134
135
  it 'prints properties for a graph' do
135
136
  @commands.send :props, "#{@fib_java}:0"
136
- lines = @out.string.lines.map(&:rstrip)
137
- expect(lines[3]).to include '"name": "2:Fib.fib(int)"'
137
+ expect(@out.string).to include '"scope": "main.Compiling.GraalCompiler.FrontEnd.PhaseSuite.GraphBuilderPhase"'
138
138
  end
139
139
 
140
140
  it 'prints properties for a node' do
141
141
  @commands.send :props, "#{@fib_java}:0:13"
142
- lines = @out.string.lines.map(&:rstrip)
143
- expect(lines[3]).to include '"declaring_class": "Fib"'
142
+ expect(@out.string).to include '"declaring_class": "Fib"'
144
143
  end
145
144
 
146
145
  it 'prints properties for an edge' do
@@ -150,6 +149,28 @@ describe Seafoam::Commands do
150
149
  end
151
150
  end
152
151
 
152
+ describe '#source' do
153
+ it 'prints source information for a node' do
154
+ @commands.send :source, "#{@fib_ruby}:8:2443"
155
+ expect(@out.string).to eq <<~SOURCE
156
+ java.lang.Math#addExact
157
+ org.truffleruby.core.numeric.IntegerNodes$AddNode#add
158
+ org.truffleruby.core.numeric.IntegerNodesFactory$AddNodeFactory$AddNodeGen#executeAdd
159
+ org.truffleruby.core.inlined.InlinedAddNode#intAdd
160
+ org.truffleruby.core.inlined.InlinedAddNodeGen#execute
161
+ org.truffleruby.language.control.IfElseNode#execute
162
+ org.truffleruby.language.control.SequenceNode#execute
163
+ org.truffleruby.language.arguments.CheckArityNode#execute
164
+ org.truffleruby.language.control.SequenceNode#execute
165
+ org.truffleruby.language.methods.CatchForMethodNode#execute
166
+ org.truffleruby.language.methods.ExceptionTranslatingNode#execute
167
+ org.truffleruby.language.RubyRootNode#execute
168
+ org.graalvm.compiler.truffle.runtime.OptimizedCallTarget#executeRootNode
169
+ org.graalvm.compiler.truffle.runtime.OptimizedCallTarget#profiledPERoot
170
+ SOURCE
171
+ end
172
+ end
173
+
153
174
  describe '#render' do
154
175
  it 'does not work on a file' do
155
176
  expect { @commands.send :render, @fib_java }.to raise_error(ArgumentError)
@@ -161,32 +182,57 @@ describe Seafoam::Commands do
161
182
  end
162
183
  end
163
184
 
164
- it 'supports -o out.pdf' do
165
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
166
- expect(`file out.pdf`).to start_with 'out.pdf: PDF document'
167
- end
185
+ if Seafoam::SpecHelpers.dependencies_installed?
186
+ it 'supports -o out.pdf' do
187
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
188
+ expect(`file out.pdf`).to start_with 'out.pdf: PDF document'
189
+ end
168
190
 
169
- it 'supports -o out.svg' do
170
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.svg'
171
- expect(`file out.svg`).to start_with 'out.svg: SVG Scalable Vector Graphics image'
172
- end
191
+ it 'supports -o out.svg' do
192
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.svg'
193
+ expect(`file out.svg`).to start_with 'out.svg: SVG Scalable Vector Graphics image'
194
+ end
173
195
 
174
- it 'supports -o out.png' do
175
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.png'
176
- expect(`file out.png`).to start_with 'out.png: PNG image data'
177
- end
196
+ it 'supports -o out.png' do
197
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.png'
198
+ expect(`file out.png`).to start_with 'out.png: PNG image data'
199
+ end
178
200
 
179
- it 'supports -o out.dot' do
180
- @commands.send :render, "#{@fib_java}:0", '--out', 'out.dot'
181
- expect(`file out.dot`).to start_with 'out.dot: ASCII text'
182
- end
201
+ it 'supports -o out.dot' do
202
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.dot'
203
+ expect(`file out.dot`).to start_with 'out.dot: ASCII text'
204
+ end
183
205
 
184
- it 'supports spotlighting nodes' do
185
- @commands.send :render, "#{@fib_java}:0", '--spotlight', '13'
206
+ it 'supports spotlighting nodes' do
207
+ @commands.send :render, "#{@fib_java}:0", '--spotlight', '13'
208
+ end
209
+
210
+ it 'does not work on a node' do
211
+ expect { @commands.send :render, "#{@fib_java}:0:13" }.to raise_error(ArgumentError)
212
+ end
213
+ else
214
+ it 'raises an exception if Graphviz is not installed' do
215
+ expect do
216
+ @commands.send :render, "#{@fib_java}:0", '--out', 'out.pdf'
217
+ end.to raise_error(RuntimeError, /Could not run Graphviz - is it installed?/)
218
+ end
186
219
  end
220
+ end
187
221
 
188
- it 'does not work on a node' do
189
- expect { @commands.send :render, "#{@fib_java}:0:13" }.to raise_error(ArgumentError)
222
+ describe '#cfg2asm' do
223
+ if Seafoam::SpecHelpers.dependencies_installed?
224
+ it 'prints format and version' do
225
+ @commands.cfg2asm(File.expand_path('../../examples/java/exampleWhile.cfg', __dir__), '--no-comments')
226
+ lines = @out.string.lines.map(&:rstrip)
227
+ expect(lines[1]).to include "0x117df59e0:\tnop dword ptr [rax + rax]"
228
+ expect(lines[-1]).to include "0x117df5a47:\thlt"
229
+ end
230
+ else
231
+ it 'raises an exception if Capstone is not installed' do
232
+ expect do
233
+ @commands.cfg2asm(File.expand_path('../../examples/java/exampleWhile.cfg', __dir__), '--no-comments')
234
+ end.to raise_error(RuntimeError, /Could not load Capstone - is it installed?/)
235
+ end
190
236
  end
191
237
  end
192
238
 
@@ -198,13 +244,13 @@ describe Seafoam::Commands do
198
244
 
199
245
  describe '#help' do
200
246
  it 'does not take any arguments' do
201
- expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
247
+ expect { @commands.send :seafoam, '--help', 'foo' }.to raise_error(ArgumentError)
202
248
  end
203
249
  end
204
250
 
205
251
  describe '#version' do
206
252
  it 'does not take any arguments' do
207
- expect { @commands.send :help, 'foo' }.to raise_error(ArgumentError)
253
+ expect { @commands.send :version, 'foo' }.to raise_error(ArgumentError)
208
254
  end
209
255
  end
210
256