seafoam 0.2

Sign up to get free protection for your applications and to get access to all the features.
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