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.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.github/probots.yml +2 -0
  3. data/.github/workflows/rubocop.yml +10 -0
  4. data/.github/workflows/specs.yml +19 -0
  5. data/.gitignore +7 -0
  6. data/.rubocop.yml +34 -0
  7. data/.ruby-version +1 -0
  8. data/.seafoam/config +1 -0
  9. data/CODE_OF_CONDUCT.md +128 -0
  10. data/CONTRIBUTING.md +5 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE.md +7 -0
  13. data/README.md +298 -0
  14. data/bin/bgv2isabelle +53 -0
  15. data/bin/bgv2json +42 -0
  16. data/bin/seafoam +24 -0
  17. data/docs/annotators.md +43 -0
  18. data/docs/bgv.md +284 -0
  19. data/docs/getting-graphs.md +47 -0
  20. data/examples/Fib.java +24 -0
  21. data/examples/MatMult.java +39 -0
  22. data/examples/fib-java.bgv +0 -0
  23. data/examples/fib-js.bgv +0 -0
  24. data/examples/fib-ruby.bgv +0 -0
  25. data/examples/fib.js +15 -0
  26. data/examples/fib.rb +15 -0
  27. data/examples/identity.bgv +0 -0
  28. data/examples/identity.rb +13 -0
  29. data/examples/java/Irreducible.j +35 -0
  30. data/examples/java/IrreducibleDecompiled.java +21 -0
  31. data/examples/java/JavaExamples.java +418 -0
  32. data/examples/java/exampleArithOperator.bgv +0 -0
  33. data/examples/java/exampleArithOperator.cfg +925 -0
  34. data/examples/java/exampleArrayAllocation.bgv +0 -0
  35. data/examples/java/exampleArrayAllocation.cfg +5268 -0
  36. data/examples/java/exampleArrayRead.bgv +0 -0
  37. data/examples/java/exampleArrayRead.cfg +2263 -0
  38. data/examples/java/exampleArrayWrite.bgv +0 -0
  39. data/examples/java/exampleArrayWrite.cfg +2315 -0
  40. data/examples/java/exampleCatch.bgv +0 -0
  41. data/examples/java/exampleCatch.cfg +4150 -0
  42. data/examples/java/exampleCompareOperator.bgv +0 -0
  43. data/examples/java/exampleCompareOperator.cfg +1109 -0
  44. data/examples/java/exampleDoubleSynchronized.bgv +0 -0
  45. data/examples/java/exampleDoubleSynchronized.cfg +26497 -0
  46. data/examples/java/exampleExactArith.bgv +0 -0
  47. data/examples/java/exampleExactArith.cfg +1888 -0
  48. data/examples/java/exampleFieldRead.bgv +0 -0
  49. data/examples/java/exampleFieldRead.cfg +1228 -0
  50. data/examples/java/exampleFieldWrite.bgv +0 -0
  51. data/examples/java/exampleFieldWrite.cfg +1102 -0
  52. data/examples/java/exampleFor.bgv +0 -0
  53. data/examples/java/exampleFor.cfg +3936 -0
  54. data/examples/java/exampleFullEscape.bgv +0 -0
  55. data/examples/java/exampleFullEscape.cfg +5893 -0
  56. data/examples/java/exampleIf.bgv +0 -0
  57. data/examples/java/exampleIf.cfg +2462 -0
  58. data/examples/java/exampleIfNeverTaken.bgv +0 -0
  59. data/examples/java/exampleIfNeverTaken.cfg +2476 -0
  60. data/examples/java/exampleInstanceOfManyImpls.bgv +0 -0
  61. data/examples/java/exampleInstanceOfManyImpls.cfg +6391 -0
  62. data/examples/java/exampleInstanceOfOneImpl.bgv +0 -0
  63. data/examples/java/exampleInstanceOfOneImpl.cfg +2604 -0
  64. data/examples/java/exampleIntSwitch.bgv +0 -0
  65. data/examples/java/exampleIntSwitch.cfg +3121 -0
  66. data/examples/java/exampleInterfaceCallManyImpls.bgv +0 -0
  67. data/examples/java/exampleInterfaceCallManyImpls.cfg +1358 -0
  68. data/examples/java/exampleInterfaceCallOneImpl.bgv +0 -0
  69. data/examples/java/exampleInterfaceCallOneImpl.cfg +3859 -0
  70. data/examples/java/exampleLocalInstanceOf.bgv +0 -0
  71. data/examples/java/exampleLocalInstanceOf.cfg +5276 -0
  72. data/examples/java/exampleLocalSynchronized.bgv +0 -0
  73. data/examples/java/exampleLocalSynchronized.cfg +1364 -0
  74. data/examples/java/exampleLocalVariables.bgv +0 -0
  75. data/examples/java/exampleLocalVariables.cfg +1195 -0
  76. data/examples/java/exampleLocalVariablesState.bgv +0 -0
  77. data/examples/java/exampleLocalVariablesState.cfg +1673 -0
  78. data/examples/java/exampleNestedWhile.bgv +0 -0
  79. data/examples/java/exampleNestedWhile.cfg +15499 -0
  80. data/examples/java/exampleNestedWhileBreak.bgv +0 -0
  81. data/examples/java/exampleNestedWhileBreak.cfg +11162 -0
  82. data/examples/java/exampleNoEscape.bgv +0 -0
  83. data/examples/java/exampleNoEscape.cfg +974 -0
  84. data/examples/java/exampleObjectAllocation.bgv +0 -0
  85. data/examples/java/exampleObjectAllocation.cfg +5287 -0
  86. data/examples/java/examplePartialEscape.bgv +0 -0
  87. data/examples/java/examplePartialEscape.cfg +7042 -0
  88. data/examples/java/examplePhi.bgv +0 -0
  89. data/examples/java/examplePhi.cfg +3227 -0
  90. data/examples/java/exampleReducible.bgv +0 -0
  91. data/examples/java/exampleReducible.cfg +5578 -0
  92. data/examples/java/exampleSimpleCall.bgv +0 -0
  93. data/examples/java/exampleSimpleCall.cfg +1435 -0
  94. data/examples/java/exampleStamp.bgv +0 -0
  95. data/examples/java/exampleStamp.cfg +913 -0
  96. data/examples/java/exampleStaticCall.bgv +0 -0
  97. data/examples/java/exampleStaticCall.cfg +1154 -0
  98. data/examples/java/exampleStringSwitch.bgv +0 -0
  99. data/examples/java/exampleStringSwitch.cfg +15377 -0
  100. data/examples/java/exampleSynchronized.bgv +0 -0
  101. data/examples/java/exampleSynchronized.cfg +26027 -0
  102. data/examples/java/exampleThrow.bgv +0 -0
  103. data/examples/java/exampleThrow.cfg +780 -0
  104. data/examples/java/exampleThrowCatch.bgv +0 -0
  105. data/examples/java/exampleThrowCatch.cfg +744 -0
  106. data/examples/java/exampleUnsafeRead.bgv +0 -0
  107. data/examples/java/exampleUnsafeRead.cfg +912 -0
  108. data/examples/java/exampleUnsafeWrite.bgv +0 -0
  109. data/examples/java/exampleUnsafeWrite.cfg +962 -0
  110. data/examples/java/exampleWhile.bgv +0 -0
  111. data/examples/java/exampleWhile.cfg +3936 -0
  112. data/examples/java/exampleWhileBreak.bgv +0 -0
  113. data/examples/java/exampleWhileBreak.cfg +5963 -0
  114. data/examples/matmult-java.bgv +0 -0
  115. data/examples/matmult-ruby.bgv +0 -0
  116. data/examples/matmult.rb +29 -0
  117. data/examples/overflow.bgv +0 -0
  118. data/examples/overflow.rb +13 -0
  119. data/lib/seafoam.rb +13 -0
  120. data/lib/seafoam/annotators.rb +54 -0
  121. data/lib/seafoam/annotators/fallback.rb +27 -0
  122. data/lib/seafoam/annotators/graal.rb +376 -0
  123. data/lib/seafoam/bgv/bgv_parser.rb +602 -0
  124. data/lib/seafoam/binary/binary_reader.rb +21 -0
  125. data/lib/seafoam/binary/io_binary_reader.rb +88 -0
  126. data/lib/seafoam/colors.rb +18 -0
  127. data/lib/seafoam/commands.rb +447 -0
  128. data/lib/seafoam/config.rb +34 -0
  129. data/lib/seafoam/graph.rb +91 -0
  130. data/lib/seafoam/graphviz_writer.rb +213 -0
  131. data/lib/seafoam/spotlight.rb +28 -0
  132. data/lib/seafoam/version.rb +5 -0
  133. data/seafoam.gemspec +20 -0
  134. data/spec/seafoam/annotators/fallback_spec.rb +69 -0
  135. data/spec/seafoam/annotators/graal_spec.rb +96 -0
  136. data/spec/seafoam/annotators_spec.rb +61 -0
  137. data/spec/seafoam/bgv/bgv_parser_spec.rb +144 -0
  138. data/spec/seafoam/bgv/fixtures/not.bgv +1 -0
  139. data/spec/seafoam/bgv/fixtures/unsupported.bgv +1 -0
  140. data/spec/seafoam/binary/io_binary_reader_spec.rb +176 -0
  141. data/spec/seafoam/command_spec.rb +252 -0
  142. data/spec/seafoam/graph_spec.rb +172 -0
  143. data/spec/seafoam/graphviz_writer_spec.rb +63 -0
  144. data/spec/seafoam/spec_helpers.rb +30 -0
  145. data/spec/seafoam/spotlight_spec.rb +38 -0
  146. data/tools/render-all +36 -0
  147. 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