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,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
|