seafoam 0.2
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 +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
|