syntax_tree 5.0.0 → 5.1.0
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 +4 -4
- data/.rubocop.yml +51 -0
- data/CHANGELOG.md +24 -1
- data/Gemfile.lock +9 -9
- data/README.md +5 -5
- data/lib/syntax_tree/cli.rb +8 -6
- data/lib/syntax_tree/dsl.rb +1004 -0
- data/lib/syntax_tree/formatter.rb +2 -2
- data/lib/syntax_tree/language_server.rb +2 -0
- data/lib/syntax_tree/node.rb +7 -7
- data/lib/syntax_tree/parser.rb +20 -21
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +459 -0
- data/lib/syntax_tree/yarv/bf.rb +179 -0
- data/lib/syntax_tree/yarv/compiler.rb +2287 -0
- data/lib/syntax_tree/yarv/decompiler.rb +254 -0
- data/lib/syntax_tree/yarv/disassembler.rb +211 -0
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1171 -0
- data/lib/syntax_tree/yarv/instructions.rb +5203 -0
- data/lib/syntax_tree/yarv/legacy.rb +192 -0
- data/lib/syntax_tree/yarv/local_table.rb +89 -0
- data/lib/syntax_tree/yarv.rb +287 -0
- data/lib/syntax_tree.rb +23 -1
- data/syntax_tree.gemspec +1 -1
- metadata +15 -4
@@ -0,0 +1,1171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
# This module provides an object representation of the YARV bytecode.
|
5
|
+
module YARV
|
6
|
+
# This class is meant to mirror RubyVM::InstructionSequence. It contains a
|
7
|
+
# list of instructions along with the metadata pertaining to them. It also
|
8
|
+
# functions as a builder for the instruction sequence.
|
9
|
+
class InstructionSequence
|
10
|
+
# When the list of instructions is first being created, it's stored as a
|
11
|
+
# linked list. This is to make it easier to perform peephole optimizations
|
12
|
+
# and other transformations like instruction specialization.
|
13
|
+
class InstructionList
|
14
|
+
class Node
|
15
|
+
attr_accessor :value, :next_node
|
16
|
+
|
17
|
+
def initialize(value, next_node = nil)
|
18
|
+
@value = value
|
19
|
+
@next_node = next_node
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
include Enumerable
|
24
|
+
attr_reader :head_node, :tail_node
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@head_node = nil
|
28
|
+
@tail_node = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def each
|
32
|
+
return to_enum(__method__) unless block_given?
|
33
|
+
each_node { |node| yield node.value }
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_node
|
37
|
+
return to_enum(__method__) unless block_given?
|
38
|
+
node = head_node
|
39
|
+
|
40
|
+
while node
|
41
|
+
yield node, node.value
|
42
|
+
node = node.next_node
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def push(instruction)
|
47
|
+
node = Node.new(instruction)
|
48
|
+
|
49
|
+
if head_node.nil?
|
50
|
+
@head_node = node
|
51
|
+
@tail_node = node
|
52
|
+
else
|
53
|
+
@tail_node.next_node = node
|
54
|
+
@tail_node = node
|
55
|
+
end
|
56
|
+
|
57
|
+
node
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
MAGIC = "YARVInstructionSequence/SimpleDataFormat"
|
62
|
+
|
63
|
+
# This provides a handle to the rb_iseq_load function, which allows you to
|
64
|
+
# pass a serialized iseq to Ruby and have it return a
|
65
|
+
# RubyVM::InstructionSequence object.
|
66
|
+
ISEQ_LOAD =
|
67
|
+
begin
|
68
|
+
Fiddle::Function.new(
|
69
|
+
Fiddle::Handle::DEFAULT["rb_iseq_load"],
|
70
|
+
[Fiddle::TYPE_VOIDP] * 3,
|
71
|
+
Fiddle::TYPE_VOIDP
|
72
|
+
)
|
73
|
+
rescue NameError
|
74
|
+
end
|
75
|
+
|
76
|
+
# This object is used to track the size of the stack at any given time. It
|
77
|
+
# is effectively a mini symbolic interpreter. It's necessary because when
|
78
|
+
# instruction sequences get serialized they include a :stack_max field on
|
79
|
+
# them. This field is used to determine how much stack space to allocate
|
80
|
+
# for the instruction sequence.
|
81
|
+
class Stack
|
82
|
+
attr_reader :current_size, :maximum_size
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
@current_size = 0
|
86
|
+
@maximum_size = 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def change_by(value)
|
90
|
+
@current_size += value
|
91
|
+
@maximum_size = @current_size if @current_size > @maximum_size
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# This represents the destination of instructions that jump. Initially it
|
96
|
+
# does not track its position so that when we perform optimizations the
|
97
|
+
# indices don't get messed up.
|
98
|
+
class Label
|
99
|
+
attr_reader :name
|
100
|
+
|
101
|
+
# When we're serializing the instruction sequence, we need to be able to
|
102
|
+
# look up the label from the branch instructions and then access the
|
103
|
+
# subsequent node. So we'll store the reference here.
|
104
|
+
attr_accessor :node
|
105
|
+
|
106
|
+
def initialize(name = nil)
|
107
|
+
@name = name
|
108
|
+
end
|
109
|
+
|
110
|
+
def patch!(name)
|
111
|
+
@name = name
|
112
|
+
end
|
113
|
+
|
114
|
+
def inspect
|
115
|
+
name.inspect
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# The type of the instruction sequence.
|
120
|
+
attr_reader :type
|
121
|
+
|
122
|
+
# The name of the instruction sequence.
|
123
|
+
attr_reader :name
|
124
|
+
|
125
|
+
# The parent instruction sequence, if there is one.
|
126
|
+
attr_reader :parent_iseq
|
127
|
+
|
128
|
+
# The location of the root node of this instruction sequence.
|
129
|
+
attr_reader :location
|
130
|
+
|
131
|
+
# This is the list of information about the arguments to this
|
132
|
+
# instruction sequence.
|
133
|
+
attr_accessor :argument_size
|
134
|
+
attr_reader :argument_options
|
135
|
+
|
136
|
+
# The catch table for this instruction sequence.
|
137
|
+
attr_reader :catch_table
|
138
|
+
|
139
|
+
# The list of instructions for this instruction sequence.
|
140
|
+
attr_reader :insns
|
141
|
+
|
142
|
+
# The table of local variables.
|
143
|
+
attr_reader :local_table
|
144
|
+
|
145
|
+
# The hash of names of instance and class variables pointing to the
|
146
|
+
# index of their associated inline storage.
|
147
|
+
attr_reader :inline_storages
|
148
|
+
|
149
|
+
# The index of the next inline storage that will be created.
|
150
|
+
attr_reader :storage_index
|
151
|
+
|
152
|
+
# An object that will track the current size of the stack and the
|
153
|
+
# maximum size of the stack for this instruction sequence.
|
154
|
+
attr_reader :stack
|
155
|
+
|
156
|
+
# These are various compilation options provided.
|
157
|
+
attr_reader :options
|
158
|
+
|
159
|
+
def initialize(
|
160
|
+
type,
|
161
|
+
name,
|
162
|
+
parent_iseq,
|
163
|
+
location,
|
164
|
+
options = Compiler::Options.new
|
165
|
+
)
|
166
|
+
@type = type
|
167
|
+
@name = name
|
168
|
+
@parent_iseq = parent_iseq
|
169
|
+
@location = location
|
170
|
+
|
171
|
+
@argument_size = 0
|
172
|
+
@argument_options = {}
|
173
|
+
@catch_table = []
|
174
|
+
|
175
|
+
@local_table = LocalTable.new
|
176
|
+
@inline_storages = {}
|
177
|
+
@insns = InstructionList.new
|
178
|
+
@storage_index = 0
|
179
|
+
@stack = Stack.new
|
180
|
+
|
181
|
+
@options = options
|
182
|
+
end
|
183
|
+
|
184
|
+
##########################################################################
|
185
|
+
# Query methods
|
186
|
+
##########################################################################
|
187
|
+
|
188
|
+
def local_variable(name, level = 0)
|
189
|
+
if (lookup = local_table.find(name, level))
|
190
|
+
lookup
|
191
|
+
elsif parent_iseq
|
192
|
+
parent_iseq.local_variable(name, level + 1)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def inline_storage
|
197
|
+
storage = storage_index
|
198
|
+
@storage_index += 1
|
199
|
+
storage
|
200
|
+
end
|
201
|
+
|
202
|
+
def inline_storage_for(name)
|
203
|
+
inline_storages[name] = inline_storage unless inline_storages.key?(name)
|
204
|
+
|
205
|
+
inline_storages[name]
|
206
|
+
end
|
207
|
+
|
208
|
+
def length
|
209
|
+
insns
|
210
|
+
.each
|
211
|
+
.inject(0) do |sum, insn|
|
212
|
+
case insn
|
213
|
+
when Integer, Label, Symbol
|
214
|
+
sum
|
215
|
+
else
|
216
|
+
sum + insn.length
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def eval
|
222
|
+
raise "Unsupported platform" if ISEQ_LOAD.nil?
|
223
|
+
Fiddle.dlunwrap(ISEQ_LOAD.call(Fiddle.dlwrap(to_a), 0, nil)).eval
|
224
|
+
end
|
225
|
+
|
226
|
+
def to_a
|
227
|
+
versions = RUBY_VERSION.split(".").map(&:to_i)
|
228
|
+
|
229
|
+
# Dump all of the instructions into a flat list.
|
230
|
+
dumped =
|
231
|
+
insns.map do |insn|
|
232
|
+
case insn
|
233
|
+
when Integer, Symbol
|
234
|
+
insn
|
235
|
+
when Label
|
236
|
+
insn.name
|
237
|
+
else
|
238
|
+
insn.to_a(self)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
dumped_options = argument_options.dup
|
243
|
+
dumped_options[:opt].map!(&:name) if dumped_options[:opt]
|
244
|
+
|
245
|
+
# Next, return the instruction sequence as an array.
|
246
|
+
[
|
247
|
+
MAGIC,
|
248
|
+
versions[0],
|
249
|
+
versions[1],
|
250
|
+
1,
|
251
|
+
{
|
252
|
+
arg_size: argument_size,
|
253
|
+
local_size: local_table.size,
|
254
|
+
stack_max: stack.maximum_size,
|
255
|
+
node_id: -1,
|
256
|
+
node_ids: [-1] * insns.length
|
257
|
+
},
|
258
|
+
name,
|
259
|
+
"<compiled>",
|
260
|
+
"<compiled>",
|
261
|
+
location.start_line,
|
262
|
+
type,
|
263
|
+
local_table.names,
|
264
|
+
dumped_options,
|
265
|
+
catch_table.map(&:to_a),
|
266
|
+
dumped
|
267
|
+
]
|
268
|
+
end
|
269
|
+
|
270
|
+
def disasm
|
271
|
+
disassembler = Disassembler.new
|
272
|
+
disassembler.enqueue(self)
|
273
|
+
disassembler.format!
|
274
|
+
end
|
275
|
+
|
276
|
+
# This method converts our linked list of instructions into a final array
|
277
|
+
# and performs any other compilation steps necessary.
|
278
|
+
def compile!
|
279
|
+
specialize_instructions! if options.specialized_instruction?
|
280
|
+
|
281
|
+
length = 0
|
282
|
+
insns.each do |insn|
|
283
|
+
case insn
|
284
|
+
when Integer, Symbol
|
285
|
+
# skip
|
286
|
+
when Label
|
287
|
+
insn.patch!(:"label_#{length}")
|
288
|
+
when DefineClass
|
289
|
+
insn.class_iseq.compile!
|
290
|
+
length += insn.length
|
291
|
+
when DefineMethod, DefineSMethod
|
292
|
+
insn.method_iseq.compile!
|
293
|
+
length += insn.length
|
294
|
+
when InvokeSuper, Send
|
295
|
+
insn.block_iseq.compile! if insn.block_iseq
|
296
|
+
length += insn.length
|
297
|
+
when Once
|
298
|
+
insn.iseq.compile!
|
299
|
+
length += insn.length
|
300
|
+
else
|
301
|
+
length += insn.length
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
@insns = insns.to_a
|
306
|
+
end
|
307
|
+
|
308
|
+
def specialize_instructions!
|
309
|
+
insns.each_node do |node, value|
|
310
|
+
case value
|
311
|
+
when NewArray
|
312
|
+
next unless node.next_node
|
313
|
+
|
314
|
+
next_node = node.next_node
|
315
|
+
next unless next_node.value.is_a?(Send)
|
316
|
+
next if next_node.value.block_iseq
|
317
|
+
|
318
|
+
calldata = next_node.value.calldata
|
319
|
+
next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
|
320
|
+
next unless calldata.argc == 0
|
321
|
+
|
322
|
+
case calldata.method
|
323
|
+
when :max
|
324
|
+
node.value = OptNewArrayMax.new(value.number)
|
325
|
+
node.next_node = next_node.next_node
|
326
|
+
when :min
|
327
|
+
node.value = OptNewArrayMin.new(value.number)
|
328
|
+
node.next_node = next_node.next_node
|
329
|
+
end
|
330
|
+
when PutObject, PutString
|
331
|
+
next unless node.next_node
|
332
|
+
next if value.is_a?(PutObject) && !value.object.is_a?(String)
|
333
|
+
|
334
|
+
next_node = node.next_node
|
335
|
+
next unless next_node.value.is_a?(Send)
|
336
|
+
next if next_node.value.block_iseq
|
337
|
+
|
338
|
+
calldata = next_node.value.calldata
|
339
|
+
next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
|
340
|
+
next unless calldata.argc == 0
|
341
|
+
|
342
|
+
case calldata.method
|
343
|
+
when :freeze
|
344
|
+
node.value = OptStrFreeze.new(value.object, calldata)
|
345
|
+
node.next_node = next_node.next_node
|
346
|
+
when :-@
|
347
|
+
node.value = OptStrUMinus.new(value.object, calldata)
|
348
|
+
node.next_node = next_node.next_node
|
349
|
+
end
|
350
|
+
when Send
|
351
|
+
calldata = value.calldata
|
352
|
+
|
353
|
+
if !value.block_iseq &&
|
354
|
+
!calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
|
355
|
+
# Specialize the send instruction. If it doesn't have a block
|
356
|
+
# attached, then we will replace it with an opt_send_without_block
|
357
|
+
# and do further specializations based on the called method and
|
358
|
+
# the number of arguments.
|
359
|
+
node.value =
|
360
|
+
case [calldata.method, calldata.argc]
|
361
|
+
when [:length, 0]
|
362
|
+
OptLength.new(calldata)
|
363
|
+
when [:size, 0]
|
364
|
+
OptSize.new(calldata)
|
365
|
+
when [:empty?, 0]
|
366
|
+
OptEmptyP.new(calldata)
|
367
|
+
when [:nil?, 0]
|
368
|
+
OptNilP.new(calldata)
|
369
|
+
when [:succ, 0]
|
370
|
+
OptSucc.new(calldata)
|
371
|
+
when [:!, 0]
|
372
|
+
OptNot.new(calldata)
|
373
|
+
when [:+, 1]
|
374
|
+
OptPlus.new(calldata)
|
375
|
+
when [:-, 1]
|
376
|
+
OptMinus.new(calldata)
|
377
|
+
when [:*, 1]
|
378
|
+
OptMult.new(calldata)
|
379
|
+
when [:/, 1]
|
380
|
+
OptDiv.new(calldata)
|
381
|
+
when [:%, 1]
|
382
|
+
OptMod.new(calldata)
|
383
|
+
when [:==, 1]
|
384
|
+
OptEq.new(calldata)
|
385
|
+
when [:!=, 1]
|
386
|
+
OptNEq.new(YARV.calldata(:==, 1), calldata)
|
387
|
+
when [:=~, 1]
|
388
|
+
OptRegExpMatch2.new(calldata)
|
389
|
+
when [:<, 1]
|
390
|
+
OptLT.new(calldata)
|
391
|
+
when [:<=, 1]
|
392
|
+
OptLE.new(calldata)
|
393
|
+
when [:>, 1]
|
394
|
+
OptGT.new(calldata)
|
395
|
+
when [:>=, 1]
|
396
|
+
OptGE.new(calldata)
|
397
|
+
when [:<<, 1]
|
398
|
+
OptLTLT.new(calldata)
|
399
|
+
when [:[], 1]
|
400
|
+
OptAref.new(calldata)
|
401
|
+
when [:&, 1]
|
402
|
+
OptAnd.new(calldata)
|
403
|
+
when [:|, 1]
|
404
|
+
OptOr.new(calldata)
|
405
|
+
when [:[]=, 2]
|
406
|
+
OptAset.new(calldata)
|
407
|
+
else
|
408
|
+
OptSendWithoutBlock.new(calldata)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
##########################################################################
|
416
|
+
# Child instruction sequence methods
|
417
|
+
##########################################################################
|
418
|
+
|
419
|
+
def child_iseq(type, name, location)
|
420
|
+
InstructionSequence.new(type, name, self, location, options)
|
421
|
+
end
|
422
|
+
|
423
|
+
def block_child_iseq(location)
|
424
|
+
current = self
|
425
|
+
current = current.parent_iseq while current.type == :block
|
426
|
+
child_iseq(:block, "block in #{current.name}", location)
|
427
|
+
end
|
428
|
+
|
429
|
+
def class_child_iseq(name, location)
|
430
|
+
child_iseq(:class, "<class:#{name}>", location)
|
431
|
+
end
|
432
|
+
|
433
|
+
def method_child_iseq(name, location)
|
434
|
+
child_iseq(:method, name, location)
|
435
|
+
end
|
436
|
+
|
437
|
+
def module_child_iseq(name, location)
|
438
|
+
child_iseq(:class, "<module:#{name}>", location)
|
439
|
+
end
|
440
|
+
|
441
|
+
def singleton_class_child_iseq(location)
|
442
|
+
child_iseq(:class, "singleton class", location)
|
443
|
+
end
|
444
|
+
|
445
|
+
##########################################################################
|
446
|
+
# Catch table methods
|
447
|
+
##########################################################################
|
448
|
+
|
449
|
+
class CatchEntry
|
450
|
+
attr_reader :iseq, :begin_label, :end_label, :exit_label
|
451
|
+
|
452
|
+
def initialize(iseq, begin_label, end_label, exit_label)
|
453
|
+
@iseq = iseq
|
454
|
+
@begin_label = begin_label
|
455
|
+
@end_label = end_label
|
456
|
+
@exit_label = exit_label
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
class CatchBreak < CatchEntry
|
461
|
+
def to_a
|
462
|
+
[:break, iseq.to_a, begin_label.name, end_label.name, exit_label.name]
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
class CatchNext < CatchEntry
|
467
|
+
def to_a
|
468
|
+
[:next, nil, begin_label.name, end_label.name, exit_label.name]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
class CatchRedo < CatchEntry
|
473
|
+
def to_a
|
474
|
+
[:redo, nil, begin_label.name, end_label.name, exit_label.name]
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
class CatchRescue < CatchEntry
|
479
|
+
def to_a
|
480
|
+
[
|
481
|
+
:rescue,
|
482
|
+
iseq.to_a,
|
483
|
+
begin_label.name,
|
484
|
+
end_label.name,
|
485
|
+
exit_label.name
|
486
|
+
]
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
class CatchRetry < CatchEntry
|
491
|
+
def to_a
|
492
|
+
[:retry, nil, begin_label.name, end_label.name, exit_label.name]
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
def catch_break(iseq, begin_label, end_label, exit_label)
|
497
|
+
catch_table << CatchBreak.new(iseq, begin_label, end_label, exit_label)
|
498
|
+
end
|
499
|
+
|
500
|
+
def catch_next(begin_label, end_label, exit_label)
|
501
|
+
catch_table << CatchNext.new(nil, begin_label, end_label, exit_label)
|
502
|
+
end
|
503
|
+
|
504
|
+
def catch_redo(begin_label, end_label, exit_label)
|
505
|
+
catch_table << CatchRedo.new(nil, begin_label, end_label, exit_label)
|
506
|
+
end
|
507
|
+
|
508
|
+
def catch_rescue(iseq, begin_label, end_label, exit_label)
|
509
|
+
catch_table << CatchRescue.new(iseq, begin_label, end_label, exit_label)
|
510
|
+
end
|
511
|
+
|
512
|
+
def catch_retry(begin_label, end_label, exit_label)
|
513
|
+
catch_table << CatchRetry.new(nil, begin_label, end_label, exit_label)
|
514
|
+
end
|
515
|
+
|
516
|
+
##########################################################################
|
517
|
+
# Instruction push methods
|
518
|
+
##########################################################################
|
519
|
+
|
520
|
+
def label
|
521
|
+
Label.new
|
522
|
+
end
|
523
|
+
|
524
|
+
def push(value)
|
525
|
+
node = insns.push(value)
|
526
|
+
|
527
|
+
case value
|
528
|
+
when Array, Integer, Symbol
|
529
|
+
value
|
530
|
+
when Label
|
531
|
+
value.node = node
|
532
|
+
value
|
533
|
+
else
|
534
|
+
stack.change_by(-value.pops + value.pushes)
|
535
|
+
value
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
def event(name)
|
540
|
+
push(name)
|
541
|
+
end
|
542
|
+
|
543
|
+
def adjuststack(number)
|
544
|
+
push(AdjustStack.new(number))
|
545
|
+
end
|
546
|
+
|
547
|
+
def anytostring
|
548
|
+
push(AnyToString.new)
|
549
|
+
end
|
550
|
+
|
551
|
+
def branchif(label)
|
552
|
+
push(BranchIf.new(label))
|
553
|
+
end
|
554
|
+
|
555
|
+
def branchnil(label)
|
556
|
+
push(BranchNil.new(label))
|
557
|
+
end
|
558
|
+
|
559
|
+
def branchunless(label)
|
560
|
+
push(BranchUnless.new(label))
|
561
|
+
end
|
562
|
+
|
563
|
+
def checkkeyword(keyword_bits_index, keyword_index)
|
564
|
+
push(CheckKeyword.new(keyword_bits_index, keyword_index))
|
565
|
+
end
|
566
|
+
|
567
|
+
def checkmatch(type)
|
568
|
+
push(CheckMatch.new(type))
|
569
|
+
end
|
570
|
+
|
571
|
+
def checktype(type)
|
572
|
+
push(CheckType.new(type))
|
573
|
+
end
|
574
|
+
|
575
|
+
def concatarray
|
576
|
+
push(ConcatArray.new)
|
577
|
+
end
|
578
|
+
|
579
|
+
def concatstrings(number)
|
580
|
+
push(ConcatStrings.new(number))
|
581
|
+
end
|
582
|
+
|
583
|
+
def defined(type, name, message)
|
584
|
+
push(Defined.new(type, name, message))
|
585
|
+
end
|
586
|
+
|
587
|
+
def defineclass(name, class_iseq, flags)
|
588
|
+
push(DefineClass.new(name, class_iseq, flags))
|
589
|
+
end
|
590
|
+
|
591
|
+
def definemethod(name, method_iseq)
|
592
|
+
push(DefineMethod.new(name, method_iseq))
|
593
|
+
end
|
594
|
+
|
595
|
+
def definesmethod(name, method_iseq)
|
596
|
+
push(DefineSMethod.new(name, method_iseq))
|
597
|
+
end
|
598
|
+
|
599
|
+
def dup
|
600
|
+
push(Dup.new)
|
601
|
+
end
|
602
|
+
|
603
|
+
def duparray(object)
|
604
|
+
push(DupArray.new(object))
|
605
|
+
end
|
606
|
+
|
607
|
+
def duphash(object)
|
608
|
+
push(DupHash.new(object))
|
609
|
+
end
|
610
|
+
|
611
|
+
def dupn(number)
|
612
|
+
push(DupN.new(number))
|
613
|
+
end
|
614
|
+
|
615
|
+
def expandarray(length, flags)
|
616
|
+
push(ExpandArray.new(length, flags))
|
617
|
+
end
|
618
|
+
|
619
|
+
def getblockparam(index, level)
|
620
|
+
push(GetBlockParam.new(index, level))
|
621
|
+
end
|
622
|
+
|
623
|
+
def getblockparamproxy(index, level)
|
624
|
+
push(GetBlockParamProxy.new(index, level))
|
625
|
+
end
|
626
|
+
|
627
|
+
def getclassvariable(name)
|
628
|
+
if RUBY_VERSION < "3.0"
|
629
|
+
push(Legacy::GetClassVariable.new(name))
|
630
|
+
else
|
631
|
+
push(GetClassVariable.new(name, inline_storage_for(name)))
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
def getconstant(name)
|
636
|
+
push(GetConstant.new(name))
|
637
|
+
end
|
638
|
+
|
639
|
+
def getglobal(name)
|
640
|
+
push(GetGlobal.new(name))
|
641
|
+
end
|
642
|
+
|
643
|
+
def getinstancevariable(name)
|
644
|
+
if RUBY_VERSION < "3.2"
|
645
|
+
push(GetInstanceVariable.new(name, inline_storage_for(name)))
|
646
|
+
else
|
647
|
+
push(GetInstanceVariable.new(name, inline_storage))
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
def getlocal(index, level)
|
652
|
+
if options.operands_unification?
|
653
|
+
# Specialize the getlocal instruction based on the level of the
|
654
|
+
# local variable. If it's 0 or 1, then there's a specialized
|
655
|
+
# instruction that will look at the current scope or the parent
|
656
|
+
# scope, respectively, and requires fewer operands.
|
657
|
+
case level
|
658
|
+
when 0
|
659
|
+
push(GetLocalWC0.new(index))
|
660
|
+
when 1
|
661
|
+
push(GetLocalWC1.new(index))
|
662
|
+
else
|
663
|
+
push(GetLocal.new(index, level))
|
664
|
+
end
|
665
|
+
else
|
666
|
+
push(GetLocal.new(index, level))
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
def getspecial(key, type)
|
671
|
+
push(GetSpecial.new(key, type))
|
672
|
+
end
|
673
|
+
|
674
|
+
def intern
|
675
|
+
push(Intern.new)
|
676
|
+
end
|
677
|
+
|
678
|
+
def invokeblock(calldata)
|
679
|
+
push(InvokeBlock.new(calldata))
|
680
|
+
end
|
681
|
+
|
682
|
+
def invokesuper(calldata, block_iseq)
|
683
|
+
push(InvokeSuper.new(calldata, block_iseq))
|
684
|
+
end
|
685
|
+
|
686
|
+
def jump(label)
|
687
|
+
push(Jump.new(label))
|
688
|
+
end
|
689
|
+
|
690
|
+
def leave
|
691
|
+
push(Leave.new)
|
692
|
+
end
|
693
|
+
|
694
|
+
def newarray(number)
|
695
|
+
push(NewArray.new(number))
|
696
|
+
end
|
697
|
+
|
698
|
+
def newarraykwsplat(number)
|
699
|
+
push(NewArrayKwSplat.new(number))
|
700
|
+
end
|
701
|
+
|
702
|
+
def newhash(number)
|
703
|
+
push(NewHash.new(number))
|
704
|
+
end
|
705
|
+
|
706
|
+
def newrange(exclude_end)
|
707
|
+
push(NewRange.new(exclude_end))
|
708
|
+
end
|
709
|
+
|
710
|
+
def nop
|
711
|
+
push(Nop.new)
|
712
|
+
end
|
713
|
+
|
714
|
+
def objtostring(calldata)
|
715
|
+
push(ObjToString.new(calldata))
|
716
|
+
end
|
717
|
+
|
718
|
+
def once(iseq, cache)
|
719
|
+
push(Once.new(iseq, cache))
|
720
|
+
end
|
721
|
+
|
722
|
+
def opt_aref_with(object, calldata)
|
723
|
+
push(OptArefWith.new(object, calldata))
|
724
|
+
end
|
725
|
+
|
726
|
+
def opt_aset_with(object, calldata)
|
727
|
+
push(OptAsetWith.new(object, calldata))
|
728
|
+
end
|
729
|
+
|
730
|
+
def opt_case_dispatch(case_dispatch_hash, else_label)
|
731
|
+
push(OptCaseDispatch.new(case_dispatch_hash, else_label))
|
732
|
+
end
|
733
|
+
|
734
|
+
def opt_getconstant_path(names)
|
735
|
+
if RUBY_VERSION < "3.2" || !options.inline_const_cache?
|
736
|
+
cache = nil
|
737
|
+
cache_filled_label = nil
|
738
|
+
|
739
|
+
if options.inline_const_cache?
|
740
|
+
cache = inline_storage
|
741
|
+
cache_filled_label = label
|
742
|
+
opt_getinlinecache(cache_filled_label, cache)
|
743
|
+
|
744
|
+
if names[0] == :""
|
745
|
+
names.shift
|
746
|
+
pop
|
747
|
+
putobject(Object)
|
748
|
+
end
|
749
|
+
elsif names[0] == :""
|
750
|
+
names.shift
|
751
|
+
putobject(Object)
|
752
|
+
else
|
753
|
+
putnil
|
754
|
+
end
|
755
|
+
|
756
|
+
names.each_with_index do |name, index|
|
757
|
+
putobject(index == 0)
|
758
|
+
getconstant(name)
|
759
|
+
end
|
760
|
+
|
761
|
+
if options.inline_const_cache?
|
762
|
+
opt_setinlinecache(cache)
|
763
|
+
push(cache_filled_label)
|
764
|
+
end
|
765
|
+
else
|
766
|
+
push(OptGetConstantPath.new(names))
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
def opt_getinlinecache(label, cache)
|
771
|
+
push(Legacy::OptGetInlineCache.new(label, cache))
|
772
|
+
end
|
773
|
+
|
774
|
+
def opt_setinlinecache(cache)
|
775
|
+
push(Legacy::OptSetInlineCache.new(cache))
|
776
|
+
end
|
777
|
+
|
778
|
+
def pop
|
779
|
+
push(Pop.new)
|
780
|
+
end
|
781
|
+
|
782
|
+
def putnil
|
783
|
+
push(PutNil.new)
|
784
|
+
end
|
785
|
+
|
786
|
+
def putobject(object)
|
787
|
+
if options.operands_unification?
|
788
|
+
# Specialize the putobject instruction based on the value of the
|
789
|
+
# object. If it's 0 or 1, then there's a specialized instruction
|
790
|
+
# that will push the object onto the stack and requires fewer
|
791
|
+
# operands.
|
792
|
+
if object.eql?(0)
|
793
|
+
push(PutObjectInt2Fix0.new)
|
794
|
+
elsif object.eql?(1)
|
795
|
+
push(PutObjectInt2Fix1.new)
|
796
|
+
else
|
797
|
+
push(PutObject.new(object))
|
798
|
+
end
|
799
|
+
else
|
800
|
+
push(PutObject.new(object))
|
801
|
+
end
|
802
|
+
end
|
803
|
+
|
804
|
+
def putself
|
805
|
+
push(PutSelf.new)
|
806
|
+
end
|
807
|
+
|
808
|
+
def putspecialobject(object)
|
809
|
+
push(PutSpecialObject.new(object))
|
810
|
+
end
|
811
|
+
|
812
|
+
def putstring(object)
|
813
|
+
push(PutString.new(object))
|
814
|
+
end
|
815
|
+
|
816
|
+
def send(calldata, block_iseq = nil)
|
817
|
+
push(Send.new(calldata, block_iseq))
|
818
|
+
end
|
819
|
+
|
820
|
+
def setblockparam(index, level)
|
821
|
+
push(SetBlockParam.new(index, level))
|
822
|
+
end
|
823
|
+
|
824
|
+
def setclassvariable(name)
|
825
|
+
if RUBY_VERSION < "3.0"
|
826
|
+
push(Legacy::SetClassVariable.new(name))
|
827
|
+
else
|
828
|
+
push(SetClassVariable.new(name, inline_storage_for(name)))
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
def setconstant(name)
|
833
|
+
push(SetConstant.new(name))
|
834
|
+
end
|
835
|
+
|
836
|
+
def setglobal(name)
|
837
|
+
push(SetGlobal.new(name))
|
838
|
+
end
|
839
|
+
|
840
|
+
def setinstancevariable(name)
|
841
|
+
if RUBY_VERSION < "3.2"
|
842
|
+
push(SetInstanceVariable.new(name, inline_storage_for(name)))
|
843
|
+
else
|
844
|
+
push(SetInstanceVariable.new(name, inline_storage))
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
def setlocal(index, level)
|
849
|
+
if options.operands_unification?
|
850
|
+
# Specialize the setlocal instruction based on the level of the
|
851
|
+
# local variable. If it's 0 or 1, then there's a specialized
|
852
|
+
# instruction that will write to the current scope or the parent
|
853
|
+
# scope, respectively, and requires fewer operands.
|
854
|
+
case level
|
855
|
+
when 0
|
856
|
+
push(SetLocalWC0.new(index))
|
857
|
+
when 1
|
858
|
+
push(SetLocalWC1.new(index))
|
859
|
+
else
|
860
|
+
push(SetLocal.new(index, level))
|
861
|
+
end
|
862
|
+
else
|
863
|
+
push(SetLocal.new(index, level))
|
864
|
+
end
|
865
|
+
end
|
866
|
+
|
867
|
+
def setn(number)
|
868
|
+
push(SetN.new(number))
|
869
|
+
end
|
870
|
+
|
871
|
+
def setspecial(key)
|
872
|
+
push(SetSpecial.new(key))
|
873
|
+
end
|
874
|
+
|
875
|
+
def splatarray(flag)
|
876
|
+
push(SplatArray.new(flag))
|
877
|
+
end
|
878
|
+
|
879
|
+
def swap
|
880
|
+
push(Swap.new)
|
881
|
+
end
|
882
|
+
|
883
|
+
def throw(type)
|
884
|
+
push(Throw.new(type))
|
885
|
+
end
|
886
|
+
|
887
|
+
def topn(number)
|
888
|
+
push(TopN.new(number))
|
889
|
+
end
|
890
|
+
|
891
|
+
def toregexp(options, length)
|
892
|
+
push(ToRegExp.new(options, length))
|
893
|
+
end
|
894
|
+
|
895
|
+
# This method will create a new instruction sequence from a serialized
|
896
|
+
# RubyVM::InstructionSequence object.
|
897
|
+
def self.from(source, options = Compiler::Options.new, parent_iseq = nil)
|
898
|
+
iseq = new(source[9], source[5], parent_iseq, Location.default, options)
|
899
|
+
|
900
|
+
# set up the labels object so that the labels are shared between the
|
901
|
+
# location in the instruction sequence and the instructions that
|
902
|
+
# reference them
|
903
|
+
labels = Hash.new { |hash, name| hash[name] = Label.new(name) }
|
904
|
+
|
905
|
+
# set up the correct argument size
|
906
|
+
iseq.argument_size = source[4][:arg_size]
|
907
|
+
|
908
|
+
# set up all of the locals
|
909
|
+
source[10].each { |local| iseq.local_table.plain(local) }
|
910
|
+
|
911
|
+
# set up the argument options
|
912
|
+
iseq.argument_options.merge!(source[11])
|
913
|
+
if iseq.argument_options[:opt]
|
914
|
+
iseq.argument_options[:opt].map! { |opt| labels[opt] }
|
915
|
+
end
|
916
|
+
|
917
|
+
# set up the catch table
|
918
|
+
source[12].each do |entry|
|
919
|
+
case entry[0]
|
920
|
+
when :break
|
921
|
+
iseq.catch_break(
|
922
|
+
from(entry[1]),
|
923
|
+
labels[entry[2]],
|
924
|
+
labels[entry[3]],
|
925
|
+
labels[entry[4]]
|
926
|
+
)
|
927
|
+
when :next
|
928
|
+
iseq.catch_next(
|
929
|
+
labels[entry[2]],
|
930
|
+
labels[entry[3]],
|
931
|
+
labels[entry[4]]
|
932
|
+
)
|
933
|
+
when :rescue
|
934
|
+
iseq.catch_rescue(
|
935
|
+
from(entry[1]),
|
936
|
+
labels[entry[2]],
|
937
|
+
labels[entry[3]],
|
938
|
+
labels[entry[4]]
|
939
|
+
)
|
940
|
+
when :redo
|
941
|
+
iseq.catch_redo(
|
942
|
+
labels[entry[2]],
|
943
|
+
labels[entry[3]],
|
944
|
+
labels[entry[4]]
|
945
|
+
)
|
946
|
+
when :retry
|
947
|
+
iseq.catch_retry(
|
948
|
+
labels[entry[2]],
|
949
|
+
labels[entry[3]],
|
950
|
+
labels[entry[4]]
|
951
|
+
)
|
952
|
+
else
|
953
|
+
raise "unknown catch type: #{entry[0]}"
|
954
|
+
end
|
955
|
+
end
|
956
|
+
|
957
|
+
# set up all of the instructions
|
958
|
+
source[13].each do |insn|
|
959
|
+
# add line numbers
|
960
|
+
if insn.is_a?(Integer)
|
961
|
+
iseq.push(insn)
|
962
|
+
next
|
963
|
+
end
|
964
|
+
|
965
|
+
# add events and labels
|
966
|
+
if insn.is_a?(Symbol)
|
967
|
+
if insn.start_with?("label_")
|
968
|
+
iseq.push(labels[insn])
|
969
|
+
else
|
970
|
+
iseq.push(insn)
|
971
|
+
end
|
972
|
+
next
|
973
|
+
end
|
974
|
+
|
975
|
+
# add instructions, mapped to our own instruction classes
|
976
|
+
type, *opnds = insn
|
977
|
+
|
978
|
+
case type
|
979
|
+
when :adjuststack
|
980
|
+
iseq.adjuststack(opnds[0])
|
981
|
+
when :anytostring
|
982
|
+
iseq.anytostring
|
983
|
+
when :branchif
|
984
|
+
iseq.branchif(labels[opnds[0]])
|
985
|
+
when :branchnil
|
986
|
+
iseq.branchnil(labels[opnds[0]])
|
987
|
+
when :branchunless
|
988
|
+
iseq.branchunless(labels[opnds[0]])
|
989
|
+
when :checkkeyword
|
990
|
+
iseq.checkkeyword(iseq.local_table.size - opnds[0] + 2, opnds[1])
|
991
|
+
when :checkmatch
|
992
|
+
iseq.checkmatch(opnds[0])
|
993
|
+
when :checktype
|
994
|
+
iseq.checktype(opnds[0])
|
995
|
+
when :concatarray
|
996
|
+
iseq.concatarray
|
997
|
+
when :concatstrings
|
998
|
+
iseq.concatstrings(opnds[0])
|
999
|
+
when :defineclass
|
1000
|
+
iseq.defineclass(opnds[0], from(opnds[1], options, iseq), opnds[2])
|
1001
|
+
when :defined
|
1002
|
+
iseq.defined(opnds[0], opnds[1], opnds[2])
|
1003
|
+
when :definemethod
|
1004
|
+
iseq.definemethod(opnds[0], from(opnds[1], options, iseq))
|
1005
|
+
when :definesmethod
|
1006
|
+
iseq.definesmethod(opnds[0], from(opnds[1], options, iseq))
|
1007
|
+
when :dup
|
1008
|
+
iseq.dup
|
1009
|
+
when :duparray
|
1010
|
+
iseq.duparray(opnds[0])
|
1011
|
+
when :duphash
|
1012
|
+
iseq.duphash(opnds[0])
|
1013
|
+
when :dupn
|
1014
|
+
iseq.dupn(opnds[0])
|
1015
|
+
when :expandarray
|
1016
|
+
iseq.expandarray(opnds[0], opnds[1])
|
1017
|
+
when :getblockparam, :getblockparamproxy, :getlocal, :getlocal_WC_0,
|
1018
|
+
:getlocal_WC_1, :setblockparam, :setlocal, :setlocal_WC_0,
|
1019
|
+
:setlocal_WC_1
|
1020
|
+
current = iseq
|
1021
|
+
level = 0
|
1022
|
+
|
1023
|
+
case type
|
1024
|
+
when :getlocal_WC_1, :setlocal_WC_1
|
1025
|
+
level = 1
|
1026
|
+
when :getblockparam, :getblockparamproxy, :getlocal, :setblockparam,
|
1027
|
+
:setlocal
|
1028
|
+
level = opnds[1]
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
level.times { current = current.parent_iseq }
|
1032
|
+
index = current.local_table.size - opnds[0] + 2
|
1033
|
+
|
1034
|
+
case type
|
1035
|
+
when :getblockparam
|
1036
|
+
iseq.getblockparam(index, level)
|
1037
|
+
when :getblockparamproxy
|
1038
|
+
iseq.getblockparamproxy(index, level)
|
1039
|
+
when :getlocal, :getlocal_WC_0, :getlocal_WC_1
|
1040
|
+
iseq.getlocal(index, level)
|
1041
|
+
when :setblockparam
|
1042
|
+
iseq.setblockparam(index, level)
|
1043
|
+
when :setlocal, :setlocal_WC_0, :setlocal_WC_1
|
1044
|
+
iseq.setlocal(index, level)
|
1045
|
+
end
|
1046
|
+
when :getclassvariable
|
1047
|
+
iseq.push(GetClassVariable.new(opnds[0], opnds[1]))
|
1048
|
+
when :getconstant
|
1049
|
+
iseq.getconstant(opnds[0])
|
1050
|
+
when :getglobal
|
1051
|
+
iseq.getglobal(opnds[0])
|
1052
|
+
when :getinstancevariable
|
1053
|
+
iseq.push(GetInstanceVariable.new(opnds[0], opnds[1]))
|
1054
|
+
when :getspecial
|
1055
|
+
iseq.getspecial(opnds[0], opnds[1])
|
1056
|
+
when :intern
|
1057
|
+
iseq.intern
|
1058
|
+
when :invokeblock
|
1059
|
+
iseq.invokeblock(CallData.from(opnds[0]))
|
1060
|
+
when :invokesuper
|
1061
|
+
block_iseq = opnds[1] ? from(opnds[1], options, iseq) : nil
|
1062
|
+
iseq.invokesuper(CallData.from(opnds[0]), block_iseq)
|
1063
|
+
when :jump
|
1064
|
+
iseq.jump(labels[opnds[0]])
|
1065
|
+
when :leave
|
1066
|
+
iseq.leave
|
1067
|
+
when :newarray
|
1068
|
+
iseq.newarray(opnds[0])
|
1069
|
+
when :newarraykwsplat
|
1070
|
+
iseq.newarraykwsplat(opnds[0])
|
1071
|
+
when :newhash
|
1072
|
+
iseq.newhash(opnds[0])
|
1073
|
+
when :newrange
|
1074
|
+
iseq.newrange(opnds[0])
|
1075
|
+
when :nop
|
1076
|
+
iseq.nop
|
1077
|
+
when :objtostring
|
1078
|
+
iseq.objtostring(CallData.from(opnds[0]))
|
1079
|
+
when :once
|
1080
|
+
iseq.once(from(opnds[0], options, iseq), opnds[1])
|
1081
|
+
when :opt_and, :opt_aref, :opt_aset, :opt_div, :opt_empty_p, :opt_eq,
|
1082
|
+
:opt_ge, :opt_gt, :opt_le, :opt_length, :opt_lt, :opt_ltlt,
|
1083
|
+
:opt_minus, :opt_mod, :opt_mult, :opt_nil_p, :opt_not, :opt_or,
|
1084
|
+
:opt_plus, :opt_regexpmatch2, :opt_send_without_block, :opt_size,
|
1085
|
+
:opt_succ
|
1086
|
+
iseq.send(CallData.from(opnds[0]), nil)
|
1087
|
+
when :opt_aref_with
|
1088
|
+
iseq.opt_aref_with(opnds[0], CallData.from(opnds[1]))
|
1089
|
+
when :opt_aset_with
|
1090
|
+
iseq.opt_aset_with(opnds[0], CallData.from(opnds[1]))
|
1091
|
+
when :opt_case_dispatch
|
1092
|
+
hash =
|
1093
|
+
opnds[0]
|
1094
|
+
.each_slice(2)
|
1095
|
+
.to_h
|
1096
|
+
.transform_values { |value| labels[value] }
|
1097
|
+
iseq.opt_case_dispatch(hash, labels[opnds[1]])
|
1098
|
+
when :opt_getconstant_path
|
1099
|
+
iseq.opt_getconstant_path(opnds[0])
|
1100
|
+
when :opt_getinlinecache
|
1101
|
+
iseq.opt_getinlinecache(labels[opnds[0]], opnds[1])
|
1102
|
+
when :opt_newarray_max
|
1103
|
+
iseq.newarray(opnds[0])
|
1104
|
+
iseq.send(YARV.calldata(:max))
|
1105
|
+
when :opt_newarray_min
|
1106
|
+
iseq.newarray(opnds[0])
|
1107
|
+
iseq.send(YARV.calldata(:min))
|
1108
|
+
when :opt_neq
|
1109
|
+
iseq.push(
|
1110
|
+
OptNEq.new(CallData.from(opnds[0]), CallData.from(opnds[1]))
|
1111
|
+
)
|
1112
|
+
when :opt_setinlinecache
|
1113
|
+
iseq.opt_setinlinecache(opnds[0])
|
1114
|
+
when :opt_str_freeze
|
1115
|
+
iseq.putstring(opnds[0])
|
1116
|
+
iseq.send(YARV.calldata(:freeze))
|
1117
|
+
when :opt_str_uminus
|
1118
|
+
iseq.putstring(opnds[0])
|
1119
|
+
iseq.send(YARV.calldata(:-@))
|
1120
|
+
when :pop
|
1121
|
+
iseq.pop
|
1122
|
+
when :putnil
|
1123
|
+
iseq.putnil
|
1124
|
+
when :putobject
|
1125
|
+
iseq.putobject(opnds[0])
|
1126
|
+
when :putobject_INT2FIX_0_
|
1127
|
+
iseq.putobject(0)
|
1128
|
+
when :putobject_INT2FIX_1_
|
1129
|
+
iseq.putobject(1)
|
1130
|
+
when :putself
|
1131
|
+
iseq.putself
|
1132
|
+
when :putstring
|
1133
|
+
iseq.putstring(opnds[0])
|
1134
|
+
when :putspecialobject
|
1135
|
+
iseq.putspecialobject(opnds[0])
|
1136
|
+
when :send
|
1137
|
+
block_iseq = opnds[1] ? from(opnds[1], options, iseq) : nil
|
1138
|
+
iseq.send(CallData.from(opnds[0]), block_iseq)
|
1139
|
+
when :setclassvariable
|
1140
|
+
iseq.push(SetClassVariable.new(opnds[0], opnds[1]))
|
1141
|
+
when :setconstant
|
1142
|
+
iseq.setconstant(opnds[0])
|
1143
|
+
when :setglobal
|
1144
|
+
iseq.setglobal(opnds[0])
|
1145
|
+
when :setinstancevariable
|
1146
|
+
iseq.push(SetInstanceVariable.new(opnds[0], opnds[1]))
|
1147
|
+
when :setn
|
1148
|
+
iseq.setn(opnds[0])
|
1149
|
+
when :setspecial
|
1150
|
+
iseq.setspecial(opnds[0])
|
1151
|
+
when :splatarray
|
1152
|
+
iseq.splatarray(opnds[0])
|
1153
|
+
when :swap
|
1154
|
+
iseq.swap
|
1155
|
+
when :throw
|
1156
|
+
iseq.throw(opnds[0])
|
1157
|
+
when :topn
|
1158
|
+
iseq.topn(opnds[0])
|
1159
|
+
when :toregexp
|
1160
|
+
iseq.toregexp(opnds[0], opnds[1])
|
1161
|
+
else
|
1162
|
+
raise "Unknown instruction type: #{type}"
|
1163
|
+
end
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
iseq.compile! if iseq.type == :top
|
1167
|
+
iseq
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
end
|