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,602 @@
|
|
|
1
|
+
module Seafoam
|
|
2
|
+
module BGV
|
|
3
|
+
# A parser for BGV files. It's a push-pull streaming interface that you need
|
|
4
|
+
# to drive with what you want next from the file. It's slightly complicated
|
|
5
|
+
# and some code is duplicated in order to support skipping over parts of the
|
|
6
|
+
# file that you don't need.
|
|
7
|
+
class BGVParser
|
|
8
|
+
def initialize(source)
|
|
9
|
+
@reader = Binary::BinaryReader.for(source)
|
|
10
|
+
@group_stack = []
|
|
11
|
+
@pool = {}
|
|
12
|
+
@index = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Read the file header and return the version.
|
|
16
|
+
def read_file_header(version_check: true)
|
|
17
|
+
raise EncodingError, 'does not appear to be a BGV file - missing header' unless @reader.read_bytes(4) == MAGIC
|
|
18
|
+
|
|
19
|
+
@major = @reader.read_sint8
|
|
20
|
+
@minor = @reader.read_sint8
|
|
21
|
+
version = [@major, @minor]
|
|
22
|
+
if version_check && !SUPPORTED_VERSIONS.include?(version)
|
|
23
|
+
raise NotImplementedError, "unsupported BGV version #{@major}.#{@minor}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
version
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def read_document_props
|
|
30
|
+
if @major >= 7
|
|
31
|
+
token = @reader.peek_sint8
|
|
32
|
+
if token == BEGIN_DOCUMENT
|
|
33
|
+
@reader.skip_int8
|
|
34
|
+
document_props = read_props
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
document_props
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def skip_document_props
|
|
41
|
+
if @major >= 7
|
|
42
|
+
token = @reader.peek_sint8
|
|
43
|
+
if token == BEGIN_DOCUMENT
|
|
44
|
+
@reader.skip_int8
|
|
45
|
+
skip_props
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Move to the next graph in the file, and return its index and ID, or nil if
|
|
51
|
+
# there are no more graphs.
|
|
52
|
+
def read_graph_preheader
|
|
53
|
+
return nil unless read_groups
|
|
54
|
+
|
|
55
|
+
# Already read BEGIN_GRAPH
|
|
56
|
+
index = @index
|
|
57
|
+
id = @reader.read_sint32
|
|
58
|
+
if id
|
|
59
|
+
@index += 1
|
|
60
|
+
[index, id]
|
|
61
|
+
else
|
|
62
|
+
[nil, nil]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Skip over a graph's headers, having just read its ID.
|
|
67
|
+
def skip_graph_header
|
|
68
|
+
# Already read BEGIN_GRAPH and id
|
|
69
|
+
skip_string
|
|
70
|
+
skip_args
|
|
71
|
+
@graph_props = read_props
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Read a graph's headers, having just read its ID. This gives you the
|
|
75
|
+
# graph's properties.
|
|
76
|
+
def read_graph_header
|
|
77
|
+
# Already read BEGIN_GRAPH and id
|
|
78
|
+
format = read_string
|
|
79
|
+
args = read_args
|
|
80
|
+
props = read_props
|
|
81
|
+
@graph_props = props
|
|
82
|
+
{
|
|
83
|
+
group: @group_stack.dup,
|
|
84
|
+
format: format,
|
|
85
|
+
args: args,
|
|
86
|
+
props: props
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Read a graph having either read or skipped its headers, producing a Graph object.
|
|
91
|
+
def read_graph
|
|
92
|
+
# Already read BEGIN_GRAPH, id, format, args, and props
|
|
93
|
+
graph = Graph.new(@graph_props)
|
|
94
|
+
edge_delay = []
|
|
95
|
+
@reader.read_sint32.times do
|
|
96
|
+
id = @reader.read_sint32
|
|
97
|
+
node_class = read_pool_object
|
|
98
|
+
has_predecessor = read_bool
|
|
99
|
+
props = read_props
|
|
100
|
+
props[:id] = id
|
|
101
|
+
props[:node_class] = node_class
|
|
102
|
+
props[:has_predecessor] = has_predecessor
|
|
103
|
+
node = graph.create_node(id, props)
|
|
104
|
+
edge_delay.push(*read_edges(node, node_class, true))
|
|
105
|
+
edge_delay.push(*read_edges(node, node_class, false))
|
|
106
|
+
end
|
|
107
|
+
edge_delay.each do |edge|
|
|
108
|
+
node = edge[:node]
|
|
109
|
+
props = edge[:edge]
|
|
110
|
+
inputs = edge[:inputs]
|
|
111
|
+
others = edge[:ids].reject(&:nil?).map { |id| graph.nodes[id] || raise(EncodingError, "BGV edge with unknown node #{id}") }
|
|
112
|
+
others.each_with_index do |other, index|
|
|
113
|
+
# We need to give each edge their own property as they're annotated separately.
|
|
114
|
+
props = props.dup
|
|
115
|
+
props[:index] = index
|
|
116
|
+
if inputs
|
|
117
|
+
graph.create_edge other, node, props
|
|
118
|
+
else
|
|
119
|
+
graph.create_edge node, other, props
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
skip_blocks
|
|
124
|
+
graph
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Skip over a graph, having read or skipped its headers.
|
|
128
|
+
def skip_graph
|
|
129
|
+
# Already read BEGIN_GRAPH, id, format, args, and props
|
|
130
|
+
@reader.read_sint32.times do
|
|
131
|
+
@reader.skip_int32
|
|
132
|
+
node_class = read_pool_object
|
|
133
|
+
skip_bool
|
|
134
|
+
skip_props
|
|
135
|
+
skip_edges node_class, true
|
|
136
|
+
skip_edges node_class, false
|
|
137
|
+
end
|
|
138
|
+
skip_blocks
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Produce a flat graph name from a header.
|
|
142
|
+
def graph_name(graph_header)
|
|
143
|
+
groups_names = graph_header[:group].map { |g| g[:short_name] }
|
|
144
|
+
count = 0
|
|
145
|
+
name = graph_header[:format].sub(/%s/) do
|
|
146
|
+
arg = graph_header[:args][count]
|
|
147
|
+
count += 1
|
|
148
|
+
arg
|
|
149
|
+
end
|
|
150
|
+
components = groups_names + [name]
|
|
151
|
+
components.join('/')
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
# Read through group declarations to get to the start of the next graph.
|
|
157
|
+
def read_groups
|
|
158
|
+
until @reader.eof?
|
|
159
|
+
token = @reader.read_sint8
|
|
160
|
+
case token
|
|
161
|
+
when BEGIN_GROUP
|
|
162
|
+
read_begin_group
|
|
163
|
+
when BEGIN_GRAPH
|
|
164
|
+
break true
|
|
165
|
+
when CLOSE_GROUP
|
|
166
|
+
read_close_group
|
|
167
|
+
else
|
|
168
|
+
raise EncodingError, "unknown token 0x#{token.to_s(16)} beginning BGV object"
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Read the opening of a group.
|
|
174
|
+
def read_begin_group
|
|
175
|
+
# Already read BEGIN_GROUP
|
|
176
|
+
name = read_pool_object
|
|
177
|
+
short_name = read_pool_object
|
|
178
|
+
method = read_pool_object
|
|
179
|
+
bci = @reader.read_sint32
|
|
180
|
+
props = read_props
|
|
181
|
+
group = {
|
|
182
|
+
name: name,
|
|
183
|
+
short_name: short_name,
|
|
184
|
+
method: method,
|
|
185
|
+
bci: bci,
|
|
186
|
+
props: props
|
|
187
|
+
}
|
|
188
|
+
@group_stack.push group
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Read the closing of a group.
|
|
192
|
+
def read_close_group
|
|
193
|
+
# Already read CLOSE_GROUP
|
|
194
|
+
@group_stack.pop
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Skip over arguments.
|
|
198
|
+
def skip_args
|
|
199
|
+
@reader.read_sint32.times do
|
|
200
|
+
skip_prop_object
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Read arguments.
|
|
205
|
+
def read_args
|
|
206
|
+
@reader.read_sint32.times.map do
|
|
207
|
+
read_prop_object
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Skip over edges.
|
|
212
|
+
def skip_edges(node_class, inputs)
|
|
213
|
+
edges = if inputs
|
|
214
|
+
node_class[:inputs]
|
|
215
|
+
else
|
|
216
|
+
node_class[:outputs]
|
|
217
|
+
end
|
|
218
|
+
edges.each do |edge|
|
|
219
|
+
count = if edge[:direct]
|
|
220
|
+
1
|
|
221
|
+
else
|
|
222
|
+
@reader.read_sint16
|
|
223
|
+
end
|
|
224
|
+
count.times do
|
|
225
|
+
@reader.skip_int32
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Read edges, producing an array of edge hashes.
|
|
231
|
+
def read_edges(node, node_class, inputs)
|
|
232
|
+
edges = if inputs
|
|
233
|
+
node_class[:inputs]
|
|
234
|
+
else
|
|
235
|
+
node_class[:outputs]
|
|
236
|
+
end
|
|
237
|
+
edges.map do |edge|
|
|
238
|
+
count = if edge[:direct]
|
|
239
|
+
1
|
|
240
|
+
else
|
|
241
|
+
@reader.read_sint16
|
|
242
|
+
end
|
|
243
|
+
ids = count.times.map do
|
|
244
|
+
id = @reader.read_sint32
|
|
245
|
+
raise if id < -1
|
|
246
|
+
|
|
247
|
+
id = nil if id == -1
|
|
248
|
+
id
|
|
249
|
+
end
|
|
250
|
+
{
|
|
251
|
+
node: node,
|
|
252
|
+
edge: edge,
|
|
253
|
+
ids: ids,
|
|
254
|
+
inputs: inputs
|
|
255
|
+
}
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Skip over blocks in a graph.
|
|
260
|
+
def skip_blocks
|
|
261
|
+
@reader.read_sint32.times do
|
|
262
|
+
@reader.skip_int32
|
|
263
|
+
@reader.skip_int32 @reader.read_sint32
|
|
264
|
+
@reader.skip_int32 @reader.read_sint32
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Skip over a set of properties.
|
|
269
|
+
def skip_props
|
|
270
|
+
@reader.read_sint16.times do
|
|
271
|
+
skip_pool_object
|
|
272
|
+
skip_prop_object
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Read a set of properties, producing a Hash.
|
|
277
|
+
def read_props
|
|
278
|
+
@reader.read_sint16.times.map do
|
|
279
|
+
key = read_pool_object
|
|
280
|
+
value = read_prop_object
|
|
281
|
+
[key, value]
|
|
282
|
+
end.to_h
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Skip over a single property value.
|
|
286
|
+
def skip_prop_object
|
|
287
|
+
token = @reader.read_sint8
|
|
288
|
+
case token
|
|
289
|
+
when PROPERTY_POOL
|
|
290
|
+
skip_pool_object
|
|
291
|
+
when PROPERTY_INT
|
|
292
|
+
@reader.skip_int32
|
|
293
|
+
when PROPERTY_LONG
|
|
294
|
+
@reader.skip_int64
|
|
295
|
+
when PROPERTY_DOUBLE
|
|
296
|
+
@reader.skip_float64
|
|
297
|
+
when PROPERTY_FLOAT
|
|
298
|
+
@reader.skip_float32
|
|
299
|
+
when PROPERTY_TRUE
|
|
300
|
+
when PROPERTY_FALSE
|
|
301
|
+
when PROPERTY_ARRAY
|
|
302
|
+
type = @reader.read_sint8
|
|
303
|
+
case type
|
|
304
|
+
when PROPERTY_POOL
|
|
305
|
+
@reader.read_sint32.times do
|
|
306
|
+
skip_pool_object
|
|
307
|
+
end
|
|
308
|
+
when PROPERTY_INT
|
|
309
|
+
@reader.skip_int32 @reader.read_sint32
|
|
310
|
+
when PROPERTY_DOUBLE
|
|
311
|
+
@reader.skip_float64 @reader.read_sint32
|
|
312
|
+
else
|
|
313
|
+
raise EncodingError, "unknown BGV property array type 0x#{type.to_s(16)}"
|
|
314
|
+
end
|
|
315
|
+
when PROPERTY_SUBGRAPH
|
|
316
|
+
skip_props
|
|
317
|
+
skip_graph
|
|
318
|
+
else
|
|
319
|
+
raise EncodingError, "unknown BGV property 0x#{token.to_s(16)}"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Read a single property value.
|
|
324
|
+
def read_prop_object
|
|
325
|
+
token = @reader.read_sint8
|
|
326
|
+
case token
|
|
327
|
+
when PROPERTY_POOL
|
|
328
|
+
read_pool_object
|
|
329
|
+
when PROPERTY_INT
|
|
330
|
+
@reader.read_sint32
|
|
331
|
+
when PROPERTY_LONG
|
|
332
|
+
@reader.read_sint64
|
|
333
|
+
when PROPERTY_DOUBLE
|
|
334
|
+
@reader.read_float64
|
|
335
|
+
when PROPERTY_FLOAT
|
|
336
|
+
@reader.read_float32
|
|
337
|
+
when PROPERTY_TRUE
|
|
338
|
+
true
|
|
339
|
+
when PROPERTY_FALSE
|
|
340
|
+
false
|
|
341
|
+
when PROPERTY_ARRAY
|
|
342
|
+
type = @reader.read_sint8
|
|
343
|
+
case type
|
|
344
|
+
when PROPERTY_POOL
|
|
345
|
+
@reader.read_sint32.times.map do
|
|
346
|
+
read_pool_object
|
|
347
|
+
end
|
|
348
|
+
when PROPERTY_INT
|
|
349
|
+
@reader.read_sint32.times.map do
|
|
350
|
+
@reader.read_sint32
|
|
351
|
+
end
|
|
352
|
+
when PROPERTY_DOUBLE
|
|
353
|
+
@reader.read_sint32.times.map do
|
|
354
|
+
@reader.read_float64
|
|
355
|
+
end
|
|
356
|
+
else
|
|
357
|
+
raise EncodingError, "unknown BGV property array type 0x#{type.to_s(16)}"
|
|
358
|
+
end
|
|
359
|
+
when PROPERTY_SUBGRAPH
|
|
360
|
+
@graph_props = read_props
|
|
361
|
+
read_graph
|
|
362
|
+
else
|
|
363
|
+
raise EncodingError, "unknown BGV property 0x#{token.to_s(16)}"
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Skip over an object from the pool.
|
|
368
|
+
def skip_pool_object
|
|
369
|
+
token = @reader.read_sint8
|
|
370
|
+
case token
|
|
371
|
+
when POOL_NULL
|
|
372
|
+
when POOL_NEW
|
|
373
|
+
read_pool_entry
|
|
374
|
+
when POOL_STRING, POOL_ENUM, POOL_CLASS, POOL_METHOD, POOL_NODE_CLASS, POOL_FIELD, POOL_SIGNATURE, POOL_NODE_SOURCE_POSITION, POOL_NODE
|
|
375
|
+
@reader.skip_int16
|
|
376
|
+
else
|
|
377
|
+
raise EncodingError, "unknown token 0x#{token.to_s(16)} in BGV pool object"
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Read an object from the pool.
|
|
382
|
+
def read_pool_object
|
|
383
|
+
token = @reader.read_sint8
|
|
384
|
+
case token
|
|
385
|
+
when POOL_NULL
|
|
386
|
+
nil
|
|
387
|
+
when POOL_NEW
|
|
388
|
+
read_pool_entry
|
|
389
|
+
when POOL_STRING, POOL_ENUM, POOL_CLASS, POOL_METHOD, POOL_NODE_CLASS, POOL_FIELD, POOL_SIGNATURE, POOL_NODE_SOURCE_POSITION, POOL_NODE
|
|
390
|
+
id = @reader.read_uint16
|
|
391
|
+
object = @pool[id]
|
|
392
|
+
raise EncodingError, "unknown BGV pool object #{token}" unless object
|
|
393
|
+
|
|
394
|
+
object
|
|
395
|
+
else
|
|
396
|
+
raise EncodingError, "unknown token 0x#{token.to_s(16)} in BGV pool object"
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# Read a new entry to the pool.
|
|
401
|
+
def read_pool_entry
|
|
402
|
+
# Already read POOL_NEW
|
|
403
|
+
id = @reader.read_uint16
|
|
404
|
+
type = @reader.read_sint8
|
|
405
|
+
case type
|
|
406
|
+
when POOL_STRING
|
|
407
|
+
object = read_string
|
|
408
|
+
when POOL_ENUM
|
|
409
|
+
enum_class = read_pool_object
|
|
410
|
+
enum_ordinal = @reader.read_sint32
|
|
411
|
+
raise EncodingError, "unknown BGV eum ordinal #{enum_ordinal} in #{enum_class}" if enum_ordinal.negative? || enum_ordinal >= enum_class.size
|
|
412
|
+
|
|
413
|
+
object = enum_class[enum_ordinal]
|
|
414
|
+
when POOL_CLASS
|
|
415
|
+
type_name = read_string
|
|
416
|
+
token = @reader.read_sint8
|
|
417
|
+
case token
|
|
418
|
+
when ENUM_KLASS
|
|
419
|
+
values = @reader.read_sint32.times.map do
|
|
420
|
+
read_pool_object
|
|
421
|
+
end
|
|
422
|
+
object = values
|
|
423
|
+
when KLASS
|
|
424
|
+
object = type_name
|
|
425
|
+
else
|
|
426
|
+
raise EncodingError, "unknown BGV pool class token 0x#{token.to_s(16)}"
|
|
427
|
+
end
|
|
428
|
+
when POOL_METHOD
|
|
429
|
+
declaring_class = read_pool_object
|
|
430
|
+
method_name = read_pool_object
|
|
431
|
+
signature = read_pool_object
|
|
432
|
+
modifiers = @reader.read_sint32
|
|
433
|
+
bytes_length = @reader.read_sint32
|
|
434
|
+
@reader.skip bytes_length if bytes_length != -1
|
|
435
|
+
object = {
|
|
436
|
+
declaring_class: declaring_class,
|
|
437
|
+
method_name: method_name,
|
|
438
|
+
signature: signature,
|
|
439
|
+
modifiers: modifiers
|
|
440
|
+
}
|
|
441
|
+
when POOL_NODE_CLASS
|
|
442
|
+
node_class = read_pool_object
|
|
443
|
+
name_template = read_string
|
|
444
|
+
inputs = read_edges_info(true)
|
|
445
|
+
outputs = read_edges_info(false)
|
|
446
|
+
object = {
|
|
447
|
+
node_class: node_class,
|
|
448
|
+
name_template: name_template,
|
|
449
|
+
inputs: inputs,
|
|
450
|
+
outputs: outputs
|
|
451
|
+
}
|
|
452
|
+
when POOL_FIELD
|
|
453
|
+
field_class = read_pool_object
|
|
454
|
+
name = read_pool_object
|
|
455
|
+
type_name = read_pool_object
|
|
456
|
+
modifiers = @reader.read_sint32
|
|
457
|
+
object = {
|
|
458
|
+
field_class: field_class,
|
|
459
|
+
name: name,
|
|
460
|
+
type_name: type_name,
|
|
461
|
+
modifiers: modifiers
|
|
462
|
+
}
|
|
463
|
+
when POOL_SIGNATURE
|
|
464
|
+
args = @reader.read_sint16.times.map do
|
|
465
|
+
read_pool_object
|
|
466
|
+
end
|
|
467
|
+
ret = read_pool_object
|
|
468
|
+
object = {
|
|
469
|
+
args: args,
|
|
470
|
+
ret: ret
|
|
471
|
+
}
|
|
472
|
+
when POOL_NODE_SOURCE_POSITION
|
|
473
|
+
method = read_pool_object
|
|
474
|
+
bci = @reader.read_sint32
|
|
475
|
+
locs = []
|
|
476
|
+
loop do
|
|
477
|
+
uri = read_pool_object
|
|
478
|
+
break unless uri
|
|
479
|
+
|
|
480
|
+
location = read_string
|
|
481
|
+
loc_line = @reader.read_sint32
|
|
482
|
+
loc_start = @reader.read_sint32
|
|
483
|
+
loc_end = @reader.read_sint32
|
|
484
|
+
locs.push [location, loc_line, loc_start, loc_end]
|
|
485
|
+
end
|
|
486
|
+
caller = read_pool_object
|
|
487
|
+
object = {
|
|
488
|
+
method: method,
|
|
489
|
+
bci: bci,
|
|
490
|
+
locs: locs,
|
|
491
|
+
caller: caller
|
|
492
|
+
}
|
|
493
|
+
when POOL_NODE
|
|
494
|
+
node_id = @reader.read_sint32
|
|
495
|
+
node_class = read_pool_object
|
|
496
|
+
object = {
|
|
497
|
+
node_id: node_id,
|
|
498
|
+
node_class: node_class
|
|
499
|
+
}
|
|
500
|
+
else
|
|
501
|
+
raise EncodingError, "unknown BGV pool type 0x#{type.to_s(16)}"
|
|
502
|
+
end
|
|
503
|
+
set_pool_entry id, object
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Hook method that can be overidden for debugging.
|
|
507
|
+
def set_pool_entry(id, object)
|
|
508
|
+
@pool[id] = object
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# Read information about edges.
|
|
512
|
+
def read_edges_info(inputs)
|
|
513
|
+
@reader.read_sint16.times.map do
|
|
514
|
+
indirect = read_bool
|
|
515
|
+
name = read_pool_object
|
|
516
|
+
type = (read_pool_object if inputs)
|
|
517
|
+
{
|
|
518
|
+
direct: !indirect,
|
|
519
|
+
name: name,
|
|
520
|
+
type: type
|
|
521
|
+
}
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Skip over a UTF-8 string.
|
|
526
|
+
def skip_string
|
|
527
|
+
length = @reader.read_sint32
|
|
528
|
+
@reader.skip length if length != -1
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# Read a UTF-8 string.
|
|
532
|
+
def read_string
|
|
533
|
+
length = @reader.read_sint32
|
|
534
|
+
if length == -1
|
|
535
|
+
nil
|
|
536
|
+
else
|
|
537
|
+
string = @reader.read_utf8(length)
|
|
538
|
+
raise EncodingError, 'null byte in BGV string' if string.include?("\0")
|
|
539
|
+
|
|
540
|
+
string
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
# Skip over a boolean value.
|
|
545
|
+
def skip_bool
|
|
546
|
+
@reader.skip_int8
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# Read a boolean value.
|
|
550
|
+
def read_bool
|
|
551
|
+
token = @reader.read_uint8
|
|
552
|
+
case token
|
|
553
|
+
when 0
|
|
554
|
+
false
|
|
555
|
+
when 1
|
|
556
|
+
true
|
|
557
|
+
else
|
|
558
|
+
raise ::EncodingError, "unknown BGV boolean value 0x#{token.to_s(16)}"
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# File format constants.
|
|
563
|
+
|
|
564
|
+
MAGIC = 'BIGV'
|
|
565
|
+
|
|
566
|
+
SUPPORTED_VERSIONS = [
|
|
567
|
+
[6, 1],
|
|
568
|
+
[7, 0]
|
|
569
|
+
]
|
|
570
|
+
|
|
571
|
+
BEGIN_GROUP = 0x00
|
|
572
|
+
BEGIN_GRAPH = 0x01
|
|
573
|
+
CLOSE_GROUP = 0x02
|
|
574
|
+
BEGIN_DOCUMENT = 0x03
|
|
575
|
+
|
|
576
|
+
POOL_NEW = 0x00
|
|
577
|
+
POOL_STRING = 0x01
|
|
578
|
+
POOL_ENUM = 0x02
|
|
579
|
+
POOL_CLASS = 0x03
|
|
580
|
+
POOL_METHOD = 0x04
|
|
581
|
+
POOL_NULL = 0x05
|
|
582
|
+
POOL_NODE_CLASS = 0x06
|
|
583
|
+
POOL_FIELD = 0x07
|
|
584
|
+
POOL_SIGNATURE = 0x08
|
|
585
|
+
POOL_NODE_SOURCE_POSITION = 0x09
|
|
586
|
+
POOL_NODE = 0x0a
|
|
587
|
+
|
|
588
|
+
PROPERTY_POOL = 0x00
|
|
589
|
+
PROPERTY_INT = 0x01
|
|
590
|
+
PROPERTY_LONG = 0x02
|
|
591
|
+
PROPERTY_DOUBLE = 0x03
|
|
592
|
+
PROPERTY_FLOAT = 0x04
|
|
593
|
+
PROPERTY_TRUE = 0x05
|
|
594
|
+
PROPERTY_FALSE = 0x06
|
|
595
|
+
PROPERTY_ARRAY = 0x07
|
|
596
|
+
PROPERTY_SUBGRAPH = 0x08
|
|
597
|
+
|
|
598
|
+
KLASS = 0x00
|
|
599
|
+
ENUM_KLASS = 0x01
|
|
600
|
+
end
|
|
601
|
+
end
|
|
602
|
+
end
|