seafoam 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/probots.yml +2 -0
- data/.github/workflows/rubocop.yml +10 -0
- data/.github/workflows/specs.yml +19 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +34 -0
- data/.ruby-version +1 -0
- data/.seafoam/config +1 -0
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +5 -0
- data/Gemfile +2 -0
- data/LICENSE.md +7 -0
- data/README.md +298 -0
- data/bin/bgv2isabelle +53 -0
- data/bin/bgv2json +42 -0
- data/bin/seafoam +24 -0
- data/docs/annotators.md +43 -0
- data/docs/bgv.md +284 -0
- data/docs/getting-graphs.md +47 -0
- data/examples/Fib.java +24 -0
- data/examples/MatMult.java +39 -0
- data/examples/fib-java.bgv +0 -0
- data/examples/fib-js.bgv +0 -0
- data/examples/fib-ruby.bgv +0 -0
- data/examples/fib.js +15 -0
- data/examples/fib.rb +15 -0
- data/examples/identity.bgv +0 -0
- data/examples/identity.rb +13 -0
- data/examples/java/Irreducible.j +35 -0
- data/examples/java/IrreducibleDecompiled.java +21 -0
- data/examples/java/JavaExamples.java +418 -0
- data/examples/java/exampleArithOperator.bgv +0 -0
- data/examples/java/exampleArithOperator.cfg +925 -0
- data/examples/java/exampleArrayAllocation.bgv +0 -0
- data/examples/java/exampleArrayAllocation.cfg +5268 -0
- data/examples/java/exampleArrayRead.bgv +0 -0
- data/examples/java/exampleArrayRead.cfg +2263 -0
- data/examples/java/exampleArrayWrite.bgv +0 -0
- data/examples/java/exampleArrayWrite.cfg +2315 -0
- data/examples/java/exampleCatch.bgv +0 -0
- data/examples/java/exampleCatch.cfg +4150 -0
- data/examples/java/exampleCompareOperator.bgv +0 -0
- data/examples/java/exampleCompareOperator.cfg +1109 -0
- data/examples/java/exampleDoubleSynchronized.bgv +0 -0
- data/examples/java/exampleDoubleSynchronized.cfg +26497 -0
- data/examples/java/exampleExactArith.bgv +0 -0
- data/examples/java/exampleExactArith.cfg +1888 -0
- data/examples/java/exampleFieldRead.bgv +0 -0
- data/examples/java/exampleFieldRead.cfg +1228 -0
- data/examples/java/exampleFieldWrite.bgv +0 -0
- data/examples/java/exampleFieldWrite.cfg +1102 -0
- data/examples/java/exampleFor.bgv +0 -0
- data/examples/java/exampleFor.cfg +3936 -0
- data/examples/java/exampleFullEscape.bgv +0 -0
- data/examples/java/exampleFullEscape.cfg +5893 -0
- data/examples/java/exampleIf.bgv +0 -0
- data/examples/java/exampleIf.cfg +2462 -0
- data/examples/java/exampleIfNeverTaken.bgv +0 -0
- data/examples/java/exampleIfNeverTaken.cfg +2476 -0
- data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
- data/examples/java/exampleInstanceOfManyImpls.cfg +6391 -0
- data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
- data/examples/java/exampleInstanceOfOneImpl.cfg +2604 -0
- data/examples/java/exampleIntSwitch.bgv +0 -0
- data/examples/java/exampleIntSwitch.cfg +3121 -0
- data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
- data/examples/java/exampleInterfaceCallManyImpls.cfg +1358 -0
- data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
- data/examples/java/exampleInterfaceCallOneImpl.cfg +3859 -0
- data/examples/java/exampleLocalInstanceOf.bgv +0 -0
- data/examples/java/exampleLocalInstanceOf.cfg +5276 -0
- data/examples/java/exampleLocalSynchronized.bgv +0 -0
- data/examples/java/exampleLocalSynchronized.cfg +1364 -0
- data/examples/java/exampleLocalVariables.bgv +0 -0
- data/examples/java/exampleLocalVariables.cfg +1195 -0
- data/examples/java/exampleLocalVariablesState.bgv +0 -0
- data/examples/java/exampleLocalVariablesState.cfg +1673 -0
- data/examples/java/exampleNestedWhile.bgv +0 -0
- data/examples/java/exampleNestedWhile.cfg +15499 -0
- data/examples/java/exampleNestedWhileBreak.bgv +0 -0
- data/examples/java/exampleNestedWhileBreak.cfg +11162 -0
- data/examples/java/exampleNoEscape.bgv +0 -0
- data/examples/java/exampleNoEscape.cfg +974 -0
- data/examples/java/exampleObjectAllocation.bgv +0 -0
- data/examples/java/exampleObjectAllocation.cfg +5287 -0
- data/examples/java/examplePartialEscape.bgv +0 -0
- data/examples/java/examplePartialEscape.cfg +7042 -0
- data/examples/java/examplePhi.bgv +0 -0
- data/examples/java/examplePhi.cfg +3227 -0
- data/examples/java/exampleReducible.bgv +0 -0
- data/examples/java/exampleReducible.cfg +5578 -0
- data/examples/java/exampleSimpleCall.bgv +0 -0
- data/examples/java/exampleSimpleCall.cfg +1435 -0
- data/examples/java/exampleStamp.bgv +0 -0
- data/examples/java/exampleStamp.cfg +913 -0
- data/examples/java/exampleStaticCall.bgv +0 -0
- data/examples/java/exampleStaticCall.cfg +1154 -0
- data/examples/java/exampleStringSwitch.bgv +0 -0
- data/examples/java/exampleStringSwitch.cfg +15377 -0
- data/examples/java/exampleSynchronized.bgv +0 -0
- data/examples/java/exampleSynchronized.cfg +26027 -0
- data/examples/java/exampleThrow.bgv +0 -0
- data/examples/java/exampleThrow.cfg +780 -0
- data/examples/java/exampleThrowCatch.bgv +0 -0
- data/examples/java/exampleThrowCatch.cfg +744 -0
- data/examples/java/exampleUnsafeRead.bgv +0 -0
- data/examples/java/exampleUnsafeRead.cfg +912 -0
- data/examples/java/exampleUnsafeWrite.bgv +0 -0
- data/examples/java/exampleUnsafeWrite.cfg +962 -0
- data/examples/java/exampleWhile.bgv +0 -0
- data/examples/java/exampleWhile.cfg +3936 -0
- data/examples/java/exampleWhileBreak.bgv +0 -0
- data/examples/java/exampleWhileBreak.cfg +5963 -0
- data/examples/matmult-java.bgv +0 -0
- data/examples/matmult-ruby.bgv +0 -0
- data/examples/matmult.rb +29 -0
- data/examples/overflow.bgv +0 -0
- data/examples/overflow.rb +13 -0
- data/lib/seafoam.rb +13 -0
- data/lib/seafoam/annotators.rb +54 -0
- data/lib/seafoam/annotators/fallback.rb +27 -0
- data/lib/seafoam/annotators/graal.rb +376 -0
- data/lib/seafoam/bgv/bgv_parser.rb +602 -0
- data/lib/seafoam/binary/binary_reader.rb +21 -0
- data/lib/seafoam/binary/io_binary_reader.rb +88 -0
- data/lib/seafoam/colors.rb +18 -0
- data/lib/seafoam/commands.rb +447 -0
- data/lib/seafoam/config.rb +34 -0
- data/lib/seafoam/graph.rb +91 -0
- data/lib/seafoam/graphviz_writer.rb +213 -0
- data/lib/seafoam/spotlight.rb +28 -0
- data/lib/seafoam/version.rb +5 -0
- data/seafoam.gemspec +20 -0
- data/spec/seafoam/annotators/fallback_spec.rb +69 -0
- data/spec/seafoam/annotators/graal_spec.rb +96 -0
- data/spec/seafoam/annotators_spec.rb +61 -0
- data/spec/seafoam/bgv/bgv_parser_spec.rb +144 -0
- data/spec/seafoam/bgv/fixtures/not.bgv +1 -0
- data/spec/seafoam/bgv/fixtures/unsupported.bgv +1 -0
- data/spec/seafoam/binary/io_binary_reader_spec.rb +176 -0
- data/spec/seafoam/command_spec.rb +252 -0
- data/spec/seafoam/graph_spec.rb +172 -0
- data/spec/seafoam/graphviz_writer_spec.rb +63 -0
- data/spec/seafoam/spec_helpers.rb +30 -0
- data/spec/seafoam/spotlight_spec.rb +38 -0
- data/tools/render-all +36 -0
- metadata +238 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Seafoam
|
2
|
+
# Finds and loads configuration.
|
3
|
+
class Config
|
4
|
+
def initialize
|
5
|
+
@dot_dir = find_dot_dir
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load the configuration.
|
9
|
+
def load_config
|
10
|
+
config_file = File.expand_path('config', @dot_dir)
|
11
|
+
if File.exist?(config_file)
|
12
|
+
puts "loading config #{config_file}" if $DEBUG
|
13
|
+
load config_file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# Walk up the directory chain from the current directory to root, looking
|
20
|
+
# for .seafoam.
|
21
|
+
def find_dot_dir
|
22
|
+
dir = Dir.getwd
|
23
|
+
loop do
|
24
|
+
dot_dir = File.expand_path('.seafoam', dir)
|
25
|
+
return dot_dir if Dir.exist?(dot_dir)
|
26
|
+
|
27
|
+
new_dir = File.expand_path('..', dir)
|
28
|
+
break if new_dir == dir
|
29
|
+
|
30
|
+
dir = new_dir
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Seafoam
|
2
|
+
# A graph, with properties, nodes, and edges. We don't encapsulate the graph
|
3
|
+
# too much - be careful.
|
4
|
+
class Graph
|
5
|
+
attr_reader :props, :nodes, :edges
|
6
|
+
|
7
|
+
def initialize(props = nil)
|
8
|
+
@props = props || {}
|
9
|
+
@nodes = {}
|
10
|
+
@edges = []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create a node.
|
14
|
+
def create_node(id, props = nil)
|
15
|
+
props ||= {}
|
16
|
+
node = Node.new(id, props)
|
17
|
+
@nodes[id] = node
|
18
|
+
node
|
19
|
+
end
|
20
|
+
|
21
|
+
# Create an edge between two nodes.
|
22
|
+
def create_edge(from, to, props = nil)
|
23
|
+
props ||= {}
|
24
|
+
edge = Edge.new(from, to, props)
|
25
|
+
@edges.push edge
|
26
|
+
from.outputs.push edge
|
27
|
+
to.inputs.push edge
|
28
|
+
edge
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# A node, with properties, input edges, and output edges.
|
33
|
+
class Node
|
34
|
+
attr_reader :id, :inputs, :outputs, :props
|
35
|
+
|
36
|
+
def initialize(id, props = nil)
|
37
|
+
props ||= {}
|
38
|
+
@id = id
|
39
|
+
@inputs = []
|
40
|
+
@outputs = []
|
41
|
+
@props = props
|
42
|
+
end
|
43
|
+
|
44
|
+
# All edges - input and output.
|
45
|
+
def edges
|
46
|
+
inputs + outputs
|
47
|
+
end
|
48
|
+
|
49
|
+
# All adjacent nodes - from input and output edges.
|
50
|
+
def adjacent
|
51
|
+
(inputs.map(&:from) + outputs.map(&:to)).uniq
|
52
|
+
end
|
53
|
+
|
54
|
+
# id (label)
|
55
|
+
def id_and_label
|
56
|
+
if props[:label]
|
57
|
+
"#{id} (#{props[:label]})"
|
58
|
+
else
|
59
|
+
id.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Inspect.
|
64
|
+
def inspect
|
65
|
+
"<Node #{id}>"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# A directed edge, with a node it's from and a node it's going to, and
|
70
|
+
# properties.
|
71
|
+
class Edge
|
72
|
+
attr_reader :from, :to, :props
|
73
|
+
|
74
|
+
def initialize(from, to, props = nil)
|
75
|
+
props ||= {}
|
76
|
+
@from = from
|
77
|
+
@to = to
|
78
|
+
@props = props
|
79
|
+
end
|
80
|
+
|
81
|
+
# Both nodes - from and to.
|
82
|
+
def nodes
|
83
|
+
[@from, @to]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Inspect.
|
87
|
+
def inspect
|
88
|
+
"<Edge #{from.id} -> #{to.id}>"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Seafoam
|
2
|
+
# A writer from graphs to the Graphviz DOT format, including all the
|
3
|
+
# formatting.
|
4
|
+
class GraphvizWriter
|
5
|
+
def initialize(stream)
|
6
|
+
@stream = stream
|
7
|
+
end
|
8
|
+
|
9
|
+
# Write a graph.
|
10
|
+
def write_graph(graph, hidpi = false)
|
11
|
+
inline_attrs = {}
|
12
|
+
attrs = {}
|
13
|
+
attrs[:dpi] = 200 if hidpi
|
14
|
+
attrs[:bgcolor] = 'transparent'
|
15
|
+
@stream.puts 'digraph G {'
|
16
|
+
@stream.puts " graph #{write_attrs(attrs)};"
|
17
|
+
write_nodes inline_attrs, graph
|
18
|
+
write_edges inline_attrs, graph
|
19
|
+
@stream.puts '}'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Write node declarations.
|
25
|
+
def write_nodes(inline_attrs, graph)
|
26
|
+
graph.nodes.each_value do |node|
|
27
|
+
write_node inline_attrs, node
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_node(inline_attrs, node)
|
32
|
+
# We're going to build up a hash of Graphviz drawing attributes.
|
33
|
+
attrs = {}
|
34
|
+
|
35
|
+
# The node is hidden, and it's not going to be inlined above any
|
36
|
+
# other node.
|
37
|
+
if node.props[:hidden] && !node.props[:inlined]
|
38
|
+
# If the node has any adjacent nodes that are not hidden, and are
|
39
|
+
# shaded, then we need to declare the node but make it invisible so
|
40
|
+
# the edge appears, pointing off into space, but the node does not.
|
41
|
+
if node.adjacent.any? { |a| !a.props[:hidden] && a.props[:spotlight] == 'shaded' }
|
42
|
+
attrs[:style] = 'invis'
|
43
|
+
attrs[:label] = ''
|
44
|
+
@stream.puts " node#{node.id} #{write_attrs(attrs)};"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
# This is a visible node.
|
48
|
+
|
49
|
+
# Give it a label.
|
50
|
+
if node.props[:label]
|
51
|
+
attrs[:label] = "#{node.id} #{node.props[:label]}"
|
52
|
+
else
|
53
|
+
# If we really still don't have a label, just use the ID.
|
54
|
+
attrs[:label] = node.id.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
# Basic attributes for a node.
|
58
|
+
attrs[:shape] = 'rectangle'
|
59
|
+
attrs[:fontname] = 'Arial'
|
60
|
+
attrs[:style] = 'filled'
|
61
|
+
attrs[:color] = 'black'
|
62
|
+
|
63
|
+
# Color depends on the kind of node.
|
64
|
+
back_color, fore_color = NODE_COLORS[node.props[:kind]]
|
65
|
+
attrs[:fillcolor] = back_color
|
66
|
+
attrs[:fontcolor] = fore_color
|
67
|
+
|
68
|
+
if node.props[:inlined]
|
69
|
+
# If the node is to be inlined then draw it smaller and a different
|
70
|
+
# shape.
|
71
|
+
attrs[:shape] = 'oval'
|
72
|
+
attrs[:fontsize] = '8'
|
73
|
+
|
74
|
+
# Just record these attributes for where it's used by other nodes
|
75
|
+
# so it can be drawn above them - don't actually declare a node.
|
76
|
+
inline_attrs[node.id] = attrs
|
77
|
+
else
|
78
|
+
attrs[:shape] = 'diamond' if node.props[:kind] == 'calc'
|
79
|
+
|
80
|
+
# If the node is shaded, convert the attributes to the shaded
|
81
|
+
# version.
|
82
|
+
attrs = shade(attrs) if node.props[:spotlight] == 'shaded'
|
83
|
+
|
84
|
+
# Declare the node.
|
85
|
+
@stream.puts " node#{node.id} #{write_attrs(attrs)};"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Write edge declarations.
|
91
|
+
|
92
|
+
def write_edges(inline_attrs, graph)
|
93
|
+
graph.edges.each do |edge|
|
94
|
+
# Skip the edge if it's from a node that is hidden and it doesn't point
|
95
|
+
# to a shaded node.
|
96
|
+
next if edge.from.props[:hidden] && edge.to.props[:spotlight] != 'shaded'
|
97
|
+
|
98
|
+
# Skip the edge if it's to a node that is hidden and it doesn't come
|
99
|
+
# from a shaded node.
|
100
|
+
next if edge.to.props[:hidden] && edge.from.props[:spotlight] != 'shaded'
|
101
|
+
|
102
|
+
write_edge inline_attrs, edge
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def write_edge(inline_attrs, edge)
|
107
|
+
# We're going to build up a hash of Graphviz drawing attributes.
|
108
|
+
attrs = {}
|
109
|
+
|
110
|
+
label = edge.props[:label]
|
111
|
+
if edge.from.props[:out_annotation]
|
112
|
+
label = "#{label} #{edge.from.props[:out_annotation]}"
|
113
|
+
end
|
114
|
+
attrs[:label] = label
|
115
|
+
|
116
|
+
# Basic edge attributes.
|
117
|
+
attrs[:fontname] = 'arial'
|
118
|
+
color = EDGE_COLORS[edge.props[:kind]]
|
119
|
+
attrs[:color] = EDGE_COLORS[edge.props[:kind]]
|
120
|
+
attrs[:fontcolor] = color
|
121
|
+
|
122
|
+
# Properties depending on the kind of edge.
|
123
|
+
case edge.props[:kind]
|
124
|
+
when 'control'
|
125
|
+
attrs[:penwidth] = 2
|
126
|
+
when 'loop'
|
127
|
+
attrs[:penwidth] = 4
|
128
|
+
when 'info'
|
129
|
+
attrs[:style] = 'dashed'
|
130
|
+
end
|
131
|
+
|
132
|
+
# Reversed edges.
|
133
|
+
attrs[:dir] = 'back' if edge.props[:reverse]
|
134
|
+
|
135
|
+
# Convert attributes to shaded if any edges involved are shaded.
|
136
|
+
attrs = shade(attrs) if edge.nodes.any? { |n| n.props[:spotlight] == 'shaded' }
|
137
|
+
|
138
|
+
# Does this edge come from an inlined node?
|
139
|
+
|
140
|
+
if edge.from.props[:inlined]
|
141
|
+
# An inlined edge is drawn as a new version of the from-node and an
|
142
|
+
# edge from that new version directly to the to-node. With only one
|
143
|
+
# user it's a short edge and the from-node is show directly above the
|
144
|
+
# to-node.
|
145
|
+
|
146
|
+
if edge.to.props[:spotlight] == 'shaded'
|
147
|
+
# Draw inlined edges to shaded nodes as invisible.
|
148
|
+
node_attrs = { label: '', style: 'invis' }
|
149
|
+
else
|
150
|
+
# Get attributes from when we went through nodes earlier.
|
151
|
+
node_attrs = inline_attrs[edge.from.id]
|
152
|
+
end
|
153
|
+
|
154
|
+
# Inlined nodes skip the arrow for simplicity.
|
155
|
+
attrs[:arrowhead] = 'none'
|
156
|
+
attrs[:fontsize] = '8'
|
157
|
+
|
158
|
+
# Declare a new node just for this user.
|
159
|
+
@stream.puts " inline#{edge.from.id}x#{edge.to.id} #{write_attrs(node_attrs)};"
|
160
|
+
|
161
|
+
# Declare the edge.
|
162
|
+
@stream.puts " inline#{edge.from.id}x#{edge.to.id} -> node#{edge.to.id} #{write_attrs(attrs)};"
|
163
|
+
else
|
164
|
+
# Declare the edge.
|
165
|
+
@stream.puts " node#{edge.from.id} -> node#{edge.to.id} #{write_attrs(attrs)};"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Return attributes for a node or edge modified to 'shade' them in terms
|
170
|
+
# the spotlight functionality - so basically make them light grey.
|
171
|
+
def shade(attrs)
|
172
|
+
attrs = attrs.dup
|
173
|
+
attrs[:color] = ICE_STONE
|
174
|
+
attrs[:fontcolor] = ICE_STONE
|
175
|
+
attrs[:fillcolor] = DUST
|
176
|
+
attrs
|
177
|
+
end
|
178
|
+
|
179
|
+
# Write a hash of key-value attributes into the DOT format.
|
180
|
+
def write_attrs(attrs)
|
181
|
+
'[' + attrs.reject { |_k, v| v.nil? }.map { |k, v| "#{k}=#{quote(v)}" }.join(',') + ']'
|
182
|
+
end
|
183
|
+
|
184
|
+
# Quote and escape a string.
|
185
|
+
def quote(string)
|
186
|
+
string = string.to_s
|
187
|
+
string = string.gsub('\\', '\\\\')
|
188
|
+
string = string.gsub('"', '\\"')
|
189
|
+
"\"#{string}\""
|
190
|
+
end
|
191
|
+
|
192
|
+
# Color theme.
|
193
|
+
|
194
|
+
EDGE_COLORS = {
|
195
|
+
'info' => BIG_STONE,
|
196
|
+
'control' => AMARANTH,
|
197
|
+
'loop' => AMARANTH,
|
198
|
+
'data' => KEPPEL,
|
199
|
+
'other' => BLACK
|
200
|
+
}
|
201
|
+
|
202
|
+
NODE_COLORS = {
|
203
|
+
'info' => [DUST, BLACK],
|
204
|
+
'input' => [WHITE_ICE, BLACK],
|
205
|
+
'control' => [CARISSMA, BLACK],
|
206
|
+
'effect' => [AMARANTH, WHITE],
|
207
|
+
'virtual' => [BIG_STONE, WHITE],
|
208
|
+
'guard' => [ORANGE, BLACK],
|
209
|
+
'calc' => [KEPPEL, BLACK],
|
210
|
+
'other' => [DUST, BLACK]
|
211
|
+
}
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Seafoam
|
2
|
+
# Spotlight can *light* nodes, which makes them visible, their adjacent nodes
|
3
|
+
# visible by grey, and other nodes invisible. Multiple nodes can be *lit*.
|
4
|
+
class Spotlight
|
5
|
+
def initialize(graph)
|
6
|
+
@graph = graph
|
7
|
+
end
|
8
|
+
|
9
|
+
# Mark a node as lit by the spotlight.
|
10
|
+
def light(node)
|
11
|
+
# This node is lit.
|
12
|
+
node.props[:spotlight] = 'lit'
|
13
|
+
|
14
|
+
# Adjacent nodes are shaded, if they haven't be lit themselvs.
|
15
|
+
node.adjacent.each do |adjacent|
|
16
|
+
adjacent.props[:spotlight] ||= 'shaded'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Go through all other nodes and make them hidden, having lit as many nodes
|
21
|
+
# as you want.
|
22
|
+
def shade
|
23
|
+
@graph.nodes.each_value do |node|
|
24
|
+
node.props[:hidden] = true unless node.props[:spotlight]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/seafoam.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'lib/seafoam/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'seafoam'
|
5
|
+
spec.version = Seafoam::VERSION
|
6
|
+
spec.summary = 'A tool for working with compiler graphs'
|
7
|
+
spec.authors = ['Chris Seaton']
|
8
|
+
spec.homepage = 'https://github.com/Shopify/seafoam'
|
9
|
+
spec.license = 'MIT'
|
10
|
+
|
11
|
+
spec.files = `git ls-files`.split("\n")
|
12
|
+
spec.bindir = 'bin'
|
13
|
+
spec.executables = %w[seafoam bgv2json bgv2isabelle]
|
14
|
+
|
15
|
+
spec.required_ruby_version = '>= 2.5.8'
|
16
|
+
|
17
|
+
spec.add_development_dependency 'benchmark-ips', '~> 2.7'
|
18
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
19
|
+
spec.add_development_dependency 'rubocop', '~> 0.74'
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'seafoam'
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
describe Seafoam::Annotators::FallbackAnnotator do
|
6
|
+
it 'always applies' do
|
7
|
+
expect(Seafoam::Annotators::FallbackAnnotator.applies?(Seafoam::Graph.new)).to be true
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'adds a label annotation when there is a label property' do
|
11
|
+
graph = Seafoam::Graph.new
|
12
|
+
node = graph.create_node(0, 'label' => 'foo')
|
13
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
14
|
+
annotator.annotate graph
|
15
|
+
expect(node.props[:label]).to eq 'foo'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'does not overwrite an existing label annotation' do
|
19
|
+
graph = Seafoam::Graph.new
|
20
|
+
node = graph.create_node(0, 'label' => 'foo', :label => 'bar')
|
21
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
22
|
+
annotator.annotate graph
|
23
|
+
expect(node.props[:label]).to eq 'bar'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'adds nothing when there is no label property' do
|
27
|
+
graph = Seafoam::Graph.new
|
28
|
+
node = graph.create_node(0, 'xlabel' => 'foo')
|
29
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
30
|
+
annotator.annotate graph
|
31
|
+
expect(node.props[:label]).to be nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets node kind to other when there is no kind' do
|
35
|
+
graph = Seafoam::Graph.new
|
36
|
+
node = graph.create_node(0)
|
37
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
38
|
+
annotator.annotate graph
|
39
|
+
expect(node.props[:kind]).to eq 'other'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not overwrite an existing node kind annotations' do
|
43
|
+
graph = Seafoam::Graph.new
|
44
|
+
node = graph.create_node(0, kind: 'control')
|
45
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
46
|
+
annotator.annotate graph
|
47
|
+
expect(node.props[:kind]).to eq 'control'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'sets edge kind to other when there is no kind' do
|
51
|
+
graph = Seafoam::Graph.new
|
52
|
+
node_a = graph.create_node(0)
|
53
|
+
node_b = graph.create_node(1)
|
54
|
+
edge = graph.create_edge(node_a, node_b)
|
55
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
56
|
+
annotator.annotate graph
|
57
|
+
expect(edge.props[:kind]).to eq 'other'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'does not overwrite an existing edge kind annotations' do
|
61
|
+
graph = Seafoam::Graph.new
|
62
|
+
node_a = graph.create_node(0)
|
63
|
+
node_b = graph.create_node(1)
|
64
|
+
edge = graph.create_edge(node_a, node_b, kind: 'control')
|
65
|
+
annotator = Seafoam::Annotators::FallbackAnnotator.new
|
66
|
+
annotator.annotate graph
|
67
|
+
expect(edge.props[:kind]).to eq 'control'
|
68
|
+
end
|
69
|
+
end
|