syntax_tree 5.0.1 → 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 +17 -1
- data/Gemfile.lock +9 -9
- data/README.md +5 -5
- 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 +19 -1
- data/syntax_tree.gemspec +1 -1
- metadata +15 -4
@@ -0,0 +1,5203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
module YARV
|
5
|
+
# This is an operand to various YARV instructions that represents the
|
6
|
+
# information about a specific call site.
|
7
|
+
class CallData
|
8
|
+
CALL_ARGS_SPLAT = 1 << 0
|
9
|
+
CALL_ARGS_BLOCKARG = 1 << 1
|
10
|
+
CALL_FCALL = 1 << 2
|
11
|
+
CALL_VCALL = 1 << 3
|
12
|
+
CALL_ARGS_SIMPLE = 1 << 4
|
13
|
+
CALL_BLOCKISEQ = 1 << 5
|
14
|
+
CALL_KWARG = 1 << 6
|
15
|
+
CALL_KW_SPLAT = 1 << 7
|
16
|
+
CALL_TAILCALL = 1 << 8
|
17
|
+
CALL_SUPER = 1 << 9
|
18
|
+
CALL_ZSUPER = 1 << 10
|
19
|
+
CALL_OPT_SEND = 1 << 11
|
20
|
+
CALL_KW_SPLAT_MUT = 1 << 12
|
21
|
+
|
22
|
+
attr_reader :method, :argc, :flags, :kw_arg
|
23
|
+
|
24
|
+
def initialize(
|
25
|
+
method,
|
26
|
+
argc = 0,
|
27
|
+
flags = CallData::CALL_ARGS_SIMPLE,
|
28
|
+
kw_arg = nil
|
29
|
+
)
|
30
|
+
@method = method
|
31
|
+
@argc = argc
|
32
|
+
@flags = flags
|
33
|
+
@kw_arg = kw_arg
|
34
|
+
end
|
35
|
+
|
36
|
+
def flag?(mask)
|
37
|
+
(flags & mask) > 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_h
|
41
|
+
result = { mid: method, flag: flags, orig_argc: argc }
|
42
|
+
result[:kw_arg] = kw_arg if kw_arg
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.from(serialized)
|
47
|
+
new(
|
48
|
+
serialized[:mid],
|
49
|
+
serialized[:orig_argc],
|
50
|
+
serialized[:flag],
|
51
|
+
serialized[:kw_arg]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# A convenience method for creating a CallData object.
|
57
|
+
def self.calldata(
|
58
|
+
method,
|
59
|
+
argc = 0,
|
60
|
+
flags = CallData::CALL_ARGS_SIMPLE,
|
61
|
+
kw_arg = nil
|
62
|
+
)
|
63
|
+
CallData.new(method, argc, flags, kw_arg)
|
64
|
+
end
|
65
|
+
|
66
|
+
# ### Summary
|
67
|
+
#
|
68
|
+
# `adjuststack` accepts a single integer argument and removes that many
|
69
|
+
# elements from the top of the stack.
|
70
|
+
#
|
71
|
+
# ### Usage
|
72
|
+
#
|
73
|
+
# ~~~ruby
|
74
|
+
# x = [true]
|
75
|
+
# x[0] ||= nil
|
76
|
+
# x[0]
|
77
|
+
# ~~~
|
78
|
+
#
|
79
|
+
class AdjustStack
|
80
|
+
attr_reader :number
|
81
|
+
|
82
|
+
def initialize(number)
|
83
|
+
@number = number
|
84
|
+
end
|
85
|
+
|
86
|
+
def disasm(fmt)
|
87
|
+
fmt.instruction("adjuststack", [fmt.object(number)])
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_a(_iseq)
|
91
|
+
[:adjuststack, number]
|
92
|
+
end
|
93
|
+
|
94
|
+
def length
|
95
|
+
2
|
96
|
+
end
|
97
|
+
|
98
|
+
def pops
|
99
|
+
number
|
100
|
+
end
|
101
|
+
|
102
|
+
def pushes
|
103
|
+
0
|
104
|
+
end
|
105
|
+
|
106
|
+
def canonical
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def call(vm)
|
111
|
+
vm.pop(number)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# ### Summary
|
116
|
+
#
|
117
|
+
# `anytostring` ensures that the value on top of the stack is a string.
|
118
|
+
#
|
119
|
+
# It pops two values off the stack. If the first value is a string it
|
120
|
+
# pushes it back on the stack. If the first value is not a string, it uses
|
121
|
+
# Ruby's built in string coercion to coerce the second value to a string
|
122
|
+
# and then pushes that back on the stack.
|
123
|
+
#
|
124
|
+
# This is used in conjunction with `objtostring` as a fallback for when an
|
125
|
+
# object's `to_s` method does not return a string.
|
126
|
+
#
|
127
|
+
# ### Usage
|
128
|
+
#
|
129
|
+
# ~~~ruby
|
130
|
+
# "#{5}"
|
131
|
+
# ~~~
|
132
|
+
#
|
133
|
+
class AnyToString
|
134
|
+
def disasm(fmt)
|
135
|
+
fmt.instruction("anytostring")
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_a(_iseq)
|
139
|
+
[:anytostring]
|
140
|
+
end
|
141
|
+
|
142
|
+
def length
|
143
|
+
1
|
144
|
+
end
|
145
|
+
|
146
|
+
def pops
|
147
|
+
2
|
148
|
+
end
|
149
|
+
|
150
|
+
def pushes
|
151
|
+
1
|
152
|
+
end
|
153
|
+
|
154
|
+
def canonical
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def call(vm)
|
159
|
+
original, value = vm.pop(2)
|
160
|
+
|
161
|
+
if value.is_a?(String)
|
162
|
+
vm.push(value)
|
163
|
+
else
|
164
|
+
vm.push("#<#{original.class.name}:0000>")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# ### Summary
|
170
|
+
#
|
171
|
+
# `branchif` has one argument: the jump index. It pops one value off the
|
172
|
+
# stack: the jump condition.
|
173
|
+
#
|
174
|
+
# If the value popped off the stack is true, `branchif` jumps to
|
175
|
+
# the jump index and continues executing there.
|
176
|
+
#
|
177
|
+
# ### Usage
|
178
|
+
#
|
179
|
+
# ~~~ruby
|
180
|
+
# x = true
|
181
|
+
# x ||= "foo"
|
182
|
+
# puts x
|
183
|
+
# ~~~
|
184
|
+
#
|
185
|
+
class BranchIf
|
186
|
+
attr_reader :label
|
187
|
+
|
188
|
+
def initialize(label)
|
189
|
+
@label = label
|
190
|
+
end
|
191
|
+
|
192
|
+
def disasm(fmt)
|
193
|
+
fmt.instruction("branchif", [fmt.label(label)])
|
194
|
+
end
|
195
|
+
|
196
|
+
def to_a(_iseq)
|
197
|
+
[:branchif, label.name]
|
198
|
+
end
|
199
|
+
|
200
|
+
def length
|
201
|
+
2
|
202
|
+
end
|
203
|
+
|
204
|
+
def pops
|
205
|
+
1
|
206
|
+
end
|
207
|
+
|
208
|
+
def pushes
|
209
|
+
0
|
210
|
+
end
|
211
|
+
|
212
|
+
def canonical
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
def call(vm)
|
217
|
+
vm.jump(label) if vm.pop
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# ### Summary
|
222
|
+
#
|
223
|
+
# `branchnil` has one argument: the jump index. It pops one value off the
|
224
|
+
# stack: the jump condition.
|
225
|
+
#
|
226
|
+
# If the value popped off the stack is nil, `branchnil` jumps to
|
227
|
+
# the jump index and continues executing there.
|
228
|
+
#
|
229
|
+
# ### Usage
|
230
|
+
#
|
231
|
+
# ~~~ruby
|
232
|
+
# x = nil
|
233
|
+
# if x&.to_s
|
234
|
+
# puts "hi"
|
235
|
+
# end
|
236
|
+
# ~~~
|
237
|
+
#
|
238
|
+
class BranchNil
|
239
|
+
attr_reader :label
|
240
|
+
|
241
|
+
def initialize(label)
|
242
|
+
@label = label
|
243
|
+
end
|
244
|
+
|
245
|
+
def disasm(fmt)
|
246
|
+
fmt.instruction("branchnil", [fmt.label(label)])
|
247
|
+
end
|
248
|
+
|
249
|
+
def to_a(_iseq)
|
250
|
+
[:branchnil, label.name]
|
251
|
+
end
|
252
|
+
|
253
|
+
def length
|
254
|
+
2
|
255
|
+
end
|
256
|
+
|
257
|
+
def pops
|
258
|
+
1
|
259
|
+
end
|
260
|
+
|
261
|
+
def pushes
|
262
|
+
0
|
263
|
+
end
|
264
|
+
|
265
|
+
def canonical
|
266
|
+
self
|
267
|
+
end
|
268
|
+
|
269
|
+
def call(vm)
|
270
|
+
vm.jump(label) if vm.pop.nil?
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# ### Summary
|
275
|
+
#
|
276
|
+
# `branchunless` has one argument: the jump index. It pops one value off
|
277
|
+
# the stack: the jump condition.
|
278
|
+
#
|
279
|
+
# If the value popped off the stack is false or nil, `branchunless` jumps
|
280
|
+
# to the jump index and continues executing there.
|
281
|
+
#
|
282
|
+
# ### Usage
|
283
|
+
#
|
284
|
+
# ~~~ruby
|
285
|
+
# if 2 + 3
|
286
|
+
# puts "foo"
|
287
|
+
# end
|
288
|
+
# ~~~
|
289
|
+
#
|
290
|
+
class BranchUnless
|
291
|
+
attr_reader :label
|
292
|
+
|
293
|
+
def initialize(label)
|
294
|
+
@label = label
|
295
|
+
end
|
296
|
+
|
297
|
+
def disasm(fmt)
|
298
|
+
fmt.instruction("branchunless", [fmt.label(label)])
|
299
|
+
end
|
300
|
+
|
301
|
+
def to_a(_iseq)
|
302
|
+
[:branchunless, label.name]
|
303
|
+
end
|
304
|
+
|
305
|
+
def length
|
306
|
+
2
|
307
|
+
end
|
308
|
+
|
309
|
+
def pops
|
310
|
+
1
|
311
|
+
end
|
312
|
+
|
313
|
+
def pushes
|
314
|
+
0
|
315
|
+
end
|
316
|
+
|
317
|
+
def canonical
|
318
|
+
self
|
319
|
+
end
|
320
|
+
|
321
|
+
def call(vm)
|
322
|
+
vm.jump(label) unless vm.pop
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# ### Summary
|
327
|
+
#
|
328
|
+
# `checkkeyword` checks if a keyword was passed at the callsite that
|
329
|
+
# called into the method represented by the instruction sequence. It has
|
330
|
+
# two arguments: the index of the local variable that stores the keywords
|
331
|
+
# metadata and the index of the keyword within that metadata. It pushes
|
332
|
+
# a boolean onto the stack indicating whether or not the keyword was
|
333
|
+
# given.
|
334
|
+
#
|
335
|
+
# ### Usage
|
336
|
+
#
|
337
|
+
# ~~~ruby
|
338
|
+
# def evaluate(value: rand)
|
339
|
+
# value
|
340
|
+
# end
|
341
|
+
#
|
342
|
+
# evaluate(value: 3)
|
343
|
+
# ~~~
|
344
|
+
#
|
345
|
+
class CheckKeyword
|
346
|
+
attr_reader :keyword_bits_index, :keyword_index
|
347
|
+
|
348
|
+
def initialize(keyword_bits_index, keyword_index)
|
349
|
+
@keyword_bits_index = keyword_bits_index
|
350
|
+
@keyword_index = keyword_index
|
351
|
+
end
|
352
|
+
|
353
|
+
def disasm(fmt)
|
354
|
+
fmt.instruction(
|
355
|
+
"checkkeyword",
|
356
|
+
[fmt.object(keyword_bits_index), fmt.object(keyword_index)]
|
357
|
+
)
|
358
|
+
end
|
359
|
+
|
360
|
+
def to_a(iseq)
|
361
|
+
[
|
362
|
+
:checkkeyword,
|
363
|
+
iseq.local_table.offset(keyword_bits_index),
|
364
|
+
keyword_index
|
365
|
+
]
|
366
|
+
end
|
367
|
+
|
368
|
+
def length
|
369
|
+
3
|
370
|
+
end
|
371
|
+
|
372
|
+
def pops
|
373
|
+
0
|
374
|
+
end
|
375
|
+
|
376
|
+
def pushes
|
377
|
+
1
|
378
|
+
end
|
379
|
+
|
380
|
+
def canonical
|
381
|
+
self
|
382
|
+
end
|
383
|
+
|
384
|
+
def call(vm)
|
385
|
+
vm.push(vm.local_get(keyword_bits_index, 0)[keyword_index])
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# ### Summary
|
390
|
+
#
|
391
|
+
# `checkmatch` checks if the current pattern matches the current value. It
|
392
|
+
# pops the target and the pattern off the stack and pushes a boolean onto
|
393
|
+
# the stack if it matches or not.
|
394
|
+
#
|
395
|
+
# ### Usage
|
396
|
+
#
|
397
|
+
# ~~~ruby
|
398
|
+
# foo in Foo
|
399
|
+
# ~~~
|
400
|
+
#
|
401
|
+
class CheckMatch
|
402
|
+
TYPE_WHEN = 1
|
403
|
+
TYPE_CASE = 2
|
404
|
+
TYPE_RESCUE = 3
|
405
|
+
|
406
|
+
attr_reader :type
|
407
|
+
|
408
|
+
def initialize(type)
|
409
|
+
@type = type
|
410
|
+
end
|
411
|
+
|
412
|
+
def disasm(fmt)
|
413
|
+
fmt.instruction("checkmatch", [fmt.object(type)])
|
414
|
+
end
|
415
|
+
|
416
|
+
def to_a(_iseq)
|
417
|
+
[:checkmatch, type]
|
418
|
+
end
|
419
|
+
|
420
|
+
def length
|
421
|
+
2
|
422
|
+
end
|
423
|
+
|
424
|
+
def pops
|
425
|
+
2
|
426
|
+
end
|
427
|
+
|
428
|
+
def pushes
|
429
|
+
1
|
430
|
+
end
|
431
|
+
|
432
|
+
def canonical
|
433
|
+
self
|
434
|
+
end
|
435
|
+
|
436
|
+
def call(vm)
|
437
|
+
raise NotImplementedError, "checkmatch"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# ### Summary
|
442
|
+
#
|
443
|
+
# `checktype` checks if the value on top of the stack is of a certain type.
|
444
|
+
# The type is the only argument. It pops the value off the stack and pushes
|
445
|
+
# a boolean onto the stack indicating whether or not the value is of the
|
446
|
+
# given type.
|
447
|
+
#
|
448
|
+
# ### Usage
|
449
|
+
#
|
450
|
+
# ~~~ruby
|
451
|
+
# foo in [bar]
|
452
|
+
# ~~~
|
453
|
+
#
|
454
|
+
class CheckType
|
455
|
+
TYPE_OBJECT = 0x01
|
456
|
+
TYPE_CLASS = 0x02
|
457
|
+
TYPE_MODULE = 0x03
|
458
|
+
TYPE_FLOAT = 0x04
|
459
|
+
TYPE_STRING = 0x05
|
460
|
+
TYPE_REGEXP = 0x06
|
461
|
+
TYPE_ARRAY = 0x07
|
462
|
+
TYPE_HASH = 0x08
|
463
|
+
TYPE_STRUCT = 0x09
|
464
|
+
TYPE_BIGNUM = 0x0a
|
465
|
+
TYPE_FILE = 0x0b
|
466
|
+
TYPE_DATA = 0x0c
|
467
|
+
TYPE_MATCH = 0x0d
|
468
|
+
TYPE_COMPLEX = 0x0e
|
469
|
+
TYPE_RATIONAL = 0x0f
|
470
|
+
TYPE_NIL = 0x11
|
471
|
+
TYPE_TRUE = 0x12
|
472
|
+
TYPE_FALSE = 0x13
|
473
|
+
TYPE_SYMBOL = 0x14
|
474
|
+
TYPE_FIXNUM = 0x15
|
475
|
+
TYPE_UNDEF = 0x16
|
476
|
+
|
477
|
+
attr_reader :type
|
478
|
+
|
479
|
+
def initialize(type)
|
480
|
+
@type = type
|
481
|
+
end
|
482
|
+
|
483
|
+
def disasm(fmt)
|
484
|
+
name =
|
485
|
+
case type
|
486
|
+
when TYPE_OBJECT
|
487
|
+
"T_OBJECT"
|
488
|
+
when TYPE_CLASS
|
489
|
+
"T_CLASS"
|
490
|
+
when TYPE_MODULE
|
491
|
+
"T_MODULE"
|
492
|
+
when TYPE_FLOAT
|
493
|
+
"T_FLOAT"
|
494
|
+
when TYPE_STRING
|
495
|
+
"T_STRING"
|
496
|
+
when TYPE_REGEXP
|
497
|
+
"T_REGEXP"
|
498
|
+
when TYPE_ARRAY
|
499
|
+
"T_ARRAY"
|
500
|
+
when TYPE_HASH
|
501
|
+
"T_HASH"
|
502
|
+
when TYPE_STRUCT
|
503
|
+
"T_STRUCT"
|
504
|
+
when TYPE_BIGNUM
|
505
|
+
"T_BIGNUM"
|
506
|
+
when TYPE_FILE
|
507
|
+
"T_FILE"
|
508
|
+
when TYPE_DATA
|
509
|
+
"T_DATA"
|
510
|
+
when TYPE_MATCH
|
511
|
+
"T_MATCH"
|
512
|
+
when TYPE_COMPLEX
|
513
|
+
"T_COMPLEX"
|
514
|
+
when TYPE_RATIONAL
|
515
|
+
"T_RATIONAL"
|
516
|
+
when TYPE_NIL
|
517
|
+
"T_NIL"
|
518
|
+
when TYPE_TRUE
|
519
|
+
"T_TRUE"
|
520
|
+
when TYPE_FALSE
|
521
|
+
"T_FALSE"
|
522
|
+
when TYPE_SYMBOL
|
523
|
+
"T_SYMBOL"
|
524
|
+
when TYPE_FIXNUM
|
525
|
+
"T_FIXNUM"
|
526
|
+
when TYPE_UNDEF
|
527
|
+
"T_UNDEF"
|
528
|
+
end
|
529
|
+
|
530
|
+
fmt.instruction("checktype", [name])
|
531
|
+
end
|
532
|
+
|
533
|
+
def to_a(_iseq)
|
534
|
+
[:checktype, type]
|
535
|
+
end
|
536
|
+
|
537
|
+
def length
|
538
|
+
2
|
539
|
+
end
|
540
|
+
|
541
|
+
def pops
|
542
|
+
1
|
543
|
+
end
|
544
|
+
|
545
|
+
def pushes
|
546
|
+
# TODO: This is incorrect. The instruction only pushes a single value
|
547
|
+
# onto the stack. However, if this is set to 1, we no longer match the
|
548
|
+
# output of RubyVM::InstructionSequence. So leaving this here until we
|
549
|
+
# can investigate further.
|
550
|
+
2
|
551
|
+
end
|
552
|
+
|
553
|
+
def canonical
|
554
|
+
self
|
555
|
+
end
|
556
|
+
|
557
|
+
def call(vm)
|
558
|
+
object = vm.pop
|
559
|
+
result =
|
560
|
+
case type
|
561
|
+
when TYPE_OBJECT
|
562
|
+
raise NotImplementedError, "checktype TYPE_OBJECT"
|
563
|
+
when TYPE_CLASS
|
564
|
+
object.is_a?(Class)
|
565
|
+
when TYPE_MODULE
|
566
|
+
object.is_a?(Module)
|
567
|
+
when TYPE_FLOAT
|
568
|
+
object.is_a?(Float)
|
569
|
+
when TYPE_STRING
|
570
|
+
object.is_a?(String)
|
571
|
+
when TYPE_REGEXP
|
572
|
+
object.is_a?(Regexp)
|
573
|
+
when TYPE_ARRAY
|
574
|
+
object.is_a?(Array)
|
575
|
+
when TYPE_HASH
|
576
|
+
object.is_a?(Hash)
|
577
|
+
when TYPE_STRUCT
|
578
|
+
object.is_a?(Struct)
|
579
|
+
when TYPE_BIGNUM
|
580
|
+
raise NotImplementedError, "checktype TYPE_BIGNUM"
|
581
|
+
when TYPE_FILE
|
582
|
+
object.is_a?(File)
|
583
|
+
when TYPE_DATA
|
584
|
+
raise NotImplementedError, "checktype TYPE_DATA"
|
585
|
+
when TYPE_MATCH
|
586
|
+
raise NotImplementedError, "checktype TYPE_MATCH"
|
587
|
+
when TYPE_COMPLEX
|
588
|
+
object.is_a?(Complex)
|
589
|
+
when TYPE_RATIONAL
|
590
|
+
object.is_a?(Rational)
|
591
|
+
when TYPE_NIL
|
592
|
+
object.nil?
|
593
|
+
when TYPE_TRUE
|
594
|
+
object == true
|
595
|
+
when TYPE_FALSE
|
596
|
+
object == false
|
597
|
+
when TYPE_SYMBOL
|
598
|
+
object.is_a?(Symbol)
|
599
|
+
when TYPE_FIXNUM
|
600
|
+
object.is_a?(Integer)
|
601
|
+
when TYPE_UNDEF
|
602
|
+
raise NotImplementedError, "checktype TYPE_UNDEF"
|
603
|
+
end
|
604
|
+
|
605
|
+
vm.push(result)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
# ### Summary
|
610
|
+
#
|
611
|
+
# `concatarray` concatenates the two Arrays on top of the stack.
|
612
|
+
#
|
613
|
+
# It coerces the two objects at the top of the stack into Arrays by
|
614
|
+
# calling `to_a` if necessary, and makes sure to `dup` the first Array if
|
615
|
+
# it was already an Array, to avoid mutating it when concatenating.
|
616
|
+
#
|
617
|
+
# ### Usage
|
618
|
+
#
|
619
|
+
# ~~~ruby
|
620
|
+
# [1, *2]
|
621
|
+
# ~~~
|
622
|
+
#
|
623
|
+
class ConcatArray
|
624
|
+
def disasm(fmt)
|
625
|
+
fmt.instruction("concatarray")
|
626
|
+
end
|
627
|
+
|
628
|
+
def to_a(_iseq)
|
629
|
+
[:concatarray]
|
630
|
+
end
|
631
|
+
|
632
|
+
def length
|
633
|
+
1
|
634
|
+
end
|
635
|
+
|
636
|
+
def pops
|
637
|
+
2
|
638
|
+
end
|
639
|
+
|
640
|
+
def pushes
|
641
|
+
1
|
642
|
+
end
|
643
|
+
|
644
|
+
def canonical
|
645
|
+
self
|
646
|
+
end
|
647
|
+
|
648
|
+
def call(vm)
|
649
|
+
left, right = vm.pop(2)
|
650
|
+
vm.push([*left, *right])
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
# ### Summary
|
655
|
+
#
|
656
|
+
# `concatstrings` pops a number of strings from the stack joins them
|
657
|
+
# together into a single string and pushes that string back on the stack.
|
658
|
+
#
|
659
|
+
# This does no coercion and so is always used in conjunction with
|
660
|
+
# `objtostring` and `anytostring` to ensure the stack contents are always
|
661
|
+
# strings.
|
662
|
+
#
|
663
|
+
# ### Usage
|
664
|
+
#
|
665
|
+
# ~~~ruby
|
666
|
+
# "#{5}"
|
667
|
+
# ~~~
|
668
|
+
#
|
669
|
+
class ConcatStrings
|
670
|
+
attr_reader :number
|
671
|
+
|
672
|
+
def initialize(number)
|
673
|
+
@number = number
|
674
|
+
end
|
675
|
+
|
676
|
+
def disasm(fmt)
|
677
|
+
fmt.instruction("concatstrings", [fmt.object(number)])
|
678
|
+
end
|
679
|
+
|
680
|
+
def to_a(_iseq)
|
681
|
+
[:concatstrings, number]
|
682
|
+
end
|
683
|
+
|
684
|
+
def length
|
685
|
+
2
|
686
|
+
end
|
687
|
+
|
688
|
+
def pops
|
689
|
+
number
|
690
|
+
end
|
691
|
+
|
692
|
+
def pushes
|
693
|
+
1
|
694
|
+
end
|
695
|
+
|
696
|
+
def canonical
|
697
|
+
self
|
698
|
+
end
|
699
|
+
|
700
|
+
def call(vm)
|
701
|
+
vm.push(vm.pop(number).join)
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
# ### Summary
|
706
|
+
#
|
707
|
+
# `defineclass` defines a class. First it pops the superclass off the
|
708
|
+
# stack, then it pops the object off the stack that the class should be
|
709
|
+
# defined under. It has three arguments: the name of the constant, the
|
710
|
+
# instruction sequence associated with the class, and various flags that
|
711
|
+
# indicate if it is a singleton class, a module, or a regular class.
|
712
|
+
#
|
713
|
+
# ### Usage
|
714
|
+
#
|
715
|
+
# ~~~ruby
|
716
|
+
# class Foo
|
717
|
+
# end
|
718
|
+
# ~~~
|
719
|
+
#
|
720
|
+
class DefineClass
|
721
|
+
TYPE_CLASS = 0
|
722
|
+
TYPE_SINGLETON_CLASS = 1
|
723
|
+
TYPE_MODULE = 2
|
724
|
+
FLAG_SCOPED = 8
|
725
|
+
FLAG_HAS_SUPERCLASS = 16
|
726
|
+
|
727
|
+
attr_reader :name, :class_iseq, :flags
|
728
|
+
|
729
|
+
def initialize(name, class_iseq, flags)
|
730
|
+
@name = name
|
731
|
+
@class_iseq = class_iseq
|
732
|
+
@flags = flags
|
733
|
+
end
|
734
|
+
|
735
|
+
def disasm(fmt)
|
736
|
+
fmt.enqueue(class_iseq)
|
737
|
+
fmt.instruction(
|
738
|
+
"defineclass",
|
739
|
+
[fmt.object(name), class_iseq.name, fmt.object(flags)]
|
740
|
+
)
|
741
|
+
end
|
742
|
+
|
743
|
+
def to_a(_iseq)
|
744
|
+
[:defineclass, name, class_iseq.to_a, flags]
|
745
|
+
end
|
746
|
+
|
747
|
+
def length
|
748
|
+
4
|
749
|
+
end
|
750
|
+
|
751
|
+
def pops
|
752
|
+
2
|
753
|
+
end
|
754
|
+
|
755
|
+
def pushes
|
756
|
+
1
|
757
|
+
end
|
758
|
+
|
759
|
+
def canonical
|
760
|
+
self
|
761
|
+
end
|
762
|
+
|
763
|
+
def call(vm)
|
764
|
+
object, superclass = vm.pop(2)
|
765
|
+
iseq = class_iseq
|
766
|
+
|
767
|
+
clazz = Class.new(superclass || Object)
|
768
|
+
vm.push(vm.run_class_frame(iseq, clazz))
|
769
|
+
|
770
|
+
object.const_set(name, clazz)
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
# ### Summary
|
775
|
+
#
|
776
|
+
# `defined` checks if the top value of the stack is defined. If it is, it
|
777
|
+
# pushes its value onto the stack. Otherwise it pushes `nil`.
|
778
|
+
#
|
779
|
+
# ### Usage
|
780
|
+
#
|
781
|
+
# ~~~ruby
|
782
|
+
# defined?(x)
|
783
|
+
# ~~~
|
784
|
+
#
|
785
|
+
class Defined
|
786
|
+
TYPE_NIL = 1
|
787
|
+
TYPE_IVAR = 2
|
788
|
+
TYPE_LVAR = 3
|
789
|
+
TYPE_GVAR = 4
|
790
|
+
TYPE_CVAR = 5
|
791
|
+
TYPE_CONST = 6
|
792
|
+
TYPE_METHOD = 7
|
793
|
+
TYPE_YIELD = 8
|
794
|
+
TYPE_ZSUPER = 9
|
795
|
+
TYPE_SELF = 10
|
796
|
+
TYPE_TRUE = 11
|
797
|
+
TYPE_FALSE = 12
|
798
|
+
TYPE_ASGN = 13
|
799
|
+
TYPE_EXPR = 14
|
800
|
+
TYPE_REF = 15
|
801
|
+
TYPE_FUNC = 16
|
802
|
+
TYPE_CONST_FROM = 17
|
803
|
+
|
804
|
+
attr_reader :type, :name, :message
|
805
|
+
|
806
|
+
def initialize(type, name, message)
|
807
|
+
@type = type
|
808
|
+
@name = name
|
809
|
+
@message = message
|
810
|
+
end
|
811
|
+
|
812
|
+
def disasm(fmt)
|
813
|
+
type_name =
|
814
|
+
case type
|
815
|
+
when TYPE_NIL
|
816
|
+
"nil"
|
817
|
+
when TYPE_IVAR
|
818
|
+
"ivar"
|
819
|
+
when TYPE_LVAR
|
820
|
+
"lvar"
|
821
|
+
when TYPE_GVAR
|
822
|
+
"gvar"
|
823
|
+
when TYPE_CVAR
|
824
|
+
"cvar"
|
825
|
+
when TYPE_CONST
|
826
|
+
"const"
|
827
|
+
when TYPE_METHOD
|
828
|
+
"method"
|
829
|
+
when TYPE_YIELD
|
830
|
+
"yield"
|
831
|
+
when TYPE_ZSUPER
|
832
|
+
"zsuper"
|
833
|
+
when TYPE_SELF
|
834
|
+
"self"
|
835
|
+
when TYPE_TRUE
|
836
|
+
"true"
|
837
|
+
when TYPE_FALSE
|
838
|
+
"false"
|
839
|
+
when TYPE_ASGN
|
840
|
+
"asgn"
|
841
|
+
when TYPE_EXPR
|
842
|
+
"expr"
|
843
|
+
when TYPE_REF
|
844
|
+
"ref"
|
845
|
+
when TYPE_FUNC
|
846
|
+
"func"
|
847
|
+
when TYPE_CONST_FROM
|
848
|
+
"constant-from"
|
849
|
+
end
|
850
|
+
|
851
|
+
fmt.instruction(
|
852
|
+
"defined",
|
853
|
+
[type_name, fmt.object(name), fmt.object(message)]
|
854
|
+
)
|
855
|
+
end
|
856
|
+
|
857
|
+
def to_a(_iseq)
|
858
|
+
[:defined, type, name, message]
|
859
|
+
end
|
860
|
+
|
861
|
+
def length
|
862
|
+
4
|
863
|
+
end
|
864
|
+
|
865
|
+
def pops
|
866
|
+
1
|
867
|
+
end
|
868
|
+
|
869
|
+
def pushes
|
870
|
+
1
|
871
|
+
end
|
872
|
+
|
873
|
+
def canonical
|
874
|
+
self
|
875
|
+
end
|
876
|
+
|
877
|
+
def call(vm)
|
878
|
+
object = vm.pop
|
879
|
+
|
880
|
+
result =
|
881
|
+
case type
|
882
|
+
when TYPE_NIL, TYPE_SELF, TYPE_TRUE, TYPE_FALSE, TYPE_ASGN, TYPE_EXPR
|
883
|
+
message
|
884
|
+
when TYPE_IVAR
|
885
|
+
message if vm._self.instance_variable_defined?(name)
|
886
|
+
when TYPE_LVAR
|
887
|
+
raise NotImplementedError, "defined TYPE_LVAR"
|
888
|
+
when TYPE_GVAR
|
889
|
+
message if global_variables.include?(name)
|
890
|
+
when TYPE_CVAR
|
891
|
+
clazz = vm._self
|
892
|
+
clazz = clazz.singleton_class unless clazz.is_a?(Module)
|
893
|
+
message if clazz.class_variable_defined?(name)
|
894
|
+
when TYPE_CONST
|
895
|
+
raise NotImplementedError, "defined TYPE_CONST"
|
896
|
+
when TYPE_METHOD
|
897
|
+
raise NotImplementedError, "defined TYPE_METHOD"
|
898
|
+
when TYPE_YIELD
|
899
|
+
raise NotImplementedError, "defined TYPE_YIELD"
|
900
|
+
when TYPE_ZSUPER
|
901
|
+
raise NotImplementedError, "defined TYPE_ZSUPER"
|
902
|
+
when TYPE_REF
|
903
|
+
raise NotImplementedError, "defined TYPE_REF"
|
904
|
+
when TYPE_FUNC
|
905
|
+
message if object.respond_to?(name, true)
|
906
|
+
when TYPE_CONST_FROM
|
907
|
+
raise NotImplementedError, "defined TYPE_CONST_FROM"
|
908
|
+
end
|
909
|
+
|
910
|
+
vm.push(result)
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
# ### Summary
|
915
|
+
#
|
916
|
+
# `definemethod` defines a method on the class of the current value of
|
917
|
+
# `self`. It accepts two arguments. The first is the name of the method
|
918
|
+
# being defined. The second is the instruction sequence representing the
|
919
|
+
# body of the method.
|
920
|
+
#
|
921
|
+
# ### Usage
|
922
|
+
#
|
923
|
+
# ~~~ruby
|
924
|
+
# def value = "value"
|
925
|
+
# ~~~
|
926
|
+
#
|
927
|
+
class DefineMethod
|
928
|
+
attr_reader :method_name, :method_iseq
|
929
|
+
|
930
|
+
def initialize(method_name, method_iseq)
|
931
|
+
@method_name = method_name
|
932
|
+
@method_iseq = method_iseq
|
933
|
+
end
|
934
|
+
|
935
|
+
def disasm(fmt)
|
936
|
+
fmt.enqueue(method_iseq)
|
937
|
+
fmt.instruction(
|
938
|
+
"definemethod",
|
939
|
+
[fmt.object(method_name), method_iseq.name]
|
940
|
+
)
|
941
|
+
end
|
942
|
+
|
943
|
+
def to_a(_iseq)
|
944
|
+
[:definemethod, method_name, method_iseq.to_a]
|
945
|
+
end
|
946
|
+
|
947
|
+
def length
|
948
|
+
3
|
949
|
+
end
|
950
|
+
|
951
|
+
def pops
|
952
|
+
0
|
953
|
+
end
|
954
|
+
|
955
|
+
def pushes
|
956
|
+
0
|
957
|
+
end
|
958
|
+
|
959
|
+
def canonical
|
960
|
+
self
|
961
|
+
end
|
962
|
+
|
963
|
+
def call(vm)
|
964
|
+
name = method_name
|
965
|
+
iseq = method_iseq
|
966
|
+
|
967
|
+
vm
|
968
|
+
._self
|
969
|
+
.__send__(:define_method, name) do |*args, **kwargs, &block|
|
970
|
+
vm.run_method_frame(name, iseq, self, *args, **kwargs, &block)
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
# ### Summary
|
976
|
+
#
|
977
|
+
# `definesmethod` defines a method on the singleton class of the current
|
978
|
+
# value of `self`. It accepts two arguments. The first is the name of the
|
979
|
+
# method being defined. The second is the instruction sequence representing
|
980
|
+
# the body of the method. It pops the object off the stack that the method
|
981
|
+
# should be defined on.
|
982
|
+
#
|
983
|
+
# ### Usage
|
984
|
+
#
|
985
|
+
# ~~~ruby
|
986
|
+
# def self.value = "value"
|
987
|
+
# ~~~
|
988
|
+
#
|
989
|
+
class DefineSMethod
|
990
|
+
attr_reader :method_name, :method_iseq
|
991
|
+
|
992
|
+
def initialize(method_name, method_iseq)
|
993
|
+
@method_name = method_name
|
994
|
+
@method_iseq = method_iseq
|
995
|
+
end
|
996
|
+
|
997
|
+
def disasm(fmt)
|
998
|
+
fmt.enqueue(method_iseq)
|
999
|
+
fmt.instruction(
|
1000
|
+
"definesmethod",
|
1001
|
+
[fmt.object(method_name), method_iseq.name]
|
1002
|
+
)
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def to_a(_iseq)
|
1006
|
+
[:definesmethod, method_name, method_iseq.to_a]
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def length
|
1010
|
+
3
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def pops
|
1014
|
+
1
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def pushes
|
1018
|
+
0
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def canonical
|
1022
|
+
self
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
def call(vm)
|
1026
|
+
name = method_name
|
1027
|
+
iseq = method_iseq
|
1028
|
+
|
1029
|
+
vm
|
1030
|
+
._self
|
1031
|
+
.__send__(:define_singleton_method, name) do |*args, **kwargs, &block|
|
1032
|
+
vm.run_method_frame(name, iseq, self, *args, **kwargs, &block)
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# ### Summary
|
1038
|
+
#
|
1039
|
+
# `dup` copies the top value of the stack and pushes it onto the stack.
|
1040
|
+
#
|
1041
|
+
# ### Usage
|
1042
|
+
#
|
1043
|
+
# ~~~ruby
|
1044
|
+
# $global = 5
|
1045
|
+
# ~~~
|
1046
|
+
#
|
1047
|
+
class Dup
|
1048
|
+
def disasm(fmt)
|
1049
|
+
fmt.instruction("dup")
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
def to_a(_iseq)
|
1053
|
+
[:dup]
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
def length
|
1057
|
+
1
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def pops
|
1061
|
+
1
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def pushes
|
1065
|
+
2
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def canonical
|
1069
|
+
self
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
def call(vm)
|
1073
|
+
vm.push(vm.stack.last.dup)
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
# ### Summary
|
1078
|
+
#
|
1079
|
+
# `duparray` dups an Array literal and pushes it onto the stack.
|
1080
|
+
#
|
1081
|
+
# ### Usage
|
1082
|
+
#
|
1083
|
+
# ~~~ruby
|
1084
|
+
# [true]
|
1085
|
+
# ~~~
|
1086
|
+
#
|
1087
|
+
class DupArray
|
1088
|
+
attr_reader :object
|
1089
|
+
|
1090
|
+
def initialize(object)
|
1091
|
+
@object = object
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
def disasm(fmt)
|
1095
|
+
fmt.instruction("duparray", [fmt.object(object)])
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
def to_a(_iseq)
|
1099
|
+
[:duparray, object]
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
def length
|
1103
|
+
2
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
def pops
|
1107
|
+
0
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
def pushes
|
1111
|
+
1
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
def canonical
|
1115
|
+
self
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
def call(vm)
|
1119
|
+
vm.push(object.dup)
|
1120
|
+
end
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
# ### Summary
|
1124
|
+
#
|
1125
|
+
# `duphash` dups a Hash literal and pushes it onto the stack.
|
1126
|
+
#
|
1127
|
+
# ### Usage
|
1128
|
+
#
|
1129
|
+
# ~~~ruby
|
1130
|
+
# { a: 1 }
|
1131
|
+
# ~~~
|
1132
|
+
#
|
1133
|
+
class DupHash
|
1134
|
+
attr_reader :object
|
1135
|
+
|
1136
|
+
def initialize(object)
|
1137
|
+
@object = object
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
def disasm(fmt)
|
1141
|
+
fmt.instruction("duphash", [fmt.object(object)])
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
def to_a(_iseq)
|
1145
|
+
[:duphash, object]
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
def length
|
1149
|
+
2
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
def pops
|
1153
|
+
0
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
def pushes
|
1157
|
+
1
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
def canonical
|
1161
|
+
self
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def call(vm)
|
1165
|
+
vm.push(object.dup)
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
# ### Summary
|
1170
|
+
#
|
1171
|
+
# `dupn` duplicates the top `n` stack elements.
|
1172
|
+
#
|
1173
|
+
# ### Usage
|
1174
|
+
#
|
1175
|
+
# ~~~ruby
|
1176
|
+
# Object::X ||= true
|
1177
|
+
# ~~~
|
1178
|
+
#
|
1179
|
+
class DupN
|
1180
|
+
attr_reader :number
|
1181
|
+
|
1182
|
+
def initialize(number)
|
1183
|
+
@number = number
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
def disasm(fmt)
|
1187
|
+
fmt.instruction("dupn", [fmt.object(number)])
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
def to_a(_iseq)
|
1191
|
+
[:dupn, number]
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
def length
|
1195
|
+
2
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
def pops
|
1199
|
+
0
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
def pushes
|
1203
|
+
number
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
def canonical
|
1207
|
+
self
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
def call(vm)
|
1211
|
+
values = vm.pop(number)
|
1212
|
+
vm.push(*values)
|
1213
|
+
vm.push(*values)
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
# ### Summary
|
1218
|
+
#
|
1219
|
+
# `expandarray` looks at the top of the stack, and if the value is an array
|
1220
|
+
# it replaces it on the stack with `number` elements of the array, or `nil`
|
1221
|
+
# if the elements are missing.
|
1222
|
+
#
|
1223
|
+
# ### Usage
|
1224
|
+
#
|
1225
|
+
# ~~~ruby
|
1226
|
+
# x, = [true, false, nil]
|
1227
|
+
# ~~~
|
1228
|
+
#
|
1229
|
+
class ExpandArray
|
1230
|
+
attr_reader :number, :flags
|
1231
|
+
|
1232
|
+
def initialize(number, flags)
|
1233
|
+
@number = number
|
1234
|
+
@flags = flags
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
def disasm(fmt)
|
1238
|
+
fmt.instruction("expandarray", [fmt.object(number), fmt.object(flags)])
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
def to_a(_iseq)
|
1242
|
+
[:expandarray, number, flags]
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
def length
|
1246
|
+
3
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
def pops
|
1250
|
+
1
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
def pushes
|
1254
|
+
number
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
def canonical
|
1258
|
+
self
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
def call(vm)
|
1262
|
+
raise NotImplementedError, "expandarray"
|
1263
|
+
end
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
# ### Summary
|
1267
|
+
#
|
1268
|
+
# `getblockparam` is a similar instruction to `getlocal` in that it looks
|
1269
|
+
# for a local variable in the current instruction sequence's local table and
|
1270
|
+
# walks recursively up the parent instruction sequences until it finds it.
|
1271
|
+
# The local it retrieves, however, is a special block local that was passed
|
1272
|
+
# to the current method. It pushes the value of the block local onto the
|
1273
|
+
# stack.
|
1274
|
+
#
|
1275
|
+
# ### Usage
|
1276
|
+
#
|
1277
|
+
# ~~~ruby
|
1278
|
+
# def foo(&block)
|
1279
|
+
# block
|
1280
|
+
# end
|
1281
|
+
# ~~~
|
1282
|
+
#
|
1283
|
+
class GetBlockParam
|
1284
|
+
attr_reader :index, :level
|
1285
|
+
|
1286
|
+
def initialize(index, level)
|
1287
|
+
@index = index
|
1288
|
+
@level = level
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
def disasm(fmt)
|
1292
|
+
fmt.instruction("getblockparam", [fmt.local(index, explicit: level)])
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
def to_a(iseq)
|
1296
|
+
current = iseq
|
1297
|
+
level.times { current = iseq.parent_iseq }
|
1298
|
+
[:getblockparam, current.local_table.offset(index), level]
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def length
|
1302
|
+
3
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
def pops
|
1306
|
+
0
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
def pushes
|
1310
|
+
1
|
1311
|
+
end
|
1312
|
+
|
1313
|
+
def canonical
|
1314
|
+
self
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
def call(vm)
|
1318
|
+
vm.push(vm.local_get(index, level))
|
1319
|
+
end
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
# ### Summary
|
1323
|
+
#
|
1324
|
+
# `getblockparamproxy` is almost the same as `getblockparam` except that it
|
1325
|
+
# pushes a proxy object onto the stack instead of the actual value of the
|
1326
|
+
# block local. This is used when a method is being called on the block
|
1327
|
+
# local.
|
1328
|
+
#
|
1329
|
+
# ### Usage
|
1330
|
+
#
|
1331
|
+
# ~~~ruby
|
1332
|
+
# def foo(&block)
|
1333
|
+
# block.call
|
1334
|
+
# end
|
1335
|
+
# ~~~
|
1336
|
+
#
|
1337
|
+
class GetBlockParamProxy
|
1338
|
+
attr_reader :index, :level
|
1339
|
+
|
1340
|
+
def initialize(index, level)
|
1341
|
+
@index = index
|
1342
|
+
@level = level
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
def disasm(fmt)
|
1346
|
+
fmt.instruction(
|
1347
|
+
"getblockparamproxy",
|
1348
|
+
[fmt.local(index, explicit: level)]
|
1349
|
+
)
|
1350
|
+
end
|
1351
|
+
|
1352
|
+
def to_a(iseq)
|
1353
|
+
current = iseq
|
1354
|
+
level.times { current = iseq.parent_iseq }
|
1355
|
+
[:getblockparamproxy, current.local_table.offset(index), level]
|
1356
|
+
end
|
1357
|
+
|
1358
|
+
def length
|
1359
|
+
3
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
def pops
|
1363
|
+
0
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def pushes
|
1367
|
+
1
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
def canonical
|
1371
|
+
self
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
def call(vm)
|
1375
|
+
vm.push(vm.local_get(index, level))
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
# ### Summary
|
1380
|
+
#
|
1381
|
+
# `getclassvariable` looks for a class variable in the current class and
|
1382
|
+
# pushes its value onto the stack. It uses an inline cache to reduce the
|
1383
|
+
# need to lookup the class variable in the class hierarchy every time.
|
1384
|
+
#
|
1385
|
+
# ### Usage
|
1386
|
+
#
|
1387
|
+
# ~~~ruby
|
1388
|
+
# @@class_variable
|
1389
|
+
# ~~~
|
1390
|
+
#
|
1391
|
+
class GetClassVariable
|
1392
|
+
attr_reader :name, :cache
|
1393
|
+
|
1394
|
+
def initialize(name, cache)
|
1395
|
+
@name = name
|
1396
|
+
@cache = cache
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def disasm(fmt)
|
1400
|
+
fmt.instruction(
|
1401
|
+
"getclassvariable",
|
1402
|
+
[fmt.object(name), fmt.inline_storage(cache)]
|
1403
|
+
)
|
1404
|
+
end
|
1405
|
+
|
1406
|
+
def to_a(_iseq)
|
1407
|
+
[:getclassvariable, name, cache]
|
1408
|
+
end
|
1409
|
+
|
1410
|
+
def length
|
1411
|
+
3
|
1412
|
+
end
|
1413
|
+
|
1414
|
+
def pops
|
1415
|
+
0
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
def pushes
|
1419
|
+
1
|
1420
|
+
end
|
1421
|
+
|
1422
|
+
def canonical
|
1423
|
+
self
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
def call(vm)
|
1427
|
+
clazz = vm._self
|
1428
|
+
clazz = clazz.class unless clazz.is_a?(Class)
|
1429
|
+
vm.push(clazz.class_variable_get(name))
|
1430
|
+
end
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
# ### Summary
|
1434
|
+
#
|
1435
|
+
# `getconstant` performs a constant lookup and pushes the value of the
|
1436
|
+
# constant onto the stack. It pops both the class it should look in and
|
1437
|
+
# whether or not it should look globally as well.
|
1438
|
+
#
|
1439
|
+
# ### Usage
|
1440
|
+
#
|
1441
|
+
# ~~~ruby
|
1442
|
+
# Constant
|
1443
|
+
# ~~~
|
1444
|
+
#
|
1445
|
+
class GetConstant
|
1446
|
+
attr_reader :name
|
1447
|
+
|
1448
|
+
def initialize(name)
|
1449
|
+
@name = name
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
def disasm(fmt)
|
1453
|
+
fmt.instruction("getconstant", [fmt.object(name)])
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
def to_a(_iseq)
|
1457
|
+
[:getconstant, name]
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
def length
|
1461
|
+
2
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
def pops
|
1465
|
+
2
|
1466
|
+
end
|
1467
|
+
|
1468
|
+
def pushes
|
1469
|
+
1
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
def canonical
|
1473
|
+
self
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
def call(vm)
|
1477
|
+
# const_base, allow_nil =
|
1478
|
+
vm.pop(2)
|
1479
|
+
|
1480
|
+
vm.frame.nesting.reverse_each do |clazz|
|
1481
|
+
if clazz.const_defined?(name)
|
1482
|
+
vm.push(clazz.const_get(name))
|
1483
|
+
return
|
1484
|
+
end
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
raise NameError, "uninitialized constant #{name}"
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
# ### Summary
|
1492
|
+
#
|
1493
|
+
# `getglobal` pushes the value of a global variables onto the stack.
|
1494
|
+
#
|
1495
|
+
# ### Usage
|
1496
|
+
#
|
1497
|
+
# ~~~ruby
|
1498
|
+
# $$
|
1499
|
+
# ~~~
|
1500
|
+
#
|
1501
|
+
class GetGlobal
|
1502
|
+
attr_reader :name
|
1503
|
+
|
1504
|
+
def initialize(name)
|
1505
|
+
@name = name
|
1506
|
+
end
|
1507
|
+
|
1508
|
+
def disasm(fmt)
|
1509
|
+
fmt.instruction("getglobal", [fmt.object(name)])
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
def to_a(_iseq)
|
1513
|
+
[:getglobal, name]
|
1514
|
+
end
|
1515
|
+
|
1516
|
+
def length
|
1517
|
+
2
|
1518
|
+
end
|
1519
|
+
|
1520
|
+
def pops
|
1521
|
+
0
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
def pushes
|
1525
|
+
1
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
def canonical
|
1529
|
+
self
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
def call(vm)
|
1533
|
+
# Evaluating the name of the global variable because there isn't a
|
1534
|
+
# reflection API for global variables.
|
1535
|
+
vm.push(eval(name.to_s, binding, __FILE__, __LINE__))
|
1536
|
+
end
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
# ### Summary
|
1540
|
+
#
|
1541
|
+
# `getinstancevariable` pushes the value of an instance variable onto the
|
1542
|
+
# stack. It uses an inline cache to avoid having to look up the instance
|
1543
|
+
# variable in the class hierarchy every time.
|
1544
|
+
#
|
1545
|
+
# This instruction has two forms, but both have the same structure. Before
|
1546
|
+
# Ruby 3.2, the inline cache corresponded to both the get and set
|
1547
|
+
# instructions and could be shared. Since Ruby 3.2, it uses object shapes
|
1548
|
+
# instead so the caches are unique per instruction.
|
1549
|
+
#
|
1550
|
+
# ### Usage
|
1551
|
+
#
|
1552
|
+
# ~~~ruby
|
1553
|
+
# @instance_variable
|
1554
|
+
# ~~~
|
1555
|
+
#
|
1556
|
+
class GetInstanceVariable
|
1557
|
+
attr_reader :name, :cache
|
1558
|
+
|
1559
|
+
def initialize(name, cache)
|
1560
|
+
@name = name
|
1561
|
+
@cache = cache
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
def disasm(fmt)
|
1565
|
+
fmt.instruction(
|
1566
|
+
"getinstancevariable",
|
1567
|
+
[fmt.object(name), fmt.inline_storage(cache)]
|
1568
|
+
)
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
def to_a(_iseq)
|
1572
|
+
[:getinstancevariable, name, cache]
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
def length
|
1576
|
+
3
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
def pops
|
1580
|
+
0
|
1581
|
+
end
|
1582
|
+
|
1583
|
+
def pushes
|
1584
|
+
1
|
1585
|
+
end
|
1586
|
+
|
1587
|
+
def canonical
|
1588
|
+
self
|
1589
|
+
end
|
1590
|
+
|
1591
|
+
def call(vm)
|
1592
|
+
method = Object.instance_method(:instance_variable_get)
|
1593
|
+
vm.push(method.bind(vm._self).call(name))
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
# ### Summary
|
1598
|
+
#
|
1599
|
+
# `getlocal` fetches the value of a local variable from a frame determined
|
1600
|
+
# by the level and index arguments. The level is the number of frames back
|
1601
|
+
# to look and the index is the index in the local table. It pushes the value
|
1602
|
+
# it finds onto the stack.
|
1603
|
+
#
|
1604
|
+
# ### Usage
|
1605
|
+
#
|
1606
|
+
# ~~~ruby
|
1607
|
+
# value = 5
|
1608
|
+
# tap { tap { value } }
|
1609
|
+
# ~~~
|
1610
|
+
#
|
1611
|
+
class GetLocal
|
1612
|
+
attr_reader :index, :level
|
1613
|
+
|
1614
|
+
def initialize(index, level)
|
1615
|
+
@index = index
|
1616
|
+
@level = level
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
def disasm(fmt)
|
1620
|
+
fmt.instruction("getlocal", [fmt.local(index, explicit: level)])
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
def to_a(iseq)
|
1624
|
+
current = iseq
|
1625
|
+
level.times { current = current.parent_iseq }
|
1626
|
+
[:getlocal, current.local_table.offset(index), level]
|
1627
|
+
end
|
1628
|
+
|
1629
|
+
def length
|
1630
|
+
3
|
1631
|
+
end
|
1632
|
+
|
1633
|
+
def pops
|
1634
|
+
0
|
1635
|
+
end
|
1636
|
+
|
1637
|
+
def pushes
|
1638
|
+
1
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
def canonical
|
1642
|
+
self
|
1643
|
+
end
|
1644
|
+
|
1645
|
+
def call(vm)
|
1646
|
+
vm.push(vm.local_get(index, level))
|
1647
|
+
end
|
1648
|
+
end
|
1649
|
+
|
1650
|
+
# ### Summary
|
1651
|
+
#
|
1652
|
+
# `getlocal_WC_0` is a specialized version of the `getlocal` instruction. It
|
1653
|
+
# fetches the value of a local variable from the current frame determined by
|
1654
|
+
# the index given as its only argument.
|
1655
|
+
#
|
1656
|
+
# ### Usage
|
1657
|
+
#
|
1658
|
+
# ~~~ruby
|
1659
|
+
# value = 5
|
1660
|
+
# value
|
1661
|
+
# ~~~
|
1662
|
+
#
|
1663
|
+
class GetLocalWC0
|
1664
|
+
attr_reader :index
|
1665
|
+
|
1666
|
+
def initialize(index)
|
1667
|
+
@index = index
|
1668
|
+
end
|
1669
|
+
|
1670
|
+
def disasm(fmt)
|
1671
|
+
fmt.instruction("getlocal_WC_0", [fmt.local(index, implicit: 0)])
|
1672
|
+
end
|
1673
|
+
|
1674
|
+
def to_a(iseq)
|
1675
|
+
[:getlocal_WC_0, iseq.local_table.offset(index)]
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
def length
|
1679
|
+
2
|
1680
|
+
end
|
1681
|
+
|
1682
|
+
def pops
|
1683
|
+
0
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
def pushes
|
1687
|
+
1
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
def canonical
|
1691
|
+
GetLocal.new(index, 0)
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
def call(vm)
|
1695
|
+
canonical.call(vm)
|
1696
|
+
end
|
1697
|
+
end
|
1698
|
+
|
1699
|
+
# ### Summary
|
1700
|
+
#
|
1701
|
+
# `getlocal_WC_1` is a specialized version of the `getlocal` instruction. It
|
1702
|
+
# fetches the value of a local variable from the parent frame determined by
|
1703
|
+
# the index given as its only argument.
|
1704
|
+
#
|
1705
|
+
# ### Usage
|
1706
|
+
#
|
1707
|
+
# ~~~ruby
|
1708
|
+
# value = 5
|
1709
|
+
# self.then { value }
|
1710
|
+
# ~~~
|
1711
|
+
#
|
1712
|
+
class GetLocalWC1
|
1713
|
+
attr_reader :index
|
1714
|
+
|
1715
|
+
def initialize(index)
|
1716
|
+
@index = index
|
1717
|
+
end
|
1718
|
+
|
1719
|
+
def disasm(fmt)
|
1720
|
+
fmt.instruction("getlocal_WC_1", [fmt.local(index, implicit: 1)])
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
def to_a(iseq)
|
1724
|
+
[:getlocal_WC_1, iseq.parent_iseq.local_table.offset(index)]
|
1725
|
+
end
|
1726
|
+
|
1727
|
+
def length
|
1728
|
+
2
|
1729
|
+
end
|
1730
|
+
|
1731
|
+
def pops
|
1732
|
+
0
|
1733
|
+
end
|
1734
|
+
|
1735
|
+
def pushes
|
1736
|
+
1
|
1737
|
+
end
|
1738
|
+
|
1739
|
+
def canonical
|
1740
|
+
GetLocal.new(index, 1)
|
1741
|
+
end
|
1742
|
+
|
1743
|
+
def call(vm)
|
1744
|
+
canonical.call(vm)
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
|
1748
|
+
# ### Summary
|
1749
|
+
#
|
1750
|
+
# `getspecial` pushes the value of a special local variable onto the stack.
|
1751
|
+
#
|
1752
|
+
# ### Usage
|
1753
|
+
#
|
1754
|
+
# ~~~ruby
|
1755
|
+
# 1 if (a == 1) .. (b == 2)
|
1756
|
+
# ~~~
|
1757
|
+
#
|
1758
|
+
class GetSpecial
|
1759
|
+
SVAR_LASTLINE = 0 # $_
|
1760
|
+
SVAR_BACKREF = 1 # $~
|
1761
|
+
SVAR_FLIPFLOP_START = 2 # flipflop
|
1762
|
+
|
1763
|
+
attr_reader :key, :type
|
1764
|
+
|
1765
|
+
def initialize(key, type)
|
1766
|
+
@key = key
|
1767
|
+
@type = type
|
1768
|
+
end
|
1769
|
+
|
1770
|
+
def disasm(fmt)
|
1771
|
+
fmt.instruction("getspecial", [fmt.object(key), fmt.object(type)])
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
def to_a(_iseq)
|
1775
|
+
[:getspecial, key, type]
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
def length
|
1779
|
+
3
|
1780
|
+
end
|
1781
|
+
|
1782
|
+
def pops
|
1783
|
+
0
|
1784
|
+
end
|
1785
|
+
|
1786
|
+
def pushes
|
1787
|
+
1
|
1788
|
+
end
|
1789
|
+
|
1790
|
+
def canonical
|
1791
|
+
self
|
1792
|
+
end
|
1793
|
+
|
1794
|
+
def call(vm)
|
1795
|
+
case key
|
1796
|
+
when SVAR_LASTLINE
|
1797
|
+
raise NotImplementedError, "getspecial SVAR_LASTLINE"
|
1798
|
+
when SVAR_BACKREF
|
1799
|
+
raise NotImplementedError, "getspecial SVAR_BACKREF"
|
1800
|
+
when SVAR_FLIPFLOP_START
|
1801
|
+
vm.frame_svar.svars[SVAR_FLIPFLOP_START]
|
1802
|
+
end
|
1803
|
+
end
|
1804
|
+
end
|
1805
|
+
|
1806
|
+
# ### Summary
|
1807
|
+
#
|
1808
|
+
# `intern` converts the top element of the stack to a symbol and pushes the
|
1809
|
+
# symbol onto the stack.
|
1810
|
+
#
|
1811
|
+
# ### Usage
|
1812
|
+
#
|
1813
|
+
# ~~~ruby
|
1814
|
+
# :"#{"foo"}"
|
1815
|
+
# ~~~
|
1816
|
+
#
|
1817
|
+
class Intern
|
1818
|
+
def disasm(fmt)
|
1819
|
+
fmt.instruction("intern")
|
1820
|
+
end
|
1821
|
+
|
1822
|
+
def to_a(_iseq)
|
1823
|
+
[:intern]
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
def length
|
1827
|
+
1
|
1828
|
+
end
|
1829
|
+
|
1830
|
+
def pops
|
1831
|
+
1
|
1832
|
+
end
|
1833
|
+
|
1834
|
+
def pushes
|
1835
|
+
1
|
1836
|
+
end
|
1837
|
+
|
1838
|
+
def canonical
|
1839
|
+
self
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
def call(vm)
|
1843
|
+
vm.push(vm.pop.to_sym)
|
1844
|
+
end
|
1845
|
+
end
|
1846
|
+
|
1847
|
+
# ### Summary
|
1848
|
+
#
|
1849
|
+
# `invokeblock` invokes the block given to the current method. It pops the
|
1850
|
+
# arguments for the block off the stack and pushes the result of running the
|
1851
|
+
# block onto the stack.
|
1852
|
+
#
|
1853
|
+
# ### Usage
|
1854
|
+
#
|
1855
|
+
# ~~~ruby
|
1856
|
+
# def foo
|
1857
|
+
# yield
|
1858
|
+
# end
|
1859
|
+
# ~~~
|
1860
|
+
#
|
1861
|
+
class InvokeBlock
|
1862
|
+
attr_reader :calldata
|
1863
|
+
|
1864
|
+
def initialize(calldata)
|
1865
|
+
@calldata = calldata
|
1866
|
+
end
|
1867
|
+
|
1868
|
+
def disasm(fmt)
|
1869
|
+
fmt.instruction("invokeblock", [fmt.calldata(calldata)])
|
1870
|
+
end
|
1871
|
+
|
1872
|
+
def to_a(_iseq)
|
1873
|
+
[:invokeblock, calldata.to_h]
|
1874
|
+
end
|
1875
|
+
|
1876
|
+
def length
|
1877
|
+
2
|
1878
|
+
end
|
1879
|
+
|
1880
|
+
def pops
|
1881
|
+
calldata.argc
|
1882
|
+
end
|
1883
|
+
|
1884
|
+
def pushes
|
1885
|
+
1
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
def canonical
|
1889
|
+
self
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
def call(vm)
|
1893
|
+
vm.push(vm.frame_yield.block.call(*vm.pop(calldata.argc)))
|
1894
|
+
end
|
1895
|
+
end
|
1896
|
+
|
1897
|
+
# ### Summary
|
1898
|
+
#
|
1899
|
+
# `invokesuper` is similar to the `send` instruction, except that it calls
|
1900
|
+
# the super method. It pops the receiver and arguments off the stack and
|
1901
|
+
# pushes the return value onto the stack.
|
1902
|
+
#
|
1903
|
+
# ### Usage
|
1904
|
+
#
|
1905
|
+
# ~~~ruby
|
1906
|
+
# def foo
|
1907
|
+
# super
|
1908
|
+
# end
|
1909
|
+
# ~~~
|
1910
|
+
#
|
1911
|
+
class InvokeSuper
|
1912
|
+
attr_reader :calldata, :block_iseq
|
1913
|
+
|
1914
|
+
def initialize(calldata, block_iseq)
|
1915
|
+
@calldata = calldata
|
1916
|
+
@block_iseq = block_iseq
|
1917
|
+
end
|
1918
|
+
|
1919
|
+
def disasm(fmt)
|
1920
|
+
fmt.enqueue(block_iseq) if block_iseq
|
1921
|
+
fmt.instruction(
|
1922
|
+
"invokesuper",
|
1923
|
+
[fmt.calldata(calldata), block_iseq&.name || "nil"]
|
1924
|
+
)
|
1925
|
+
end
|
1926
|
+
|
1927
|
+
def to_a(_iseq)
|
1928
|
+
[:invokesuper, calldata.to_h, block_iseq&.to_a]
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
def length
|
1932
|
+
1
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
def pops
|
1936
|
+
argb = (calldata.flag?(CallData::CALL_ARGS_BLOCKARG) ? 1 : 0)
|
1937
|
+
argb + calldata.argc + 1
|
1938
|
+
end
|
1939
|
+
|
1940
|
+
def pushes
|
1941
|
+
1
|
1942
|
+
end
|
1943
|
+
|
1944
|
+
def canonical
|
1945
|
+
self
|
1946
|
+
end
|
1947
|
+
|
1948
|
+
def call(vm)
|
1949
|
+
block =
|
1950
|
+
if (iseq = block_iseq)
|
1951
|
+
->(*args, **kwargs, &blk) do
|
1952
|
+
vm.run_block_frame(iseq, *args, **kwargs, &blk)
|
1953
|
+
end
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
keywords =
|
1957
|
+
if calldata.kw_arg
|
1958
|
+
calldata.kw_arg.zip(vm.pop(calldata.kw_arg.length)).to_h
|
1959
|
+
else
|
1960
|
+
{}
|
1961
|
+
end
|
1962
|
+
|
1963
|
+
arguments = vm.pop(calldata.argc)
|
1964
|
+
receiver = vm.pop
|
1965
|
+
|
1966
|
+
method = receiver.method(vm.frame.name).super_method
|
1967
|
+
vm.push(method.call(*arguments, **keywords, &block))
|
1968
|
+
end
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
# ### Summary
|
1972
|
+
#
|
1973
|
+
# `jump` unconditionally jumps to the label given as its only argument.
|
1974
|
+
#
|
1975
|
+
# ### Usage
|
1976
|
+
#
|
1977
|
+
# ~~~ruby
|
1978
|
+
# x = 0
|
1979
|
+
# if x == 0
|
1980
|
+
# puts "0"
|
1981
|
+
# else
|
1982
|
+
# puts "2"
|
1983
|
+
# end
|
1984
|
+
# ~~~
|
1985
|
+
#
|
1986
|
+
class Jump
|
1987
|
+
attr_reader :label
|
1988
|
+
|
1989
|
+
def initialize(label)
|
1990
|
+
@label = label
|
1991
|
+
end
|
1992
|
+
|
1993
|
+
def disasm(fmt)
|
1994
|
+
fmt.instruction("jump", [fmt.label(label)])
|
1995
|
+
end
|
1996
|
+
|
1997
|
+
def to_a(_iseq)
|
1998
|
+
[:jump, label.name]
|
1999
|
+
end
|
2000
|
+
|
2001
|
+
def length
|
2002
|
+
2
|
2003
|
+
end
|
2004
|
+
|
2005
|
+
def pops
|
2006
|
+
0
|
2007
|
+
end
|
2008
|
+
|
2009
|
+
def pushes
|
2010
|
+
0
|
2011
|
+
end
|
2012
|
+
|
2013
|
+
def canonical
|
2014
|
+
self
|
2015
|
+
end
|
2016
|
+
|
2017
|
+
def call(vm)
|
2018
|
+
vm.jump(label)
|
2019
|
+
end
|
2020
|
+
end
|
2021
|
+
|
2022
|
+
# ### Summary
|
2023
|
+
#
|
2024
|
+
# `leave` exits the current frame.
|
2025
|
+
#
|
2026
|
+
# ### Usage
|
2027
|
+
#
|
2028
|
+
# ~~~ruby
|
2029
|
+
# ;;
|
2030
|
+
# ~~~
|
2031
|
+
#
|
2032
|
+
class Leave
|
2033
|
+
def disasm(fmt)
|
2034
|
+
fmt.instruction("leave")
|
2035
|
+
end
|
2036
|
+
|
2037
|
+
def to_a(_iseq)
|
2038
|
+
[:leave]
|
2039
|
+
end
|
2040
|
+
|
2041
|
+
def length
|
2042
|
+
1
|
2043
|
+
end
|
2044
|
+
|
2045
|
+
def pops
|
2046
|
+
1
|
2047
|
+
end
|
2048
|
+
|
2049
|
+
def pushes
|
2050
|
+
# TODO: This is wrong. It should be 1. But it's 0 for now because
|
2051
|
+
# otherwise the stack size is incorrectly calculated.
|
2052
|
+
0
|
2053
|
+
end
|
2054
|
+
|
2055
|
+
def canonical
|
2056
|
+
self
|
2057
|
+
end
|
2058
|
+
|
2059
|
+
def call(vm)
|
2060
|
+
vm.leave
|
2061
|
+
end
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
# ### Summary
|
2065
|
+
#
|
2066
|
+
# `newarray` puts a new array initialized with `number` values from the
|
2067
|
+
# stack. It pops `number` values off the stack and pushes the array onto the
|
2068
|
+
# stack.
|
2069
|
+
#
|
2070
|
+
# ### Usage
|
2071
|
+
#
|
2072
|
+
# ~~~ruby
|
2073
|
+
# ["string"]
|
2074
|
+
# ~~~
|
2075
|
+
#
|
2076
|
+
class NewArray
|
2077
|
+
attr_reader :number
|
2078
|
+
|
2079
|
+
def initialize(number)
|
2080
|
+
@number = number
|
2081
|
+
end
|
2082
|
+
|
2083
|
+
def disasm(fmt)
|
2084
|
+
fmt.instruction("newarray", [fmt.object(number)])
|
2085
|
+
end
|
2086
|
+
|
2087
|
+
def to_a(_iseq)
|
2088
|
+
[:newarray, number]
|
2089
|
+
end
|
2090
|
+
|
2091
|
+
def length
|
2092
|
+
2
|
2093
|
+
end
|
2094
|
+
|
2095
|
+
def pops
|
2096
|
+
number
|
2097
|
+
end
|
2098
|
+
|
2099
|
+
def pushes
|
2100
|
+
1
|
2101
|
+
end
|
2102
|
+
|
2103
|
+
def canonical
|
2104
|
+
self
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
def call(vm)
|
2108
|
+
vm.push(vm.pop(number))
|
2109
|
+
end
|
2110
|
+
end
|
2111
|
+
|
2112
|
+
# ### Summary
|
2113
|
+
#
|
2114
|
+
# `newarraykwsplat` is a specialized version of `newarray` that takes a **
|
2115
|
+
# splat argument. It pops `number` values off the stack and pushes the array
|
2116
|
+
# onto the stack.
|
2117
|
+
#
|
2118
|
+
# ### Usage
|
2119
|
+
#
|
2120
|
+
# ~~~ruby
|
2121
|
+
# ["string", **{ foo: "bar" }]
|
2122
|
+
# ~~~
|
2123
|
+
#
|
2124
|
+
class NewArrayKwSplat
|
2125
|
+
attr_reader :number
|
2126
|
+
|
2127
|
+
def initialize(number)
|
2128
|
+
@number = number
|
2129
|
+
end
|
2130
|
+
|
2131
|
+
def disasm(fmt)
|
2132
|
+
fmt.instruction("newarraykwsplat", [fmt.object(number)])
|
2133
|
+
end
|
2134
|
+
|
2135
|
+
def to_a(_iseq)
|
2136
|
+
[:newarraykwsplat, number]
|
2137
|
+
end
|
2138
|
+
|
2139
|
+
def length
|
2140
|
+
2
|
2141
|
+
end
|
2142
|
+
|
2143
|
+
def pops
|
2144
|
+
number
|
2145
|
+
end
|
2146
|
+
|
2147
|
+
def pushes
|
2148
|
+
1
|
2149
|
+
end
|
2150
|
+
|
2151
|
+
def canonical
|
2152
|
+
self
|
2153
|
+
end
|
2154
|
+
|
2155
|
+
def call(vm)
|
2156
|
+
vm.push(vm.pop(number))
|
2157
|
+
end
|
2158
|
+
end
|
2159
|
+
|
2160
|
+
# ### Summary
|
2161
|
+
#
|
2162
|
+
# `newhash` puts a new hash onto the stack, using `number` elements from the
|
2163
|
+
# stack. `number` needs to be even. It pops `number` elements off the stack
|
2164
|
+
# and pushes a hash onto the stack.
|
2165
|
+
#
|
2166
|
+
# ### Usage
|
2167
|
+
#
|
2168
|
+
# ~~~ruby
|
2169
|
+
# def foo(key, value)
|
2170
|
+
# { key => value }
|
2171
|
+
# end
|
2172
|
+
# ~~~
|
2173
|
+
#
|
2174
|
+
class NewHash
|
2175
|
+
attr_reader :number
|
2176
|
+
|
2177
|
+
def initialize(number)
|
2178
|
+
@number = number
|
2179
|
+
end
|
2180
|
+
|
2181
|
+
def disasm(fmt)
|
2182
|
+
fmt.instruction("newhash", [fmt.object(number)])
|
2183
|
+
end
|
2184
|
+
|
2185
|
+
def to_a(_iseq)
|
2186
|
+
[:newhash, number]
|
2187
|
+
end
|
2188
|
+
|
2189
|
+
def length
|
2190
|
+
2
|
2191
|
+
end
|
2192
|
+
|
2193
|
+
def pops
|
2194
|
+
number
|
2195
|
+
end
|
2196
|
+
|
2197
|
+
def pushes
|
2198
|
+
1
|
2199
|
+
end
|
2200
|
+
|
2201
|
+
def canonical
|
2202
|
+
self
|
2203
|
+
end
|
2204
|
+
|
2205
|
+
def call(vm)
|
2206
|
+
vm.push(vm.pop(number).each_slice(2).to_h)
|
2207
|
+
end
|
2208
|
+
end
|
2209
|
+
|
2210
|
+
# ### Summary
|
2211
|
+
#
|
2212
|
+
# `newrange` creates a new range object from the top two values on the
|
2213
|
+
# stack. It pops both of them off, and then pushes on the new range. It
|
2214
|
+
# takes one argument which is 0 if the end is included or 1 if the end value
|
2215
|
+
# is excluded.
|
2216
|
+
#
|
2217
|
+
# ### Usage
|
2218
|
+
#
|
2219
|
+
# ~~~ruby
|
2220
|
+
# x = 0
|
2221
|
+
# y = 1
|
2222
|
+
# p (x..y), (x...y)
|
2223
|
+
# ~~~
|
2224
|
+
#
|
2225
|
+
class NewRange
|
2226
|
+
attr_reader :exclude_end
|
2227
|
+
|
2228
|
+
def initialize(exclude_end)
|
2229
|
+
@exclude_end = exclude_end
|
2230
|
+
end
|
2231
|
+
|
2232
|
+
def disasm(fmt)
|
2233
|
+
fmt.instruction("newrange", [fmt.object(exclude_end)])
|
2234
|
+
end
|
2235
|
+
|
2236
|
+
def to_a(_iseq)
|
2237
|
+
[:newrange, exclude_end]
|
2238
|
+
end
|
2239
|
+
|
2240
|
+
def length
|
2241
|
+
2
|
2242
|
+
end
|
2243
|
+
|
2244
|
+
def pops
|
2245
|
+
2
|
2246
|
+
end
|
2247
|
+
|
2248
|
+
def pushes
|
2249
|
+
1
|
2250
|
+
end
|
2251
|
+
|
2252
|
+
def canonical
|
2253
|
+
self
|
2254
|
+
end
|
2255
|
+
|
2256
|
+
def call(vm)
|
2257
|
+
vm.push(Range.new(*vm.pop(2), exclude_end == 1))
|
2258
|
+
end
|
2259
|
+
end
|
2260
|
+
|
2261
|
+
# ### Summary
|
2262
|
+
#
|
2263
|
+
# `nop` is a no-operation instruction. It is used to pad the instruction
|
2264
|
+
# sequence so there is a place for other instructions to jump to.
|
2265
|
+
#
|
2266
|
+
# ### Usage
|
2267
|
+
#
|
2268
|
+
# ~~~ruby
|
2269
|
+
# raise rescue true
|
2270
|
+
# ~~~
|
2271
|
+
#
|
2272
|
+
class Nop
|
2273
|
+
def disasm(fmt)
|
2274
|
+
fmt.instruction("nop")
|
2275
|
+
end
|
2276
|
+
|
2277
|
+
def to_a(_iseq)
|
2278
|
+
[:nop]
|
2279
|
+
end
|
2280
|
+
|
2281
|
+
def length
|
2282
|
+
1
|
2283
|
+
end
|
2284
|
+
|
2285
|
+
def pops
|
2286
|
+
0
|
2287
|
+
end
|
2288
|
+
|
2289
|
+
def pushes
|
2290
|
+
0
|
2291
|
+
end
|
2292
|
+
|
2293
|
+
def canonical
|
2294
|
+
self
|
2295
|
+
end
|
2296
|
+
|
2297
|
+
def call(vm)
|
2298
|
+
end
|
2299
|
+
end
|
2300
|
+
|
2301
|
+
# ### Summary
|
2302
|
+
#
|
2303
|
+
# `objtostring` pops a value from the stack, calls `to_s` on that value and
|
2304
|
+
# then pushes the result back to the stack.
|
2305
|
+
#
|
2306
|
+
# It has various fast paths for classes like String, Symbol, Module, Class,
|
2307
|
+
# etc. For everything else it calls `to_s`.
|
2308
|
+
#
|
2309
|
+
# ### Usage
|
2310
|
+
#
|
2311
|
+
# ~~~ruby
|
2312
|
+
# "#{5}"
|
2313
|
+
# ~~~
|
2314
|
+
#
|
2315
|
+
class ObjToString
|
2316
|
+
attr_reader :calldata
|
2317
|
+
|
2318
|
+
def initialize(calldata)
|
2319
|
+
@calldata = calldata
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
def disasm(fmt)
|
2323
|
+
fmt.instruction("objtostring", [fmt.calldata(calldata)])
|
2324
|
+
end
|
2325
|
+
|
2326
|
+
def to_a(_iseq)
|
2327
|
+
[:objtostring, calldata.to_h]
|
2328
|
+
end
|
2329
|
+
|
2330
|
+
def length
|
2331
|
+
2
|
2332
|
+
end
|
2333
|
+
|
2334
|
+
def pops
|
2335
|
+
1
|
2336
|
+
end
|
2337
|
+
|
2338
|
+
def pushes
|
2339
|
+
1
|
2340
|
+
end
|
2341
|
+
|
2342
|
+
def canonical
|
2343
|
+
self
|
2344
|
+
end
|
2345
|
+
|
2346
|
+
def call(vm)
|
2347
|
+
vm.push(vm.pop.to_s)
|
2348
|
+
end
|
2349
|
+
end
|
2350
|
+
|
2351
|
+
# ### Summary
|
2352
|
+
#
|
2353
|
+
# `once` is an instruction that wraps an instruction sequence and ensures
|
2354
|
+
# that is it only ever executed once for the lifetime of the program. It
|
2355
|
+
# uses a cache to ensure that it is only executed once. It pushes the result
|
2356
|
+
# of running the instruction sequence onto the stack.
|
2357
|
+
#
|
2358
|
+
# ### Usage
|
2359
|
+
#
|
2360
|
+
# ~~~ruby
|
2361
|
+
# END { puts "END" }
|
2362
|
+
# ~~~
|
2363
|
+
#
|
2364
|
+
class Once
|
2365
|
+
attr_reader :iseq, :cache
|
2366
|
+
|
2367
|
+
def initialize(iseq, cache)
|
2368
|
+
@iseq = iseq
|
2369
|
+
@cache = cache
|
2370
|
+
end
|
2371
|
+
|
2372
|
+
def disasm(fmt)
|
2373
|
+
fmt.enqueue(iseq)
|
2374
|
+
fmt.instruction("once", [iseq.name, fmt.inline_storage(cache)])
|
2375
|
+
end
|
2376
|
+
|
2377
|
+
def to_a(_iseq)
|
2378
|
+
[:once, iseq.to_a, cache]
|
2379
|
+
end
|
2380
|
+
|
2381
|
+
def length
|
2382
|
+
3
|
2383
|
+
end
|
2384
|
+
|
2385
|
+
def pops
|
2386
|
+
0
|
2387
|
+
end
|
2388
|
+
|
2389
|
+
def pushes
|
2390
|
+
1
|
2391
|
+
end
|
2392
|
+
|
2393
|
+
def canonical
|
2394
|
+
self
|
2395
|
+
end
|
2396
|
+
|
2397
|
+
def call(vm)
|
2398
|
+
return if @executed
|
2399
|
+
vm.push(vm.run_block_frame(iseq))
|
2400
|
+
@executed = true
|
2401
|
+
end
|
2402
|
+
end
|
2403
|
+
|
2404
|
+
# ### Summary
|
2405
|
+
#
|
2406
|
+
# `opt_and` is a specialization of the `opt_send_without_block` instruction
|
2407
|
+
# that occurs when the `&` operator is used. There is a fast path for if
|
2408
|
+
# both operands are integers. It pops both the receiver and the argument off
|
2409
|
+
# the stack and pushes on the result.
|
2410
|
+
#
|
2411
|
+
# ### Usage
|
2412
|
+
#
|
2413
|
+
# ~~~ruby
|
2414
|
+
# 2 & 3
|
2415
|
+
# ~~~
|
2416
|
+
#
|
2417
|
+
class OptAnd
|
2418
|
+
attr_reader :calldata
|
2419
|
+
|
2420
|
+
def initialize(calldata)
|
2421
|
+
@calldata = calldata
|
2422
|
+
end
|
2423
|
+
|
2424
|
+
def disasm(fmt)
|
2425
|
+
fmt.instruction("opt_and", [fmt.calldata(calldata)])
|
2426
|
+
end
|
2427
|
+
|
2428
|
+
def to_a(_iseq)
|
2429
|
+
[:opt_and, calldata.to_h]
|
2430
|
+
end
|
2431
|
+
|
2432
|
+
def length
|
2433
|
+
2
|
2434
|
+
end
|
2435
|
+
|
2436
|
+
def pops
|
2437
|
+
2
|
2438
|
+
end
|
2439
|
+
|
2440
|
+
def pushes
|
2441
|
+
1
|
2442
|
+
end
|
2443
|
+
|
2444
|
+
def canonical
|
2445
|
+
Send.new(calldata, nil)
|
2446
|
+
end
|
2447
|
+
|
2448
|
+
def call(vm)
|
2449
|
+
canonical.call(vm)
|
2450
|
+
end
|
2451
|
+
end
|
2452
|
+
|
2453
|
+
# ### Summary
|
2454
|
+
#
|
2455
|
+
# `opt_aref` is a specialization of the `opt_send_without_block` instruction
|
2456
|
+
# that occurs when the `[]` operator is used. There are fast paths if the
|
2457
|
+
# receiver is an integer, array, or hash.
|
2458
|
+
#
|
2459
|
+
# ### Usage
|
2460
|
+
#
|
2461
|
+
# ~~~ruby
|
2462
|
+
# 7[2]
|
2463
|
+
# ~~~
|
2464
|
+
#
|
2465
|
+
class OptAref
|
2466
|
+
attr_reader :calldata
|
2467
|
+
|
2468
|
+
def initialize(calldata)
|
2469
|
+
@calldata = calldata
|
2470
|
+
end
|
2471
|
+
|
2472
|
+
def disasm(fmt)
|
2473
|
+
fmt.instruction("opt_aref", [fmt.calldata(calldata)])
|
2474
|
+
end
|
2475
|
+
|
2476
|
+
def to_a(_iseq)
|
2477
|
+
[:opt_aref, calldata.to_h]
|
2478
|
+
end
|
2479
|
+
|
2480
|
+
def length
|
2481
|
+
2
|
2482
|
+
end
|
2483
|
+
|
2484
|
+
def pops
|
2485
|
+
2
|
2486
|
+
end
|
2487
|
+
|
2488
|
+
def pushes
|
2489
|
+
1
|
2490
|
+
end
|
2491
|
+
|
2492
|
+
def canonical
|
2493
|
+
Send.new(calldata, nil)
|
2494
|
+
end
|
2495
|
+
|
2496
|
+
def call(vm)
|
2497
|
+
canonical.call(vm)
|
2498
|
+
end
|
2499
|
+
end
|
2500
|
+
|
2501
|
+
# ### Summary
|
2502
|
+
#
|
2503
|
+
# `opt_aref_with` is a specialization of the `opt_aref` instruction that
|
2504
|
+
# occurs when the `[]` operator is used with a string argument known at
|
2505
|
+
# compile time. There are fast paths if the receiver is a hash. It pops the
|
2506
|
+
# receiver off the stack and pushes on the result.
|
2507
|
+
#
|
2508
|
+
# ### Usage
|
2509
|
+
#
|
2510
|
+
# ~~~ruby
|
2511
|
+
# { 'test' => true }['test']
|
2512
|
+
# ~~~
|
2513
|
+
#
|
2514
|
+
class OptArefWith
|
2515
|
+
attr_reader :object, :calldata
|
2516
|
+
|
2517
|
+
def initialize(object, calldata)
|
2518
|
+
@object = object
|
2519
|
+
@calldata = calldata
|
2520
|
+
end
|
2521
|
+
|
2522
|
+
def disasm(fmt)
|
2523
|
+
fmt.instruction(
|
2524
|
+
"opt_aref_with",
|
2525
|
+
[fmt.object(object), fmt.calldata(calldata)]
|
2526
|
+
)
|
2527
|
+
end
|
2528
|
+
|
2529
|
+
def to_a(_iseq)
|
2530
|
+
[:opt_aref_with, object, calldata.to_h]
|
2531
|
+
end
|
2532
|
+
|
2533
|
+
def length
|
2534
|
+
3
|
2535
|
+
end
|
2536
|
+
|
2537
|
+
def pops
|
2538
|
+
1
|
2539
|
+
end
|
2540
|
+
|
2541
|
+
def pushes
|
2542
|
+
1
|
2543
|
+
end
|
2544
|
+
|
2545
|
+
def canonical
|
2546
|
+
self
|
2547
|
+
end
|
2548
|
+
|
2549
|
+
def call(vm)
|
2550
|
+
vm.push(vm.pop[object])
|
2551
|
+
end
|
2552
|
+
end
|
2553
|
+
|
2554
|
+
# ### Summary
|
2555
|
+
#
|
2556
|
+
# `opt_aset` is an instruction for setting the hash value by the key in
|
2557
|
+
# the `recv[obj] = set` format. It is a specialization of the
|
2558
|
+
# `opt_send_without_block` instruction. It pops the receiver, the key, and
|
2559
|
+
# the value off the stack and pushes on the result.
|
2560
|
+
#
|
2561
|
+
# ### Usage
|
2562
|
+
#
|
2563
|
+
# ~~~ruby
|
2564
|
+
# {}[:key] = value
|
2565
|
+
# ~~~
|
2566
|
+
#
|
2567
|
+
class OptAset
|
2568
|
+
attr_reader :calldata
|
2569
|
+
|
2570
|
+
def initialize(calldata)
|
2571
|
+
@calldata = calldata
|
2572
|
+
end
|
2573
|
+
|
2574
|
+
def disasm(fmt)
|
2575
|
+
fmt.instruction("opt_aset", [fmt.calldata(calldata)])
|
2576
|
+
end
|
2577
|
+
|
2578
|
+
def to_a(_iseq)
|
2579
|
+
[:opt_aset, calldata.to_h]
|
2580
|
+
end
|
2581
|
+
|
2582
|
+
def length
|
2583
|
+
2
|
2584
|
+
end
|
2585
|
+
|
2586
|
+
def pops
|
2587
|
+
3
|
2588
|
+
end
|
2589
|
+
|
2590
|
+
def pushes
|
2591
|
+
1
|
2592
|
+
end
|
2593
|
+
|
2594
|
+
def canonical
|
2595
|
+
Send.new(calldata, nil)
|
2596
|
+
end
|
2597
|
+
|
2598
|
+
def call(vm)
|
2599
|
+
canonical.call(vm)
|
2600
|
+
end
|
2601
|
+
end
|
2602
|
+
|
2603
|
+
# ### Summary
|
2604
|
+
#
|
2605
|
+
# `opt_aset_with` is an instruction for setting the hash value by the known
|
2606
|
+
# string key in the `recv[obj] = set` format. It pops the receiver and the
|
2607
|
+
# value off the stack and pushes on the result.
|
2608
|
+
#
|
2609
|
+
# ### Usage
|
2610
|
+
#
|
2611
|
+
# ~~~ruby
|
2612
|
+
# {}["key"] = value
|
2613
|
+
# ~~~
|
2614
|
+
#
|
2615
|
+
class OptAsetWith
|
2616
|
+
attr_reader :object, :calldata
|
2617
|
+
|
2618
|
+
def initialize(object, calldata)
|
2619
|
+
@object = object
|
2620
|
+
@calldata = calldata
|
2621
|
+
end
|
2622
|
+
|
2623
|
+
def disasm(fmt)
|
2624
|
+
fmt.instruction(
|
2625
|
+
"opt_aset_with",
|
2626
|
+
[fmt.object(object), fmt.calldata(calldata)]
|
2627
|
+
)
|
2628
|
+
end
|
2629
|
+
|
2630
|
+
def to_a(_iseq)
|
2631
|
+
[:opt_aset_with, object, calldata.to_h]
|
2632
|
+
end
|
2633
|
+
|
2634
|
+
def length
|
2635
|
+
3
|
2636
|
+
end
|
2637
|
+
|
2638
|
+
def pops
|
2639
|
+
2
|
2640
|
+
end
|
2641
|
+
|
2642
|
+
def pushes
|
2643
|
+
1
|
2644
|
+
end
|
2645
|
+
|
2646
|
+
def canonical
|
2647
|
+
self
|
2648
|
+
end
|
2649
|
+
|
2650
|
+
def call(vm)
|
2651
|
+
hash, value = vm.pop(2)
|
2652
|
+
vm.push(hash[object] = value)
|
2653
|
+
end
|
2654
|
+
end
|
2655
|
+
|
2656
|
+
# ### Summary
|
2657
|
+
#
|
2658
|
+
# `opt_case_dispatch` is a branch instruction that moves the control flow
|
2659
|
+
# for case statements that have clauses where they can all be used as hash
|
2660
|
+
# keys for an internal hash.
|
2661
|
+
#
|
2662
|
+
# It has two arguments: the `case_dispatch_hash` and an `else_label`. It
|
2663
|
+
# pops one value off the stack: a hash key. `opt_case_dispatch` looks up the
|
2664
|
+
# key in the `case_dispatch_hash` and jumps to the corresponding label if
|
2665
|
+
# there is one. If there is no value in the `case_dispatch_hash`,
|
2666
|
+
# `opt_case_dispatch` jumps to the `else_label` index.
|
2667
|
+
#
|
2668
|
+
# ### Usage
|
2669
|
+
#
|
2670
|
+
# ~~~ruby
|
2671
|
+
# case 1
|
2672
|
+
# when 1
|
2673
|
+
# puts "foo"
|
2674
|
+
# else
|
2675
|
+
# puts "bar"
|
2676
|
+
# end
|
2677
|
+
# ~~~
|
2678
|
+
#
|
2679
|
+
class OptCaseDispatch
|
2680
|
+
attr_reader :case_dispatch_hash, :else_label
|
2681
|
+
|
2682
|
+
def initialize(case_dispatch_hash, else_label)
|
2683
|
+
@case_dispatch_hash = case_dispatch_hash
|
2684
|
+
@else_label = else_label
|
2685
|
+
end
|
2686
|
+
|
2687
|
+
def disasm(fmt)
|
2688
|
+
fmt.instruction(
|
2689
|
+
"opt_case_dispatch",
|
2690
|
+
["<cdhash>", fmt.label(else_label)]
|
2691
|
+
)
|
2692
|
+
end
|
2693
|
+
|
2694
|
+
def to_a(_iseq)
|
2695
|
+
[
|
2696
|
+
:opt_case_dispatch,
|
2697
|
+
case_dispatch_hash.flat_map { |key, value| [key, value.name] },
|
2698
|
+
else_label.name
|
2699
|
+
]
|
2700
|
+
end
|
2701
|
+
|
2702
|
+
def length
|
2703
|
+
3
|
2704
|
+
end
|
2705
|
+
|
2706
|
+
def pops
|
2707
|
+
1
|
2708
|
+
end
|
2709
|
+
|
2710
|
+
def pushes
|
2711
|
+
0
|
2712
|
+
end
|
2713
|
+
|
2714
|
+
def canonical
|
2715
|
+
self
|
2716
|
+
end
|
2717
|
+
|
2718
|
+
def call(vm)
|
2719
|
+
vm.jump(case_dispatch_hash.fetch(vm.pop, else_label))
|
2720
|
+
end
|
2721
|
+
end
|
2722
|
+
|
2723
|
+
# ### Summary
|
2724
|
+
#
|
2725
|
+
# `opt_div` is a specialization of the `opt_send_without_block` instruction
|
2726
|
+
# that occurs when the `/` operator is used. There are fast paths for if
|
2727
|
+
# both operands are integers, or if both operands are floats. It pops both
|
2728
|
+
# the receiver and the argument off the stack and pushes on the result.
|
2729
|
+
#
|
2730
|
+
# ### Usage
|
2731
|
+
#
|
2732
|
+
# ~~~ruby
|
2733
|
+
# 2 / 3
|
2734
|
+
# ~~~
|
2735
|
+
#
|
2736
|
+
class OptDiv
|
2737
|
+
attr_reader :calldata
|
2738
|
+
|
2739
|
+
def initialize(calldata)
|
2740
|
+
@calldata = calldata
|
2741
|
+
end
|
2742
|
+
|
2743
|
+
def disasm(fmt)
|
2744
|
+
fmt.instruction("opt_div", [fmt.calldata(calldata)])
|
2745
|
+
end
|
2746
|
+
|
2747
|
+
def to_a(_iseq)
|
2748
|
+
[:opt_div, calldata.to_h]
|
2749
|
+
end
|
2750
|
+
|
2751
|
+
def length
|
2752
|
+
2
|
2753
|
+
end
|
2754
|
+
|
2755
|
+
def pops
|
2756
|
+
2
|
2757
|
+
end
|
2758
|
+
|
2759
|
+
def pushes
|
2760
|
+
1
|
2761
|
+
end
|
2762
|
+
|
2763
|
+
def canonical
|
2764
|
+
Send.new(calldata, nil)
|
2765
|
+
end
|
2766
|
+
|
2767
|
+
def call(vm)
|
2768
|
+
canonical.call(vm)
|
2769
|
+
end
|
2770
|
+
end
|
2771
|
+
|
2772
|
+
# ### Summary
|
2773
|
+
#
|
2774
|
+
# `opt_empty_p` is an optimization applied when the method `empty?` is
|
2775
|
+
# called. It pops the receiver off the stack and pushes on the result of the
|
2776
|
+
# method call.
|
2777
|
+
#
|
2778
|
+
# ### Usage
|
2779
|
+
#
|
2780
|
+
# ~~~ruby
|
2781
|
+
# "".empty?
|
2782
|
+
# ~~~
|
2783
|
+
#
|
2784
|
+
class OptEmptyP
|
2785
|
+
attr_reader :calldata
|
2786
|
+
|
2787
|
+
def initialize(calldata)
|
2788
|
+
@calldata = calldata
|
2789
|
+
end
|
2790
|
+
|
2791
|
+
def disasm(fmt)
|
2792
|
+
fmt.instruction("opt_empty_p", [fmt.calldata(calldata)])
|
2793
|
+
end
|
2794
|
+
|
2795
|
+
def to_a(_iseq)
|
2796
|
+
[:opt_empty_p, calldata.to_h]
|
2797
|
+
end
|
2798
|
+
|
2799
|
+
def length
|
2800
|
+
2
|
2801
|
+
end
|
2802
|
+
|
2803
|
+
def pops
|
2804
|
+
1
|
2805
|
+
end
|
2806
|
+
|
2807
|
+
def pushes
|
2808
|
+
1
|
2809
|
+
end
|
2810
|
+
|
2811
|
+
def canonical
|
2812
|
+
Send.new(calldata, nil)
|
2813
|
+
end
|
2814
|
+
|
2815
|
+
def call(vm)
|
2816
|
+
canonical.call(vm)
|
2817
|
+
end
|
2818
|
+
end
|
2819
|
+
|
2820
|
+
# ### Summary
|
2821
|
+
#
|
2822
|
+
# `opt_eq` is a specialization of the `opt_send_without_block` instruction
|
2823
|
+
# that occurs when the == operator is used. Fast paths exist when both
|
2824
|
+
# operands are integers, floats, symbols or strings. It pops both the
|
2825
|
+
# receiver and the argument off the stack and pushes on the result.
|
2826
|
+
#
|
2827
|
+
# ### Usage
|
2828
|
+
#
|
2829
|
+
# ~~~ruby
|
2830
|
+
# 2 == 2
|
2831
|
+
# ~~~
|
2832
|
+
#
|
2833
|
+
class OptEq
|
2834
|
+
attr_reader :calldata
|
2835
|
+
|
2836
|
+
def initialize(calldata)
|
2837
|
+
@calldata = calldata
|
2838
|
+
end
|
2839
|
+
|
2840
|
+
def disasm(fmt)
|
2841
|
+
fmt.instruction("opt_eq", [fmt.calldata(calldata)])
|
2842
|
+
end
|
2843
|
+
|
2844
|
+
def to_a(_iseq)
|
2845
|
+
[:opt_eq, calldata.to_h]
|
2846
|
+
end
|
2847
|
+
|
2848
|
+
def length
|
2849
|
+
2
|
2850
|
+
end
|
2851
|
+
|
2852
|
+
def pops
|
2853
|
+
2
|
2854
|
+
end
|
2855
|
+
|
2856
|
+
def pushes
|
2857
|
+
1
|
2858
|
+
end
|
2859
|
+
|
2860
|
+
def canonical
|
2861
|
+
Send.new(calldata, nil)
|
2862
|
+
end
|
2863
|
+
|
2864
|
+
def call(vm)
|
2865
|
+
canonical.call(vm)
|
2866
|
+
end
|
2867
|
+
end
|
2868
|
+
|
2869
|
+
# ### Summary
|
2870
|
+
#
|
2871
|
+
# `opt_ge` is a specialization of the `opt_send_without_block` instruction
|
2872
|
+
# that occurs when the >= operator is used. Fast paths exist when both
|
2873
|
+
# operands are integers or floats. It pops both the receiver and the
|
2874
|
+
# argument off the stack and pushes on the result.
|
2875
|
+
#
|
2876
|
+
# ### Usage
|
2877
|
+
#
|
2878
|
+
# ~~~ruby
|
2879
|
+
# 4 >= 3
|
2880
|
+
# ~~~
|
2881
|
+
#
|
2882
|
+
class OptGE
|
2883
|
+
attr_reader :calldata
|
2884
|
+
|
2885
|
+
def initialize(calldata)
|
2886
|
+
@calldata = calldata
|
2887
|
+
end
|
2888
|
+
|
2889
|
+
def disasm(fmt)
|
2890
|
+
fmt.instruction("opt_ge", [fmt.calldata(calldata)])
|
2891
|
+
end
|
2892
|
+
|
2893
|
+
def to_a(_iseq)
|
2894
|
+
[:opt_ge, calldata.to_h]
|
2895
|
+
end
|
2896
|
+
|
2897
|
+
def length
|
2898
|
+
2
|
2899
|
+
end
|
2900
|
+
|
2901
|
+
def pops
|
2902
|
+
2
|
2903
|
+
end
|
2904
|
+
|
2905
|
+
def pushes
|
2906
|
+
1
|
2907
|
+
end
|
2908
|
+
|
2909
|
+
def canonical
|
2910
|
+
Send.new(calldata, nil)
|
2911
|
+
end
|
2912
|
+
|
2913
|
+
def call(vm)
|
2914
|
+
canonical.call(vm)
|
2915
|
+
end
|
2916
|
+
end
|
2917
|
+
|
2918
|
+
# ### Summary
|
2919
|
+
#
|
2920
|
+
# `opt_getconstant_path` performs a constant lookup on a chain of constant
|
2921
|
+
# names. It accepts as its argument an array of constant names, and pushes
|
2922
|
+
# the value of the constant onto the stack.
|
2923
|
+
#
|
2924
|
+
# ### Usage
|
2925
|
+
#
|
2926
|
+
# ~~~ruby
|
2927
|
+
# ::Object
|
2928
|
+
# ~~~
|
2929
|
+
#
|
2930
|
+
class OptGetConstantPath
|
2931
|
+
attr_reader :names
|
2932
|
+
|
2933
|
+
def initialize(names)
|
2934
|
+
@names = names
|
2935
|
+
end
|
2936
|
+
|
2937
|
+
def disasm(fmt)
|
2938
|
+
cache = "<ic:0 #{names.join("::")}>"
|
2939
|
+
fmt.instruction("opt_getconstant_path", [cache])
|
2940
|
+
end
|
2941
|
+
|
2942
|
+
def to_a(_iseq)
|
2943
|
+
[:opt_getconstant_path, names]
|
2944
|
+
end
|
2945
|
+
|
2946
|
+
def length
|
2947
|
+
2
|
2948
|
+
end
|
2949
|
+
|
2950
|
+
def pops
|
2951
|
+
0
|
2952
|
+
end
|
2953
|
+
|
2954
|
+
def pushes
|
2955
|
+
1
|
2956
|
+
end
|
2957
|
+
|
2958
|
+
def canonical
|
2959
|
+
self
|
2960
|
+
end
|
2961
|
+
|
2962
|
+
def call(vm)
|
2963
|
+
current = vm._self
|
2964
|
+
current = current.class unless current.is_a?(Class)
|
2965
|
+
|
2966
|
+
names.each do |name|
|
2967
|
+
current = name == :"" ? Object : current.const_get(name)
|
2968
|
+
end
|
2969
|
+
|
2970
|
+
vm.push(current)
|
2971
|
+
end
|
2972
|
+
end
|
2973
|
+
|
2974
|
+
# ### Summary
|
2975
|
+
#
|
2976
|
+
# `opt_gt` is a specialization of the `opt_send_without_block` instruction
|
2977
|
+
# that occurs when the > operator is used. Fast paths exist when both
|
2978
|
+
# operands are integers or floats. It pops both the receiver and the
|
2979
|
+
# argument off the stack and pushes on the result.
|
2980
|
+
#
|
2981
|
+
# ### Usage
|
2982
|
+
#
|
2983
|
+
# ~~~ruby
|
2984
|
+
# 4 > 3
|
2985
|
+
# ~~~
|
2986
|
+
#
|
2987
|
+
class OptGT
|
2988
|
+
attr_reader :calldata
|
2989
|
+
|
2990
|
+
def initialize(calldata)
|
2991
|
+
@calldata = calldata
|
2992
|
+
end
|
2993
|
+
|
2994
|
+
def disasm(fmt)
|
2995
|
+
fmt.instruction("opt_gt", [fmt.calldata(calldata)])
|
2996
|
+
end
|
2997
|
+
|
2998
|
+
def to_a(_iseq)
|
2999
|
+
[:opt_gt, calldata.to_h]
|
3000
|
+
end
|
3001
|
+
|
3002
|
+
def length
|
3003
|
+
2
|
3004
|
+
end
|
3005
|
+
|
3006
|
+
def pops
|
3007
|
+
2
|
3008
|
+
end
|
3009
|
+
|
3010
|
+
def pushes
|
3011
|
+
1
|
3012
|
+
end
|
3013
|
+
|
3014
|
+
def canonical
|
3015
|
+
Send.new(calldata, nil)
|
3016
|
+
end
|
3017
|
+
|
3018
|
+
def call(vm)
|
3019
|
+
canonical.call(vm)
|
3020
|
+
end
|
3021
|
+
end
|
3022
|
+
|
3023
|
+
# ### Summary
|
3024
|
+
#
|
3025
|
+
# `opt_le` is a specialization of the `opt_send_without_block` instruction
|
3026
|
+
# that occurs when the <= operator is used. Fast paths exist when both
|
3027
|
+
# operands are integers or floats. It pops both the receiver and the
|
3028
|
+
# argument off the stack and pushes on the result.
|
3029
|
+
#
|
3030
|
+
# ### Usage
|
3031
|
+
#
|
3032
|
+
# ~~~ruby
|
3033
|
+
# 3 <= 4
|
3034
|
+
# ~~~
|
3035
|
+
#
|
3036
|
+
class OptLE
|
3037
|
+
attr_reader :calldata
|
3038
|
+
|
3039
|
+
def initialize(calldata)
|
3040
|
+
@calldata = calldata
|
3041
|
+
end
|
3042
|
+
|
3043
|
+
def disasm(fmt)
|
3044
|
+
fmt.instruction("opt_le", [fmt.calldata(calldata)])
|
3045
|
+
end
|
3046
|
+
|
3047
|
+
def to_a(_iseq)
|
3048
|
+
[:opt_le, calldata.to_h]
|
3049
|
+
end
|
3050
|
+
|
3051
|
+
def length
|
3052
|
+
2
|
3053
|
+
end
|
3054
|
+
|
3055
|
+
def pops
|
3056
|
+
2
|
3057
|
+
end
|
3058
|
+
|
3059
|
+
def pushes
|
3060
|
+
1
|
3061
|
+
end
|
3062
|
+
|
3063
|
+
def canonical
|
3064
|
+
Send.new(calldata, nil)
|
3065
|
+
end
|
3066
|
+
|
3067
|
+
def call(vm)
|
3068
|
+
canonical.call(vm)
|
3069
|
+
end
|
3070
|
+
end
|
3071
|
+
|
3072
|
+
# ### Summary
|
3073
|
+
#
|
3074
|
+
# `opt_length` is a specialization of `opt_send_without_block`, when the
|
3075
|
+
# `length` method is called. There are fast paths when the receiver is
|
3076
|
+
# either a string, hash, or array. It pops the receiver off the stack and
|
3077
|
+
# pushes on the result of the method call.
|
3078
|
+
#
|
3079
|
+
# ### Usage
|
3080
|
+
#
|
3081
|
+
# ~~~ruby
|
3082
|
+
# "".length
|
3083
|
+
# ~~~
|
3084
|
+
#
|
3085
|
+
class OptLength
|
3086
|
+
attr_reader :calldata
|
3087
|
+
|
3088
|
+
def initialize(calldata)
|
3089
|
+
@calldata = calldata
|
3090
|
+
end
|
3091
|
+
|
3092
|
+
def disasm(fmt)
|
3093
|
+
fmt.instruction("opt_length", [fmt.calldata(calldata)])
|
3094
|
+
end
|
3095
|
+
|
3096
|
+
def to_a(_iseq)
|
3097
|
+
[:opt_length, calldata.to_h]
|
3098
|
+
end
|
3099
|
+
|
3100
|
+
def length
|
3101
|
+
2
|
3102
|
+
end
|
3103
|
+
|
3104
|
+
def pops
|
3105
|
+
1
|
3106
|
+
end
|
3107
|
+
|
3108
|
+
def pushes
|
3109
|
+
1
|
3110
|
+
end
|
3111
|
+
|
3112
|
+
def canonical
|
3113
|
+
Send.new(calldata, nil)
|
3114
|
+
end
|
3115
|
+
|
3116
|
+
def call(vm)
|
3117
|
+
canonical.call(vm)
|
3118
|
+
end
|
3119
|
+
end
|
3120
|
+
|
3121
|
+
# ### Summary
|
3122
|
+
#
|
3123
|
+
# `opt_lt` is a specialization of the `opt_send_without_block` instruction
|
3124
|
+
# that occurs when the < operator is used. Fast paths exist when both
|
3125
|
+
# operands are integers or floats. It pops both the receiver and the
|
3126
|
+
# argument off the stack and pushes on the result.
|
3127
|
+
#
|
3128
|
+
# ### Usage
|
3129
|
+
#
|
3130
|
+
# ~~~ruby
|
3131
|
+
# 3 < 4
|
3132
|
+
# ~~~
|
3133
|
+
#
|
3134
|
+
class OptLT
|
3135
|
+
attr_reader :calldata
|
3136
|
+
|
3137
|
+
def initialize(calldata)
|
3138
|
+
@calldata = calldata
|
3139
|
+
end
|
3140
|
+
|
3141
|
+
def disasm(fmt)
|
3142
|
+
fmt.instruction("opt_lt", [fmt.calldata(calldata)])
|
3143
|
+
end
|
3144
|
+
|
3145
|
+
def to_a(_iseq)
|
3146
|
+
[:opt_lt, calldata.to_h]
|
3147
|
+
end
|
3148
|
+
|
3149
|
+
def length
|
3150
|
+
2
|
3151
|
+
end
|
3152
|
+
|
3153
|
+
def pops
|
3154
|
+
2
|
3155
|
+
end
|
3156
|
+
|
3157
|
+
def pushes
|
3158
|
+
1
|
3159
|
+
end
|
3160
|
+
|
3161
|
+
def canonical
|
3162
|
+
Send.new(calldata, nil)
|
3163
|
+
end
|
3164
|
+
|
3165
|
+
def call(vm)
|
3166
|
+
canonical.call(vm)
|
3167
|
+
end
|
3168
|
+
end
|
3169
|
+
|
3170
|
+
# ### Summary
|
3171
|
+
#
|
3172
|
+
# `opt_ltlt` is a specialization of the `opt_send_without_block` instruction
|
3173
|
+
# that occurs when the `<<` operator is used. Fast paths exists when the
|
3174
|
+
# receiver is either a String or an Array. It pops both the receiver and the
|
3175
|
+
# argument off the stack and pushes on the result.
|
3176
|
+
#
|
3177
|
+
# ### Usage
|
3178
|
+
#
|
3179
|
+
# ~~~ruby
|
3180
|
+
# "" << 2
|
3181
|
+
# ~~~
|
3182
|
+
#
|
3183
|
+
class OptLTLT
|
3184
|
+
attr_reader :calldata
|
3185
|
+
|
3186
|
+
def initialize(calldata)
|
3187
|
+
@calldata = calldata
|
3188
|
+
end
|
3189
|
+
|
3190
|
+
def disasm(fmt)
|
3191
|
+
fmt.instruction("opt_ltlt", [fmt.calldata(calldata)])
|
3192
|
+
end
|
3193
|
+
|
3194
|
+
def to_a(_iseq)
|
3195
|
+
[:opt_ltlt, calldata.to_h]
|
3196
|
+
end
|
3197
|
+
|
3198
|
+
def length
|
3199
|
+
2
|
3200
|
+
end
|
3201
|
+
|
3202
|
+
def pops
|
3203
|
+
2
|
3204
|
+
end
|
3205
|
+
|
3206
|
+
def pushes
|
3207
|
+
1
|
3208
|
+
end
|
3209
|
+
|
3210
|
+
def canonical
|
3211
|
+
Send.new(calldata, nil)
|
3212
|
+
end
|
3213
|
+
|
3214
|
+
def call(vm)
|
3215
|
+
canonical.call(vm)
|
3216
|
+
end
|
3217
|
+
end
|
3218
|
+
|
3219
|
+
# ### Summary
|
3220
|
+
#
|
3221
|
+
# `opt_minus` is a specialization of the `opt_send_without_block`
|
3222
|
+
# instruction that occurs when the `-` operator is used. There are fast
|
3223
|
+
# paths for if both operands are integers or if both operands are floats. It
|
3224
|
+
# pops both the receiver and the argument off the stack and pushes on the
|
3225
|
+
# result.
|
3226
|
+
#
|
3227
|
+
# ### Usage
|
3228
|
+
#
|
3229
|
+
# ~~~ruby
|
3230
|
+
# 3 - 2
|
3231
|
+
# ~~~
|
3232
|
+
#
|
3233
|
+
class OptMinus
|
3234
|
+
attr_reader :calldata
|
3235
|
+
|
3236
|
+
def initialize(calldata)
|
3237
|
+
@calldata = calldata
|
3238
|
+
end
|
3239
|
+
|
3240
|
+
def disasm(fmt)
|
3241
|
+
fmt.instruction("opt_minus", [fmt.calldata(calldata)])
|
3242
|
+
end
|
3243
|
+
|
3244
|
+
def to_a(_iseq)
|
3245
|
+
[:opt_minus, calldata.to_h]
|
3246
|
+
end
|
3247
|
+
|
3248
|
+
def length
|
3249
|
+
2
|
3250
|
+
end
|
3251
|
+
|
3252
|
+
def pops
|
3253
|
+
2
|
3254
|
+
end
|
3255
|
+
|
3256
|
+
def pushes
|
3257
|
+
1
|
3258
|
+
end
|
3259
|
+
|
3260
|
+
def canonical
|
3261
|
+
Send.new(calldata, nil)
|
3262
|
+
end
|
3263
|
+
|
3264
|
+
def call(vm)
|
3265
|
+
canonical.call(vm)
|
3266
|
+
end
|
3267
|
+
end
|
3268
|
+
|
3269
|
+
# ### Summary
|
3270
|
+
#
|
3271
|
+
# `opt_mod` is a specialization of the `opt_send_without_block` instruction
|
3272
|
+
# that occurs when the `%` operator is used. There are fast paths for if
|
3273
|
+
# both operands are integers or if both operands are floats. It pops both
|
3274
|
+
# the receiver and the argument off the stack and pushes on the result.
|
3275
|
+
#
|
3276
|
+
# ### Usage
|
3277
|
+
#
|
3278
|
+
# ~~~ruby
|
3279
|
+
# 4 % 2
|
3280
|
+
# ~~~
|
3281
|
+
#
|
3282
|
+
class OptMod
|
3283
|
+
attr_reader :calldata
|
3284
|
+
|
3285
|
+
def initialize(calldata)
|
3286
|
+
@calldata = calldata
|
3287
|
+
end
|
3288
|
+
|
3289
|
+
def disasm(fmt)
|
3290
|
+
fmt.instruction("opt_mod", [fmt.calldata(calldata)])
|
3291
|
+
end
|
3292
|
+
|
3293
|
+
def to_a(_iseq)
|
3294
|
+
[:opt_mod, calldata.to_h]
|
3295
|
+
end
|
3296
|
+
|
3297
|
+
def length
|
3298
|
+
2
|
3299
|
+
end
|
3300
|
+
|
3301
|
+
def pops
|
3302
|
+
2
|
3303
|
+
end
|
3304
|
+
|
3305
|
+
def pushes
|
3306
|
+
1
|
3307
|
+
end
|
3308
|
+
|
3309
|
+
def canonical
|
3310
|
+
Send.new(calldata, nil)
|
3311
|
+
end
|
3312
|
+
|
3313
|
+
def call(vm)
|
3314
|
+
canonical.call(vm)
|
3315
|
+
end
|
3316
|
+
end
|
3317
|
+
|
3318
|
+
# ### Summary
|
3319
|
+
#
|
3320
|
+
# `opt_mult` is a specialization of the `opt_send_without_block` instruction
|
3321
|
+
# that occurs when the `*` operator is used. There are fast paths for if
|
3322
|
+
# both operands are integers or floats. It pops both the receiver and the
|
3323
|
+
# argument off the stack and pushes on the result.
|
3324
|
+
#
|
3325
|
+
# ### Usage
|
3326
|
+
#
|
3327
|
+
# ~~~ruby
|
3328
|
+
# 3 * 2
|
3329
|
+
# ~~~
|
3330
|
+
#
|
3331
|
+
class OptMult
|
3332
|
+
attr_reader :calldata
|
3333
|
+
|
3334
|
+
def initialize(calldata)
|
3335
|
+
@calldata = calldata
|
3336
|
+
end
|
3337
|
+
|
3338
|
+
def disasm(fmt)
|
3339
|
+
fmt.instruction("opt_mult", [fmt.calldata(calldata)])
|
3340
|
+
end
|
3341
|
+
|
3342
|
+
def to_a(_iseq)
|
3343
|
+
[:opt_mult, calldata.to_h]
|
3344
|
+
end
|
3345
|
+
|
3346
|
+
def length
|
3347
|
+
2
|
3348
|
+
end
|
3349
|
+
|
3350
|
+
def pops
|
3351
|
+
2
|
3352
|
+
end
|
3353
|
+
|
3354
|
+
def pushes
|
3355
|
+
1
|
3356
|
+
end
|
3357
|
+
|
3358
|
+
def canonical
|
3359
|
+
Send.new(calldata, nil)
|
3360
|
+
end
|
3361
|
+
|
3362
|
+
def call(vm)
|
3363
|
+
canonical.call(vm)
|
3364
|
+
end
|
3365
|
+
end
|
3366
|
+
|
3367
|
+
# ### Summary
|
3368
|
+
#
|
3369
|
+
# `opt_neq` is an optimization that tests whether two values at the top of
|
3370
|
+
# the stack are not equal by testing their equality and calling the `!` on
|
3371
|
+
# the result. This allows `opt_neq` to use the fast paths optimized in
|
3372
|
+
# `opt_eq` when both operands are Integers, Floats, Symbols, or Strings. It
|
3373
|
+
# pops both the receiver and the argument off the stack and pushes on the
|
3374
|
+
# result.
|
3375
|
+
#
|
3376
|
+
# ### Usage
|
3377
|
+
#
|
3378
|
+
# ~~~ruby
|
3379
|
+
# 2 != 2
|
3380
|
+
# ~~~
|
3381
|
+
#
|
3382
|
+
class OptNEq
|
3383
|
+
attr_reader :eq_calldata, :neq_calldata
|
3384
|
+
|
3385
|
+
def initialize(eq_calldata, neq_calldata)
|
3386
|
+
@eq_calldata = eq_calldata
|
3387
|
+
@neq_calldata = neq_calldata
|
3388
|
+
end
|
3389
|
+
|
3390
|
+
def disasm(fmt)
|
3391
|
+
fmt.instruction(
|
3392
|
+
"opt_neq",
|
3393
|
+
[fmt.calldata(eq_calldata), fmt.calldata(neq_calldata)]
|
3394
|
+
)
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
def to_a(_iseq)
|
3398
|
+
[:opt_neq, eq_calldata.to_h, neq_calldata.to_h]
|
3399
|
+
end
|
3400
|
+
|
3401
|
+
def length
|
3402
|
+
3
|
3403
|
+
end
|
3404
|
+
|
3405
|
+
def pops
|
3406
|
+
2
|
3407
|
+
end
|
3408
|
+
|
3409
|
+
def pushes
|
3410
|
+
1
|
3411
|
+
end
|
3412
|
+
|
3413
|
+
def canonical
|
3414
|
+
self
|
3415
|
+
end
|
3416
|
+
|
3417
|
+
def call(vm)
|
3418
|
+
receiver, argument = vm.pop(2)
|
3419
|
+
vm.push(receiver != argument)
|
3420
|
+
end
|
3421
|
+
end
|
3422
|
+
|
3423
|
+
# ### Summary
|
3424
|
+
#
|
3425
|
+
# `opt_newarray_max` is a specialization that occurs when the `max` method
|
3426
|
+
# is called on an array literal. It pops the values of the array off the
|
3427
|
+
# stack and pushes on the result.
|
3428
|
+
#
|
3429
|
+
# ### Usage
|
3430
|
+
#
|
3431
|
+
# ~~~ruby
|
3432
|
+
# [a, b, c].max
|
3433
|
+
# ~~~
|
3434
|
+
#
|
3435
|
+
class OptNewArrayMax
|
3436
|
+
attr_reader :number
|
3437
|
+
|
3438
|
+
def initialize(number)
|
3439
|
+
@number = number
|
3440
|
+
end
|
3441
|
+
|
3442
|
+
def disasm(fmt)
|
3443
|
+
fmt.instruction("opt_newarray_max", [fmt.object(number)])
|
3444
|
+
end
|
3445
|
+
|
3446
|
+
def to_a(_iseq)
|
3447
|
+
[:opt_newarray_max, number]
|
3448
|
+
end
|
3449
|
+
|
3450
|
+
def length
|
3451
|
+
2
|
3452
|
+
end
|
3453
|
+
|
3454
|
+
def pops
|
3455
|
+
number
|
3456
|
+
end
|
3457
|
+
|
3458
|
+
def pushes
|
3459
|
+
1
|
3460
|
+
end
|
3461
|
+
|
3462
|
+
def canonical
|
3463
|
+
self
|
3464
|
+
end
|
3465
|
+
|
3466
|
+
def call(vm)
|
3467
|
+
vm.push(vm.pop(number).max)
|
3468
|
+
end
|
3469
|
+
end
|
3470
|
+
|
3471
|
+
# ### Summary
|
3472
|
+
#
|
3473
|
+
# `opt_newarray_min` is a specialization that occurs when the `min` method
|
3474
|
+
# is called on an array literal. It pops the values of the array off the
|
3475
|
+
# stack and pushes on the result.
|
3476
|
+
#
|
3477
|
+
# ### Usage
|
3478
|
+
#
|
3479
|
+
# ~~~ruby
|
3480
|
+
# [a, b, c].min
|
3481
|
+
# ~~~
|
3482
|
+
#
|
3483
|
+
class OptNewArrayMin
|
3484
|
+
attr_reader :number
|
3485
|
+
|
3486
|
+
def initialize(number)
|
3487
|
+
@number = number
|
3488
|
+
end
|
3489
|
+
|
3490
|
+
def disasm(fmt)
|
3491
|
+
fmt.instruction("opt_newarray_min", [fmt.object(number)])
|
3492
|
+
end
|
3493
|
+
|
3494
|
+
def to_a(_iseq)
|
3495
|
+
[:opt_newarray_min, number]
|
3496
|
+
end
|
3497
|
+
|
3498
|
+
def length
|
3499
|
+
2
|
3500
|
+
end
|
3501
|
+
|
3502
|
+
def pops
|
3503
|
+
number
|
3504
|
+
end
|
3505
|
+
|
3506
|
+
def pushes
|
3507
|
+
1
|
3508
|
+
end
|
3509
|
+
|
3510
|
+
def canonical
|
3511
|
+
self
|
3512
|
+
end
|
3513
|
+
|
3514
|
+
def call(vm)
|
3515
|
+
vm.push(vm.pop(number).min)
|
3516
|
+
end
|
3517
|
+
end
|
3518
|
+
|
3519
|
+
# ### Summary
|
3520
|
+
#
|
3521
|
+
# `opt_nil_p` is an optimization applied when the method `nil?` is called.
|
3522
|
+
# It returns true immediately when the receiver is `nil` and defers to the
|
3523
|
+
# `nil?` method in other cases. It pops the receiver off the stack and
|
3524
|
+
# pushes on the result.
|
3525
|
+
#
|
3526
|
+
# ### Usage
|
3527
|
+
#
|
3528
|
+
# ~~~ruby
|
3529
|
+
# "".nil?
|
3530
|
+
# ~~~
|
3531
|
+
#
|
3532
|
+
class OptNilP
|
3533
|
+
attr_reader :calldata
|
3534
|
+
|
3535
|
+
def initialize(calldata)
|
3536
|
+
@calldata = calldata
|
3537
|
+
end
|
3538
|
+
|
3539
|
+
def disasm(fmt)
|
3540
|
+
fmt.instruction("opt_nil_p", [fmt.calldata(calldata)])
|
3541
|
+
end
|
3542
|
+
|
3543
|
+
def to_a(_iseq)
|
3544
|
+
[:opt_nil_p, calldata.to_h]
|
3545
|
+
end
|
3546
|
+
|
3547
|
+
def length
|
3548
|
+
2
|
3549
|
+
end
|
3550
|
+
|
3551
|
+
def pops
|
3552
|
+
1
|
3553
|
+
end
|
3554
|
+
|
3555
|
+
def pushes
|
3556
|
+
1
|
3557
|
+
end
|
3558
|
+
|
3559
|
+
def canonical
|
3560
|
+
Send.new(calldata, nil)
|
3561
|
+
end
|
3562
|
+
|
3563
|
+
def call(vm)
|
3564
|
+
canonical.call(vm)
|
3565
|
+
end
|
3566
|
+
end
|
3567
|
+
|
3568
|
+
# ### Summary
|
3569
|
+
#
|
3570
|
+
# `opt_not` negates the value on top of the stack by calling the `!` method
|
3571
|
+
# on it. It pops the receiver off the stack and pushes on the result.
|
3572
|
+
#
|
3573
|
+
# ### Usage
|
3574
|
+
#
|
3575
|
+
# ~~~ruby
|
3576
|
+
# !true
|
3577
|
+
# ~~~
|
3578
|
+
#
|
3579
|
+
class OptNot
|
3580
|
+
attr_reader :calldata
|
3581
|
+
|
3582
|
+
def initialize(calldata)
|
3583
|
+
@calldata = calldata
|
3584
|
+
end
|
3585
|
+
|
3586
|
+
def disasm(fmt)
|
3587
|
+
fmt.instruction("opt_not", [fmt.calldata(calldata)])
|
3588
|
+
end
|
3589
|
+
|
3590
|
+
def to_a(_iseq)
|
3591
|
+
[:opt_not, calldata.to_h]
|
3592
|
+
end
|
3593
|
+
|
3594
|
+
def length
|
3595
|
+
2
|
3596
|
+
end
|
3597
|
+
|
3598
|
+
def pops
|
3599
|
+
1
|
3600
|
+
end
|
3601
|
+
|
3602
|
+
def pushes
|
3603
|
+
1
|
3604
|
+
end
|
3605
|
+
|
3606
|
+
def canonical
|
3607
|
+
Send.new(calldata, nil)
|
3608
|
+
end
|
3609
|
+
|
3610
|
+
def call(vm)
|
3611
|
+
canonical.call(vm)
|
3612
|
+
end
|
3613
|
+
end
|
3614
|
+
|
3615
|
+
# ### Summary
|
3616
|
+
#
|
3617
|
+
# `opt_or` is a specialization of the `opt_send_without_block` instruction
|
3618
|
+
# that occurs when the `|` operator is used. There is a fast path for if
|
3619
|
+
# both operands are integers. It pops both the receiver and the argument off
|
3620
|
+
# the stack and pushes on the result.
|
3621
|
+
#
|
3622
|
+
# ### Usage
|
3623
|
+
#
|
3624
|
+
# ~~~ruby
|
3625
|
+
# 2 | 3
|
3626
|
+
# ~~~
|
3627
|
+
#
|
3628
|
+
class OptOr
|
3629
|
+
attr_reader :calldata
|
3630
|
+
|
3631
|
+
def initialize(calldata)
|
3632
|
+
@calldata = calldata
|
3633
|
+
end
|
3634
|
+
|
3635
|
+
def disasm(fmt)
|
3636
|
+
fmt.instruction("opt_or", [fmt.calldata(calldata)])
|
3637
|
+
end
|
3638
|
+
|
3639
|
+
def to_a(_iseq)
|
3640
|
+
[:opt_or, calldata.to_h]
|
3641
|
+
end
|
3642
|
+
|
3643
|
+
def length
|
3644
|
+
2
|
3645
|
+
end
|
3646
|
+
|
3647
|
+
def pops
|
3648
|
+
2
|
3649
|
+
end
|
3650
|
+
|
3651
|
+
def pushes
|
3652
|
+
1
|
3653
|
+
end
|
3654
|
+
|
3655
|
+
def canonical
|
3656
|
+
Send.new(calldata, nil)
|
3657
|
+
end
|
3658
|
+
|
3659
|
+
def call(vm)
|
3660
|
+
canonical.call(vm)
|
3661
|
+
end
|
3662
|
+
end
|
3663
|
+
|
3664
|
+
# ### Summary
|
3665
|
+
#
|
3666
|
+
# `opt_plus` is a specialization of the `opt_send_without_block` instruction
|
3667
|
+
# that occurs when the `+` operator is used. There are fast paths for if
|
3668
|
+
# both operands are integers, floats, strings, or arrays. It pops both the
|
3669
|
+
# receiver and the argument off the stack and pushes on the result.
|
3670
|
+
#
|
3671
|
+
# ### Usage
|
3672
|
+
#
|
3673
|
+
# ~~~ruby
|
3674
|
+
# 2 + 3
|
3675
|
+
# ~~~
|
3676
|
+
#
|
3677
|
+
class OptPlus
|
3678
|
+
attr_reader :calldata
|
3679
|
+
|
3680
|
+
def initialize(calldata)
|
3681
|
+
@calldata = calldata
|
3682
|
+
end
|
3683
|
+
|
3684
|
+
def disasm(fmt)
|
3685
|
+
fmt.instruction("opt_plus", [fmt.calldata(calldata)])
|
3686
|
+
end
|
3687
|
+
|
3688
|
+
def to_a(_iseq)
|
3689
|
+
[:opt_plus, calldata.to_h]
|
3690
|
+
end
|
3691
|
+
|
3692
|
+
def length
|
3693
|
+
2
|
3694
|
+
end
|
3695
|
+
|
3696
|
+
def pops
|
3697
|
+
2
|
3698
|
+
end
|
3699
|
+
|
3700
|
+
def pushes
|
3701
|
+
1
|
3702
|
+
end
|
3703
|
+
|
3704
|
+
def canonical
|
3705
|
+
Send.new(calldata, nil)
|
3706
|
+
end
|
3707
|
+
|
3708
|
+
def call(vm)
|
3709
|
+
canonical.call(vm)
|
3710
|
+
end
|
3711
|
+
end
|
3712
|
+
|
3713
|
+
# ### Summary
|
3714
|
+
#
|
3715
|
+
# `opt_regexpmatch2` is a specialization of the `opt_send_without_block`
|
3716
|
+
# instruction that occurs when the `=~` operator is used. It pops both the
|
3717
|
+
# receiver and the argument off the stack and pushes on the result.
|
3718
|
+
#
|
3719
|
+
# ### Usage
|
3720
|
+
#
|
3721
|
+
# ~~~ruby
|
3722
|
+
# /a/ =~ "a"
|
3723
|
+
# ~~~
|
3724
|
+
#
|
3725
|
+
class OptRegExpMatch2
|
3726
|
+
attr_reader :calldata
|
3727
|
+
|
3728
|
+
def initialize(calldata)
|
3729
|
+
@calldata = calldata
|
3730
|
+
end
|
3731
|
+
|
3732
|
+
def disasm(fmt)
|
3733
|
+
fmt.instruction("opt_regexpmatch2", [fmt.calldata(calldata)])
|
3734
|
+
end
|
3735
|
+
|
3736
|
+
def to_a(_iseq)
|
3737
|
+
[:opt_regexpmatch2, calldata.to_h]
|
3738
|
+
end
|
3739
|
+
|
3740
|
+
def length
|
3741
|
+
2
|
3742
|
+
end
|
3743
|
+
|
3744
|
+
def pops
|
3745
|
+
2
|
3746
|
+
end
|
3747
|
+
|
3748
|
+
def pushes
|
3749
|
+
1
|
3750
|
+
end
|
3751
|
+
|
3752
|
+
def canonical
|
3753
|
+
Send.new(calldata, nil)
|
3754
|
+
end
|
3755
|
+
|
3756
|
+
def call(vm)
|
3757
|
+
canonical.call(vm)
|
3758
|
+
end
|
3759
|
+
end
|
3760
|
+
|
3761
|
+
# ### Summary
|
3762
|
+
#
|
3763
|
+
# `opt_send_without_block` is a specialization of the send instruction that
|
3764
|
+
# occurs when a method is being called without a block. It pops the receiver
|
3765
|
+
# and the arguments off the stack and pushes on the result.
|
3766
|
+
#
|
3767
|
+
# ### Usage
|
3768
|
+
#
|
3769
|
+
# ~~~ruby
|
3770
|
+
# puts "Hello, world!"
|
3771
|
+
# ~~~
|
3772
|
+
#
|
3773
|
+
class OptSendWithoutBlock
|
3774
|
+
attr_reader :calldata
|
3775
|
+
|
3776
|
+
def initialize(calldata)
|
3777
|
+
@calldata = calldata
|
3778
|
+
end
|
3779
|
+
|
3780
|
+
def disasm(fmt)
|
3781
|
+
fmt.instruction("opt_send_without_block", [fmt.calldata(calldata)])
|
3782
|
+
end
|
3783
|
+
|
3784
|
+
def to_a(_iseq)
|
3785
|
+
[:opt_send_without_block, calldata.to_h]
|
3786
|
+
end
|
3787
|
+
|
3788
|
+
def length
|
3789
|
+
2
|
3790
|
+
end
|
3791
|
+
|
3792
|
+
def pops
|
3793
|
+
1 + calldata.argc
|
3794
|
+
end
|
3795
|
+
|
3796
|
+
def pushes
|
3797
|
+
1
|
3798
|
+
end
|
3799
|
+
|
3800
|
+
def canonical
|
3801
|
+
Send.new(calldata, nil)
|
3802
|
+
end
|
3803
|
+
|
3804
|
+
def call(vm)
|
3805
|
+
canonical.call(vm)
|
3806
|
+
end
|
3807
|
+
end
|
3808
|
+
|
3809
|
+
# ### Summary
|
3810
|
+
#
|
3811
|
+
# `opt_size` is a specialization of `opt_send_without_block`, when the
|
3812
|
+
# `size` method is called. There are fast paths when the receiver is either
|
3813
|
+
# a string, hash, or array. It pops the receiver off the stack and pushes on
|
3814
|
+
# the result.
|
3815
|
+
#
|
3816
|
+
# ### Usage
|
3817
|
+
#
|
3818
|
+
# ~~~ruby
|
3819
|
+
# "".size
|
3820
|
+
# ~~~
|
3821
|
+
#
|
3822
|
+
class OptSize
|
3823
|
+
attr_reader :calldata
|
3824
|
+
|
3825
|
+
def initialize(calldata)
|
3826
|
+
@calldata = calldata
|
3827
|
+
end
|
3828
|
+
|
3829
|
+
def disasm(fmt)
|
3830
|
+
fmt.instruction("opt_size", [fmt.calldata(calldata)])
|
3831
|
+
end
|
3832
|
+
|
3833
|
+
def to_a(_iseq)
|
3834
|
+
[:opt_size, calldata.to_h]
|
3835
|
+
end
|
3836
|
+
|
3837
|
+
def length
|
3838
|
+
2
|
3839
|
+
end
|
3840
|
+
|
3841
|
+
def pops
|
3842
|
+
1
|
3843
|
+
end
|
3844
|
+
|
3845
|
+
def pushes
|
3846
|
+
1
|
3847
|
+
end
|
3848
|
+
|
3849
|
+
def canonical
|
3850
|
+
Send.new(calldata, nil)
|
3851
|
+
end
|
3852
|
+
|
3853
|
+
def call(vm)
|
3854
|
+
canonical.call(vm)
|
3855
|
+
end
|
3856
|
+
end
|
3857
|
+
|
3858
|
+
# ### Summary
|
3859
|
+
#
|
3860
|
+
# `opt_str_freeze` pushes a frozen known string value with no interpolation
|
3861
|
+
# onto the stack using the #freeze method. If the method gets overridden,
|
3862
|
+
# this will fall back to a send.
|
3863
|
+
#
|
3864
|
+
# ### Usage
|
3865
|
+
#
|
3866
|
+
# ~~~ruby
|
3867
|
+
# "hello".freeze
|
3868
|
+
# ~~~
|
3869
|
+
#
|
3870
|
+
class OptStrFreeze
|
3871
|
+
attr_reader :object, :calldata
|
3872
|
+
|
3873
|
+
def initialize(object, calldata)
|
3874
|
+
@object = object
|
3875
|
+
@calldata = calldata
|
3876
|
+
end
|
3877
|
+
|
3878
|
+
def disasm(fmt)
|
3879
|
+
fmt.instruction(
|
3880
|
+
"opt_str_freeze",
|
3881
|
+
[fmt.object(object), fmt.calldata(calldata)]
|
3882
|
+
)
|
3883
|
+
end
|
3884
|
+
|
3885
|
+
def to_a(_iseq)
|
3886
|
+
[:opt_str_freeze, object, calldata.to_h]
|
3887
|
+
end
|
3888
|
+
|
3889
|
+
def length
|
3890
|
+
3
|
3891
|
+
end
|
3892
|
+
|
3893
|
+
def pops
|
3894
|
+
0
|
3895
|
+
end
|
3896
|
+
|
3897
|
+
def pushes
|
3898
|
+
1
|
3899
|
+
end
|
3900
|
+
|
3901
|
+
def canonical
|
3902
|
+
self
|
3903
|
+
end
|
3904
|
+
|
3905
|
+
def call(vm)
|
3906
|
+
vm.push(object.freeze)
|
3907
|
+
end
|
3908
|
+
end
|
3909
|
+
|
3910
|
+
# ### Summary
|
3911
|
+
#
|
3912
|
+
# `opt_str_uminus` pushes a frozen known string value with no interpolation
|
3913
|
+
# onto the stack. If the method gets overridden, this will fall back to a
|
3914
|
+
# send.
|
3915
|
+
#
|
3916
|
+
# ### Usage
|
3917
|
+
#
|
3918
|
+
# ~~~ruby
|
3919
|
+
# -"string"
|
3920
|
+
# ~~~
|
3921
|
+
#
|
3922
|
+
class OptStrUMinus
|
3923
|
+
attr_reader :object, :calldata
|
3924
|
+
|
3925
|
+
def initialize(object, calldata)
|
3926
|
+
@object = object
|
3927
|
+
@calldata = calldata
|
3928
|
+
end
|
3929
|
+
|
3930
|
+
def disasm(fmt)
|
3931
|
+
fmt.instruction(
|
3932
|
+
"opt_str_uminus",
|
3933
|
+
[fmt.object(object), fmt.calldata(calldata)]
|
3934
|
+
)
|
3935
|
+
end
|
3936
|
+
|
3937
|
+
def to_a(_iseq)
|
3938
|
+
[:opt_str_uminus, object, calldata.to_h]
|
3939
|
+
end
|
3940
|
+
|
3941
|
+
def length
|
3942
|
+
3
|
3943
|
+
end
|
3944
|
+
|
3945
|
+
def pops
|
3946
|
+
0
|
3947
|
+
end
|
3948
|
+
|
3949
|
+
def pushes
|
3950
|
+
1
|
3951
|
+
end
|
3952
|
+
|
3953
|
+
def canonical
|
3954
|
+
self
|
3955
|
+
end
|
3956
|
+
|
3957
|
+
def call(vm)
|
3958
|
+
vm.push(-object)
|
3959
|
+
end
|
3960
|
+
end
|
3961
|
+
|
3962
|
+
# ### Summary
|
3963
|
+
#
|
3964
|
+
# `opt_succ` is a specialization of the `opt_send_without_block` instruction
|
3965
|
+
# when the method being called is `succ`. Fast paths exist when the receiver
|
3966
|
+
# is either a String or a Fixnum. It pops the receiver off the stack and
|
3967
|
+
# pushes on the result.
|
3968
|
+
#
|
3969
|
+
# ### Usage
|
3970
|
+
#
|
3971
|
+
# ~~~ruby
|
3972
|
+
# "".succ
|
3973
|
+
# ~~~
|
3974
|
+
#
|
3975
|
+
class OptSucc
|
3976
|
+
attr_reader :calldata
|
3977
|
+
|
3978
|
+
def initialize(calldata)
|
3979
|
+
@calldata = calldata
|
3980
|
+
end
|
3981
|
+
|
3982
|
+
def disasm(fmt)
|
3983
|
+
fmt.instruction("opt_succ", [fmt.calldata(calldata)])
|
3984
|
+
end
|
3985
|
+
|
3986
|
+
def to_a(_iseq)
|
3987
|
+
[:opt_succ, calldata.to_h]
|
3988
|
+
end
|
3989
|
+
|
3990
|
+
def length
|
3991
|
+
2
|
3992
|
+
end
|
3993
|
+
|
3994
|
+
def pops
|
3995
|
+
1
|
3996
|
+
end
|
3997
|
+
|
3998
|
+
def pushes
|
3999
|
+
1
|
4000
|
+
end
|
4001
|
+
|
4002
|
+
def canonical
|
4003
|
+
Send.new(calldata, nil)
|
4004
|
+
end
|
4005
|
+
|
4006
|
+
def call(vm)
|
4007
|
+
canonical.call(vm)
|
4008
|
+
end
|
4009
|
+
end
|
4010
|
+
|
4011
|
+
# ### Summary
|
4012
|
+
#
|
4013
|
+
# `pop` pops the top value off the stack.
|
4014
|
+
#
|
4015
|
+
# ### Usage
|
4016
|
+
#
|
4017
|
+
# ~~~ruby
|
4018
|
+
# a ||= 2
|
4019
|
+
# ~~~
|
4020
|
+
#
|
4021
|
+
class Pop
|
4022
|
+
def disasm(fmt)
|
4023
|
+
fmt.instruction("pop")
|
4024
|
+
end
|
4025
|
+
|
4026
|
+
def to_a(_iseq)
|
4027
|
+
[:pop]
|
4028
|
+
end
|
4029
|
+
|
4030
|
+
def length
|
4031
|
+
1
|
4032
|
+
end
|
4033
|
+
|
4034
|
+
def pops
|
4035
|
+
1
|
4036
|
+
end
|
4037
|
+
|
4038
|
+
def pushes
|
4039
|
+
0
|
4040
|
+
end
|
4041
|
+
|
4042
|
+
def canonical
|
4043
|
+
self
|
4044
|
+
end
|
4045
|
+
|
4046
|
+
def call(vm)
|
4047
|
+
vm.pop
|
4048
|
+
end
|
4049
|
+
end
|
4050
|
+
|
4051
|
+
# ### Summary
|
4052
|
+
#
|
4053
|
+
# `putnil` pushes a global nil object onto the stack.
|
4054
|
+
#
|
4055
|
+
# ### Usage
|
4056
|
+
#
|
4057
|
+
# ~~~ruby
|
4058
|
+
# nil
|
4059
|
+
# ~~~
|
4060
|
+
#
|
4061
|
+
class PutNil
|
4062
|
+
def disasm(fmt)
|
4063
|
+
fmt.instruction("putnil")
|
4064
|
+
end
|
4065
|
+
|
4066
|
+
def to_a(_iseq)
|
4067
|
+
[:putnil]
|
4068
|
+
end
|
4069
|
+
|
4070
|
+
def length
|
4071
|
+
1
|
4072
|
+
end
|
4073
|
+
|
4074
|
+
def pops
|
4075
|
+
0
|
4076
|
+
end
|
4077
|
+
|
4078
|
+
def pushes
|
4079
|
+
1
|
4080
|
+
end
|
4081
|
+
|
4082
|
+
def canonical
|
4083
|
+
PutObject.new(nil)
|
4084
|
+
end
|
4085
|
+
|
4086
|
+
def call(vm)
|
4087
|
+
canonical.call(vm)
|
4088
|
+
end
|
4089
|
+
end
|
4090
|
+
|
4091
|
+
# ### Summary
|
4092
|
+
#
|
4093
|
+
# `putobject` pushes a known value onto the stack.
|
4094
|
+
#
|
4095
|
+
# ### Usage
|
4096
|
+
#
|
4097
|
+
# ~~~ruby
|
4098
|
+
# 5
|
4099
|
+
# ~~~
|
4100
|
+
#
|
4101
|
+
class PutObject
|
4102
|
+
attr_reader :object
|
4103
|
+
|
4104
|
+
def initialize(object)
|
4105
|
+
@object = object
|
4106
|
+
end
|
4107
|
+
|
4108
|
+
def disasm(fmt)
|
4109
|
+
fmt.instruction("putobject", [fmt.object(object)])
|
4110
|
+
end
|
4111
|
+
|
4112
|
+
def to_a(_iseq)
|
4113
|
+
[:putobject, object]
|
4114
|
+
end
|
4115
|
+
|
4116
|
+
def length
|
4117
|
+
2
|
4118
|
+
end
|
4119
|
+
|
4120
|
+
def pops
|
4121
|
+
0
|
4122
|
+
end
|
4123
|
+
|
4124
|
+
def pushes
|
4125
|
+
1
|
4126
|
+
end
|
4127
|
+
|
4128
|
+
def canonical
|
4129
|
+
self
|
4130
|
+
end
|
4131
|
+
|
4132
|
+
def call(vm)
|
4133
|
+
vm.push(object)
|
4134
|
+
end
|
4135
|
+
end
|
4136
|
+
|
4137
|
+
# ### Summary
|
4138
|
+
#
|
4139
|
+
# `putobject_INT2FIX_0_` pushes 0 on the stack. It is a specialized
|
4140
|
+
# instruction resulting from the operand unification optimization. It is
|
4141
|
+
# equivalent to `putobject 0`.
|
4142
|
+
#
|
4143
|
+
# ### Usage
|
4144
|
+
#
|
4145
|
+
# ~~~ruby
|
4146
|
+
# 0
|
4147
|
+
# ~~~
|
4148
|
+
#
|
4149
|
+
class PutObjectInt2Fix0
|
4150
|
+
def disasm(fmt)
|
4151
|
+
fmt.instruction("putobject_INT2FIX_0_")
|
4152
|
+
end
|
4153
|
+
|
4154
|
+
def to_a(_iseq)
|
4155
|
+
[:putobject_INT2FIX_0_]
|
4156
|
+
end
|
4157
|
+
|
4158
|
+
def length
|
4159
|
+
1
|
4160
|
+
end
|
4161
|
+
|
4162
|
+
def pops
|
4163
|
+
0
|
4164
|
+
end
|
4165
|
+
|
4166
|
+
def pushes
|
4167
|
+
1
|
4168
|
+
end
|
4169
|
+
|
4170
|
+
def canonical
|
4171
|
+
PutObject.new(0)
|
4172
|
+
end
|
4173
|
+
|
4174
|
+
def call(vm)
|
4175
|
+
canonical.call(vm)
|
4176
|
+
end
|
4177
|
+
end
|
4178
|
+
|
4179
|
+
# ### Summary
|
4180
|
+
#
|
4181
|
+
# `putobject_INT2FIX_1_` pushes 1 on the stack. It is a specialized
|
4182
|
+
# instruction resulting from the operand unification optimization. It is
|
4183
|
+
# equivalent to `putobject 1`.
|
4184
|
+
#
|
4185
|
+
# ### Usage
|
4186
|
+
#
|
4187
|
+
# ~~~ruby
|
4188
|
+
# 1
|
4189
|
+
# ~~~
|
4190
|
+
#
|
4191
|
+
class PutObjectInt2Fix1
|
4192
|
+
def disasm(fmt)
|
4193
|
+
fmt.instruction("putobject_INT2FIX_1_")
|
4194
|
+
end
|
4195
|
+
|
4196
|
+
def to_a(_iseq)
|
4197
|
+
[:putobject_INT2FIX_1_]
|
4198
|
+
end
|
4199
|
+
|
4200
|
+
def length
|
4201
|
+
1
|
4202
|
+
end
|
4203
|
+
|
4204
|
+
def pops
|
4205
|
+
0
|
4206
|
+
end
|
4207
|
+
|
4208
|
+
def pushes
|
4209
|
+
1
|
4210
|
+
end
|
4211
|
+
|
4212
|
+
def canonical
|
4213
|
+
PutObject.new(1)
|
4214
|
+
end
|
4215
|
+
|
4216
|
+
def call(vm)
|
4217
|
+
canonical.call(vm)
|
4218
|
+
end
|
4219
|
+
end
|
4220
|
+
|
4221
|
+
# ### Summary
|
4222
|
+
#
|
4223
|
+
# `putself` pushes the current value of self onto the stack.
|
4224
|
+
#
|
4225
|
+
# ### Usage
|
4226
|
+
#
|
4227
|
+
# ~~~ruby
|
4228
|
+
# puts "Hello, world!"
|
4229
|
+
# ~~~
|
4230
|
+
#
|
4231
|
+
class PutSelf
|
4232
|
+
def disasm(fmt)
|
4233
|
+
fmt.instruction("putself")
|
4234
|
+
end
|
4235
|
+
|
4236
|
+
def to_a(_iseq)
|
4237
|
+
[:putself]
|
4238
|
+
end
|
4239
|
+
|
4240
|
+
def length
|
4241
|
+
1
|
4242
|
+
end
|
4243
|
+
|
4244
|
+
def pops
|
4245
|
+
0
|
4246
|
+
end
|
4247
|
+
|
4248
|
+
def pushes
|
4249
|
+
1
|
4250
|
+
end
|
4251
|
+
|
4252
|
+
def canonical
|
4253
|
+
self
|
4254
|
+
end
|
4255
|
+
|
4256
|
+
def call(vm)
|
4257
|
+
vm.push(vm._self)
|
4258
|
+
end
|
4259
|
+
end
|
4260
|
+
|
4261
|
+
# ### Summary
|
4262
|
+
#
|
4263
|
+
# `putspecialobject` pushes one of three special objects onto the stack.
|
4264
|
+
# These are either the VM core special object, the class base special
|
4265
|
+
# object, or the constant base special object.
|
4266
|
+
#
|
4267
|
+
# ### Usage
|
4268
|
+
#
|
4269
|
+
# ~~~ruby
|
4270
|
+
# alias foo bar
|
4271
|
+
# ~~~
|
4272
|
+
#
|
4273
|
+
class PutSpecialObject
|
4274
|
+
OBJECT_VMCORE = 1
|
4275
|
+
OBJECT_CBASE = 2
|
4276
|
+
OBJECT_CONST_BASE = 3
|
4277
|
+
|
4278
|
+
attr_reader :object
|
4279
|
+
|
4280
|
+
def initialize(object)
|
4281
|
+
@object = object
|
4282
|
+
end
|
4283
|
+
|
4284
|
+
def disasm(fmt)
|
4285
|
+
fmt.instruction("putspecialobject", [fmt.object(object)])
|
4286
|
+
end
|
4287
|
+
|
4288
|
+
def to_a(_iseq)
|
4289
|
+
[:putspecialobject, object]
|
4290
|
+
end
|
4291
|
+
|
4292
|
+
def length
|
4293
|
+
2
|
4294
|
+
end
|
4295
|
+
|
4296
|
+
def pops
|
4297
|
+
0
|
4298
|
+
end
|
4299
|
+
|
4300
|
+
def pushes
|
4301
|
+
1
|
4302
|
+
end
|
4303
|
+
|
4304
|
+
def canonical
|
4305
|
+
self
|
4306
|
+
end
|
4307
|
+
|
4308
|
+
def call(vm)
|
4309
|
+
case object
|
4310
|
+
when OBJECT_VMCORE
|
4311
|
+
vm.push(vm.frozen_core)
|
4312
|
+
when OBJECT_CBASE
|
4313
|
+
value = vm._self
|
4314
|
+
value = value.singleton_class unless value.is_a?(Class)
|
4315
|
+
vm.push(value)
|
4316
|
+
when OBJECT_CONST_BASE
|
4317
|
+
vm.push(vm.const_base)
|
4318
|
+
end
|
4319
|
+
end
|
4320
|
+
end
|
4321
|
+
|
4322
|
+
# ### Summary
|
4323
|
+
#
|
4324
|
+
# `putstring` pushes an unfrozen string literal onto the stack.
|
4325
|
+
#
|
4326
|
+
# ### Usage
|
4327
|
+
#
|
4328
|
+
# ~~~ruby
|
4329
|
+
# "foo"
|
4330
|
+
# ~~~
|
4331
|
+
#
|
4332
|
+
class PutString
|
4333
|
+
attr_reader :object
|
4334
|
+
|
4335
|
+
def initialize(object)
|
4336
|
+
@object = object
|
4337
|
+
end
|
4338
|
+
|
4339
|
+
def disasm(fmt)
|
4340
|
+
fmt.instruction("putstring", [fmt.object(object)])
|
4341
|
+
end
|
4342
|
+
|
4343
|
+
def to_a(_iseq)
|
4344
|
+
[:putstring, object]
|
4345
|
+
end
|
4346
|
+
|
4347
|
+
def length
|
4348
|
+
2
|
4349
|
+
end
|
4350
|
+
|
4351
|
+
def pops
|
4352
|
+
0
|
4353
|
+
end
|
4354
|
+
|
4355
|
+
def pushes
|
4356
|
+
1
|
4357
|
+
end
|
4358
|
+
|
4359
|
+
def canonical
|
4360
|
+
self
|
4361
|
+
end
|
4362
|
+
|
4363
|
+
def call(vm)
|
4364
|
+
vm.push(object.dup)
|
4365
|
+
end
|
4366
|
+
end
|
4367
|
+
|
4368
|
+
# ### Summary
|
4369
|
+
#
|
4370
|
+
# `send` invokes a method with an optional block. It pops its receiver and
|
4371
|
+
# the arguments for the method off the stack and pushes the return value
|
4372
|
+
# onto the stack. It has two arguments: the calldata for the call site and
|
4373
|
+
# the optional block instruction sequence.
|
4374
|
+
#
|
4375
|
+
# ### Usage
|
4376
|
+
#
|
4377
|
+
# ~~~ruby
|
4378
|
+
# "hello".tap { |i| p i }
|
4379
|
+
# ~~~
|
4380
|
+
#
|
4381
|
+
class Send
|
4382
|
+
attr_reader :calldata, :block_iseq
|
4383
|
+
|
4384
|
+
def initialize(calldata, block_iseq)
|
4385
|
+
@calldata = calldata
|
4386
|
+
@block_iseq = block_iseq
|
4387
|
+
end
|
4388
|
+
|
4389
|
+
def disasm(fmt)
|
4390
|
+
fmt.enqueue(block_iseq) if block_iseq
|
4391
|
+
fmt.instruction(
|
4392
|
+
"send",
|
4393
|
+
[fmt.calldata(calldata), block_iseq&.name || "nil"]
|
4394
|
+
)
|
4395
|
+
end
|
4396
|
+
|
4397
|
+
def to_a(_iseq)
|
4398
|
+
[:send, calldata.to_h, block_iseq&.to_a]
|
4399
|
+
end
|
4400
|
+
|
4401
|
+
def length
|
4402
|
+
3
|
4403
|
+
end
|
4404
|
+
|
4405
|
+
def pops
|
4406
|
+
argb = (calldata.flag?(CallData::CALL_ARGS_BLOCKARG) ? 1 : 0)
|
4407
|
+
argb + calldata.argc + 1
|
4408
|
+
end
|
4409
|
+
|
4410
|
+
def pushes
|
4411
|
+
1
|
4412
|
+
end
|
4413
|
+
|
4414
|
+
def canonical
|
4415
|
+
self
|
4416
|
+
end
|
4417
|
+
|
4418
|
+
def call(vm)
|
4419
|
+
block =
|
4420
|
+
if (iseq = block_iseq)
|
4421
|
+
->(*args, **kwargs, &blk) do
|
4422
|
+
vm.run_block_frame(iseq, *args, **kwargs, &blk)
|
4423
|
+
end
|
4424
|
+
end
|
4425
|
+
|
4426
|
+
keywords =
|
4427
|
+
if calldata.kw_arg
|
4428
|
+
calldata.kw_arg.zip(vm.pop(calldata.kw_arg.length)).to_h
|
4429
|
+
else
|
4430
|
+
{}
|
4431
|
+
end
|
4432
|
+
|
4433
|
+
arguments = vm.pop(calldata.argc)
|
4434
|
+
receiver = vm.pop
|
4435
|
+
|
4436
|
+
vm.push(
|
4437
|
+
receiver.__send__(calldata.method, *arguments, **keywords, &block)
|
4438
|
+
)
|
4439
|
+
end
|
4440
|
+
end
|
4441
|
+
|
4442
|
+
# ### Summary
|
4443
|
+
#
|
4444
|
+
# `setblockparam` sets the value of a block local variable on a frame
|
4445
|
+
# determined by the level and index arguments. The level is the number of
|
4446
|
+
# frames back to look and the index is the index in the local table. It pops
|
4447
|
+
# the value it is setting off the stack.
|
4448
|
+
#
|
4449
|
+
# ### Usage
|
4450
|
+
#
|
4451
|
+
# ~~~ruby
|
4452
|
+
# def foo(&bar)
|
4453
|
+
# bar = baz
|
4454
|
+
# end
|
4455
|
+
# ~~~
|
4456
|
+
#
|
4457
|
+
class SetBlockParam
|
4458
|
+
attr_reader :index, :level
|
4459
|
+
|
4460
|
+
def initialize(index, level)
|
4461
|
+
@index = index
|
4462
|
+
@level = level
|
4463
|
+
end
|
4464
|
+
|
4465
|
+
def disasm(fmt)
|
4466
|
+
fmt.instruction("setblockparam", [fmt.local(index, explicit: level)])
|
4467
|
+
end
|
4468
|
+
|
4469
|
+
def to_a(iseq)
|
4470
|
+
current = iseq
|
4471
|
+
level.times { current = current.parent_iseq }
|
4472
|
+
[:setblockparam, current.local_table.offset(index), level]
|
4473
|
+
end
|
4474
|
+
|
4475
|
+
def length
|
4476
|
+
3
|
4477
|
+
end
|
4478
|
+
|
4479
|
+
def pops
|
4480
|
+
1
|
4481
|
+
end
|
4482
|
+
|
4483
|
+
def pushes
|
4484
|
+
0
|
4485
|
+
end
|
4486
|
+
|
4487
|
+
def canonical
|
4488
|
+
self
|
4489
|
+
end
|
4490
|
+
|
4491
|
+
def call(vm)
|
4492
|
+
vm.local_set(index, level, vm.pop)
|
4493
|
+
end
|
4494
|
+
end
|
4495
|
+
|
4496
|
+
# ### Summary
|
4497
|
+
#
|
4498
|
+
# `setclassvariable` looks for a class variable in the current class and
|
4499
|
+
# sets its value to the value it pops off the top of the stack. It uses an
|
4500
|
+
# inline cache to reduce the need to lookup the class variable in the class
|
4501
|
+
# hierarchy every time.
|
4502
|
+
#
|
4503
|
+
# ### Usage
|
4504
|
+
#
|
4505
|
+
# ~~~ruby
|
4506
|
+
# @@class_variable = 1
|
4507
|
+
# ~~~
|
4508
|
+
#
|
4509
|
+
class SetClassVariable
|
4510
|
+
attr_reader :name, :cache
|
4511
|
+
|
4512
|
+
def initialize(name, cache)
|
4513
|
+
@name = name
|
4514
|
+
@cache = cache
|
4515
|
+
end
|
4516
|
+
|
4517
|
+
def disasm(fmt)
|
4518
|
+
fmt.instruction(
|
4519
|
+
"setclassvariable",
|
4520
|
+
[fmt.object(name), fmt.inline_storage(cache)]
|
4521
|
+
)
|
4522
|
+
end
|
4523
|
+
|
4524
|
+
def to_a(_iseq)
|
4525
|
+
[:setclassvariable, name, cache]
|
4526
|
+
end
|
4527
|
+
|
4528
|
+
def length
|
4529
|
+
3
|
4530
|
+
end
|
4531
|
+
|
4532
|
+
def pops
|
4533
|
+
1
|
4534
|
+
end
|
4535
|
+
|
4536
|
+
def pushes
|
4537
|
+
0
|
4538
|
+
end
|
4539
|
+
|
4540
|
+
def canonical
|
4541
|
+
self
|
4542
|
+
end
|
4543
|
+
|
4544
|
+
def call(vm)
|
4545
|
+
clazz = vm._self
|
4546
|
+
clazz = clazz.class unless clazz.is_a?(Class)
|
4547
|
+
clazz.class_variable_set(name, vm.pop)
|
4548
|
+
end
|
4549
|
+
end
|
4550
|
+
|
4551
|
+
# ### Summary
|
4552
|
+
#
|
4553
|
+
# `setconstant` pops two values off the stack: the value to set the
|
4554
|
+
# constant to and the constant base to set it in.
|
4555
|
+
#
|
4556
|
+
# ### Usage
|
4557
|
+
#
|
4558
|
+
# ~~~ruby
|
4559
|
+
# Constant = 1
|
4560
|
+
# ~~~
|
4561
|
+
#
|
4562
|
+
class SetConstant
|
4563
|
+
attr_reader :name
|
4564
|
+
|
4565
|
+
def initialize(name)
|
4566
|
+
@name = name
|
4567
|
+
end
|
4568
|
+
|
4569
|
+
def disasm(fmt)
|
4570
|
+
fmt.instruction("setconstant", [fmt.object(name)])
|
4571
|
+
end
|
4572
|
+
|
4573
|
+
def to_a(_iseq)
|
4574
|
+
[:setconstant, name]
|
4575
|
+
end
|
4576
|
+
|
4577
|
+
def length
|
4578
|
+
2
|
4579
|
+
end
|
4580
|
+
|
4581
|
+
def pops
|
4582
|
+
2
|
4583
|
+
end
|
4584
|
+
|
4585
|
+
def pushes
|
4586
|
+
0
|
4587
|
+
end
|
4588
|
+
|
4589
|
+
def canonical
|
4590
|
+
self
|
4591
|
+
end
|
4592
|
+
|
4593
|
+
def call(vm)
|
4594
|
+
value, parent = vm.pop(2)
|
4595
|
+
parent.const_set(name, value)
|
4596
|
+
end
|
4597
|
+
end
|
4598
|
+
|
4599
|
+
# ### Summary
|
4600
|
+
#
|
4601
|
+
# `setglobal` sets the value of a global variable to a value popped off the
|
4602
|
+
# top of the stack.
|
4603
|
+
#
|
4604
|
+
# ### Usage
|
4605
|
+
#
|
4606
|
+
# ~~~ruby
|
4607
|
+
# $global = 5
|
4608
|
+
# ~~~
|
4609
|
+
#
|
4610
|
+
class SetGlobal
|
4611
|
+
attr_reader :name
|
4612
|
+
|
4613
|
+
def initialize(name)
|
4614
|
+
@name = name
|
4615
|
+
end
|
4616
|
+
|
4617
|
+
def disasm(fmt)
|
4618
|
+
fmt.instruction("setglobal", [fmt.object(name)])
|
4619
|
+
end
|
4620
|
+
|
4621
|
+
def to_a(_iseq)
|
4622
|
+
[:setglobal, name]
|
4623
|
+
end
|
4624
|
+
|
4625
|
+
def length
|
4626
|
+
2
|
4627
|
+
end
|
4628
|
+
|
4629
|
+
def pops
|
4630
|
+
1
|
4631
|
+
end
|
4632
|
+
|
4633
|
+
def pushes
|
4634
|
+
0
|
4635
|
+
end
|
4636
|
+
|
4637
|
+
def canonical
|
4638
|
+
self
|
4639
|
+
end
|
4640
|
+
|
4641
|
+
def call(vm)
|
4642
|
+
# Evaluating the name of the global variable because there isn't a
|
4643
|
+
# reflection API for global variables.
|
4644
|
+
eval("#{name} = vm.pop", binding, __FILE__, __LINE__)
|
4645
|
+
end
|
4646
|
+
end
|
4647
|
+
|
4648
|
+
# ### Summary
|
4649
|
+
#
|
4650
|
+
# `setinstancevariable` pops a value off the top of the stack and then sets
|
4651
|
+
# the instance variable associated with the instruction to that value.
|
4652
|
+
#
|
4653
|
+
# This instruction has two forms, but both have the same structure. Before
|
4654
|
+
# Ruby 3.2, the inline cache corresponded to both the get and set
|
4655
|
+
# instructions and could be shared. Since Ruby 3.2, it uses object shapes
|
4656
|
+
# instead so the caches are unique per instruction.
|
4657
|
+
#
|
4658
|
+
# ### Usage
|
4659
|
+
#
|
4660
|
+
# ~~~ruby
|
4661
|
+
# @instance_variable = 1
|
4662
|
+
# ~~~
|
4663
|
+
#
|
4664
|
+
class SetInstanceVariable
|
4665
|
+
attr_reader :name, :cache
|
4666
|
+
|
4667
|
+
def initialize(name, cache)
|
4668
|
+
@name = name
|
4669
|
+
@cache = cache
|
4670
|
+
end
|
4671
|
+
|
4672
|
+
def disasm(fmt)
|
4673
|
+
fmt.instruction(
|
4674
|
+
"setinstancevariable",
|
4675
|
+
[fmt.object(name), fmt.inline_storage(cache)]
|
4676
|
+
)
|
4677
|
+
end
|
4678
|
+
|
4679
|
+
def to_a(_iseq)
|
4680
|
+
[:setinstancevariable, name, cache]
|
4681
|
+
end
|
4682
|
+
|
4683
|
+
def length
|
4684
|
+
3
|
4685
|
+
end
|
4686
|
+
|
4687
|
+
def pops
|
4688
|
+
1
|
4689
|
+
end
|
4690
|
+
|
4691
|
+
def pushes
|
4692
|
+
0
|
4693
|
+
end
|
4694
|
+
|
4695
|
+
def canonical
|
4696
|
+
self
|
4697
|
+
end
|
4698
|
+
|
4699
|
+
def call(vm)
|
4700
|
+
method = Object.instance_method(:instance_variable_set)
|
4701
|
+
method.bind(vm._self).call(name, vm.pop)
|
4702
|
+
end
|
4703
|
+
end
|
4704
|
+
|
4705
|
+
# ### Summary
|
4706
|
+
#
|
4707
|
+
# `setlocal` sets the value of a local variable on a frame determined by the
|
4708
|
+
# level and index arguments. The level is the number of frames back to
|
4709
|
+
# look and the index is the index in the local table. It pops the value it
|
4710
|
+
# is setting off the stack.
|
4711
|
+
#
|
4712
|
+
# ### Usage
|
4713
|
+
#
|
4714
|
+
# ~~~ruby
|
4715
|
+
# value = 5
|
4716
|
+
# tap { tap { value = 10 } }
|
4717
|
+
# ~~~
|
4718
|
+
#
|
4719
|
+
class SetLocal
|
4720
|
+
attr_reader :index, :level
|
4721
|
+
|
4722
|
+
def initialize(index, level)
|
4723
|
+
@index = index
|
4724
|
+
@level = level
|
4725
|
+
end
|
4726
|
+
|
4727
|
+
def disasm(fmt)
|
4728
|
+
fmt.instruction("setlocal", [fmt.local(index, explicit: level)])
|
4729
|
+
end
|
4730
|
+
|
4731
|
+
def to_a(iseq)
|
4732
|
+
current = iseq
|
4733
|
+
level.times { current = current.parent_iseq }
|
4734
|
+
[:setlocal, current.local_table.offset(index), level]
|
4735
|
+
end
|
4736
|
+
|
4737
|
+
def length
|
4738
|
+
3
|
4739
|
+
end
|
4740
|
+
|
4741
|
+
def pops
|
4742
|
+
1
|
4743
|
+
end
|
4744
|
+
|
4745
|
+
def pushes
|
4746
|
+
0
|
4747
|
+
end
|
4748
|
+
|
4749
|
+
def canonical
|
4750
|
+
self
|
4751
|
+
end
|
4752
|
+
|
4753
|
+
def call(vm)
|
4754
|
+
vm.local_set(index, level, vm.pop)
|
4755
|
+
end
|
4756
|
+
end
|
4757
|
+
|
4758
|
+
# ### Summary
|
4759
|
+
#
|
4760
|
+
# `setlocal_WC_0` is a specialized version of the `setlocal` instruction. It
|
4761
|
+
# sets the value of a local variable on the current frame to the value at
|
4762
|
+
# the top of the stack as determined by the index given as its only
|
4763
|
+
# argument.
|
4764
|
+
#
|
4765
|
+
# ### Usage
|
4766
|
+
#
|
4767
|
+
# ~~~ruby
|
4768
|
+
# value = 5
|
4769
|
+
# ~~~
|
4770
|
+
#
|
4771
|
+
class SetLocalWC0
|
4772
|
+
attr_reader :index
|
4773
|
+
|
4774
|
+
def initialize(index)
|
4775
|
+
@index = index
|
4776
|
+
end
|
4777
|
+
|
4778
|
+
def disasm(fmt)
|
4779
|
+
fmt.instruction("setlocal_WC_0", [fmt.local(index, implicit: 0)])
|
4780
|
+
end
|
4781
|
+
|
4782
|
+
def to_a(iseq)
|
4783
|
+
[:setlocal_WC_0, iseq.local_table.offset(index)]
|
4784
|
+
end
|
4785
|
+
|
4786
|
+
def length
|
4787
|
+
2
|
4788
|
+
end
|
4789
|
+
|
4790
|
+
def pops
|
4791
|
+
1
|
4792
|
+
end
|
4793
|
+
|
4794
|
+
def pushes
|
4795
|
+
0
|
4796
|
+
end
|
4797
|
+
|
4798
|
+
def canonical
|
4799
|
+
SetLocal.new(index, 0)
|
4800
|
+
end
|
4801
|
+
|
4802
|
+
def call(vm)
|
4803
|
+
canonical.call(vm)
|
4804
|
+
end
|
4805
|
+
end
|
4806
|
+
|
4807
|
+
# ### Summary
|
4808
|
+
#
|
4809
|
+
# `setlocal_WC_1` is a specialized version of the `setlocal` instruction. It
|
4810
|
+
# sets the value of a local variable on the parent frame to the value at the
|
4811
|
+
# top of the stack as determined by the index given as its only argument.
|
4812
|
+
#
|
4813
|
+
# ### Usage
|
4814
|
+
#
|
4815
|
+
# ~~~ruby
|
4816
|
+
# value = 5
|
4817
|
+
# self.then { value = 10 }
|
4818
|
+
# ~~~
|
4819
|
+
#
|
4820
|
+
class SetLocalWC1
|
4821
|
+
attr_reader :index
|
4822
|
+
|
4823
|
+
def initialize(index)
|
4824
|
+
@index = index
|
4825
|
+
end
|
4826
|
+
|
4827
|
+
def disasm(fmt)
|
4828
|
+
fmt.instruction("setlocal_WC_1", [fmt.local(index, implicit: 1)])
|
4829
|
+
end
|
4830
|
+
|
4831
|
+
def to_a(iseq)
|
4832
|
+
[:setlocal_WC_1, iseq.parent_iseq.local_table.offset(index)]
|
4833
|
+
end
|
4834
|
+
|
4835
|
+
def length
|
4836
|
+
2
|
4837
|
+
end
|
4838
|
+
|
4839
|
+
def pops
|
4840
|
+
1
|
4841
|
+
end
|
4842
|
+
|
4843
|
+
def pushes
|
4844
|
+
0
|
4845
|
+
end
|
4846
|
+
|
4847
|
+
def canonical
|
4848
|
+
SetLocal.new(index, 1)
|
4849
|
+
end
|
4850
|
+
|
4851
|
+
def call(vm)
|
4852
|
+
canonical.call(vm)
|
4853
|
+
end
|
4854
|
+
end
|
4855
|
+
|
4856
|
+
# ### Summary
|
4857
|
+
#
|
4858
|
+
# `setn` sets a value in the stack to a value popped off the top of the
|
4859
|
+
# stack. It then pushes that value onto the top of the stack as well.
|
4860
|
+
#
|
4861
|
+
# ### Usage
|
4862
|
+
#
|
4863
|
+
# ~~~ruby
|
4864
|
+
# {}[:key] = 'val'
|
4865
|
+
# ~~~
|
4866
|
+
#
|
4867
|
+
class SetN
|
4868
|
+
attr_reader :number
|
4869
|
+
|
4870
|
+
def initialize(number)
|
4871
|
+
@number = number
|
4872
|
+
end
|
4873
|
+
|
4874
|
+
def disasm(fmt)
|
4875
|
+
fmt.instruction("setn", [fmt.object(number)])
|
4876
|
+
end
|
4877
|
+
|
4878
|
+
def to_a(_iseq)
|
4879
|
+
[:setn, number]
|
4880
|
+
end
|
4881
|
+
|
4882
|
+
def length
|
4883
|
+
2
|
4884
|
+
end
|
4885
|
+
|
4886
|
+
def pops
|
4887
|
+
1
|
4888
|
+
end
|
4889
|
+
|
4890
|
+
def pushes
|
4891
|
+
1
|
4892
|
+
end
|
4893
|
+
|
4894
|
+
def canonical
|
4895
|
+
self
|
4896
|
+
end
|
4897
|
+
|
4898
|
+
def call(vm)
|
4899
|
+
vm.stack[-number - 1] = vm.stack.last
|
4900
|
+
end
|
4901
|
+
end
|
4902
|
+
|
4903
|
+
# ### Summary
|
4904
|
+
#
|
4905
|
+
# `setspecial` pops a value off the top of the stack and sets a special
|
4906
|
+
# local variable to that value. The special local variable is determined by
|
4907
|
+
# the key given as its only argument.
|
4908
|
+
#
|
4909
|
+
# ### Usage
|
4910
|
+
#
|
4911
|
+
# ~~~ruby
|
4912
|
+
# baz if (foo == 1) .. (bar == 1)
|
4913
|
+
# ~~~
|
4914
|
+
#
|
4915
|
+
class SetSpecial
|
4916
|
+
attr_reader :key
|
4917
|
+
|
4918
|
+
def initialize(key)
|
4919
|
+
@key = key
|
4920
|
+
end
|
4921
|
+
|
4922
|
+
def disasm(fmt)
|
4923
|
+
fmt.instruction("setspecial", [fmt.object(key)])
|
4924
|
+
end
|
4925
|
+
|
4926
|
+
def to_a(_iseq)
|
4927
|
+
[:setspecial, key]
|
4928
|
+
end
|
4929
|
+
|
4930
|
+
def length
|
4931
|
+
2
|
4932
|
+
end
|
4933
|
+
|
4934
|
+
def pops
|
4935
|
+
1
|
4936
|
+
end
|
4937
|
+
|
4938
|
+
def pushes
|
4939
|
+
0
|
4940
|
+
end
|
4941
|
+
|
4942
|
+
def canonical
|
4943
|
+
self
|
4944
|
+
end
|
4945
|
+
|
4946
|
+
def call(vm)
|
4947
|
+
case key
|
4948
|
+
when GetSpecial::SVAR_LASTLINE
|
4949
|
+
raise NotImplementedError, "svar SVAR_LASTLINE"
|
4950
|
+
when GetSpecial::SVAR_BACKREF
|
4951
|
+
raise NotImplementedError, "setspecial SVAR_BACKREF"
|
4952
|
+
when GetSpecial::SVAR_FLIPFLOP_START
|
4953
|
+
vm.frame_svar.svars[GetSpecial::SVAR_FLIPFLOP_START]
|
4954
|
+
end
|
4955
|
+
end
|
4956
|
+
end
|
4957
|
+
|
4958
|
+
# ### Summary
|
4959
|
+
#
|
4960
|
+
# `splatarray` coerces the array object at the top of the stack into Array
|
4961
|
+
# by calling `to_a`. It pushes a duplicate of the array if there is a flag,
|
4962
|
+
# and the original array if there isn't one.
|
4963
|
+
#
|
4964
|
+
# ### Usage
|
4965
|
+
#
|
4966
|
+
# ~~~ruby
|
4967
|
+
# x = *(5)
|
4968
|
+
# ~~~
|
4969
|
+
#
|
4970
|
+
class SplatArray
|
4971
|
+
attr_reader :flag
|
4972
|
+
|
4973
|
+
def initialize(flag)
|
4974
|
+
@flag = flag
|
4975
|
+
end
|
4976
|
+
|
4977
|
+
def disasm(fmt)
|
4978
|
+
fmt.instruction("splatarray", [fmt.object(flag)])
|
4979
|
+
end
|
4980
|
+
|
4981
|
+
def to_a(_iseq)
|
4982
|
+
[:splatarray, flag]
|
4983
|
+
end
|
4984
|
+
|
4985
|
+
def length
|
4986
|
+
2
|
4987
|
+
end
|
4988
|
+
|
4989
|
+
def pops
|
4990
|
+
1
|
4991
|
+
end
|
4992
|
+
|
4993
|
+
def pushes
|
4994
|
+
1
|
4995
|
+
end
|
4996
|
+
|
4997
|
+
def canonical
|
4998
|
+
self
|
4999
|
+
end
|
5000
|
+
|
5001
|
+
def call(vm)
|
5002
|
+
vm.push(*vm.pop)
|
5003
|
+
end
|
5004
|
+
end
|
5005
|
+
|
5006
|
+
# ### Summary
|
5007
|
+
#
|
5008
|
+
# `swap` swaps the top two elements in the stack.
|
5009
|
+
#
|
5010
|
+
# ### TracePoint
|
5011
|
+
#
|
5012
|
+
# `swap` does not dispatch any events.
|
5013
|
+
#
|
5014
|
+
# ### Usage
|
5015
|
+
#
|
5016
|
+
# ~~~ruby
|
5017
|
+
# !!defined?([[]])
|
5018
|
+
# ~~~
|
5019
|
+
#
|
5020
|
+
class Swap
|
5021
|
+
def disasm(fmt)
|
5022
|
+
fmt.instruction("swap")
|
5023
|
+
end
|
5024
|
+
|
5025
|
+
def to_a(_iseq)
|
5026
|
+
[:swap]
|
5027
|
+
end
|
5028
|
+
|
5029
|
+
def length
|
5030
|
+
1
|
5031
|
+
end
|
5032
|
+
|
5033
|
+
def pops
|
5034
|
+
2
|
5035
|
+
end
|
5036
|
+
|
5037
|
+
def pushes
|
5038
|
+
2
|
5039
|
+
end
|
5040
|
+
|
5041
|
+
def canonical
|
5042
|
+
self
|
5043
|
+
end
|
5044
|
+
|
5045
|
+
def call(vm)
|
5046
|
+
left, right = vm.pop(2)
|
5047
|
+
vm.push(right, left)
|
5048
|
+
end
|
5049
|
+
end
|
5050
|
+
|
5051
|
+
# ### Summary
|
5052
|
+
#
|
5053
|
+
# `throw` pops a value off the top of the stack and throws it. It is caught
|
5054
|
+
# using the instruction sequence's (or an ancestor's) catch table. It pushes
|
5055
|
+
# on the result of throwing the value.
|
5056
|
+
#
|
5057
|
+
# ### Usage
|
5058
|
+
#
|
5059
|
+
# ~~~ruby
|
5060
|
+
# [1, 2, 3].map { break 2 }
|
5061
|
+
# ~~~
|
5062
|
+
#
|
5063
|
+
class Throw
|
5064
|
+
TAG_NONE = 0x0
|
5065
|
+
TAG_RETURN = 0x1
|
5066
|
+
TAG_BREAK = 0x2
|
5067
|
+
TAG_NEXT = 0x3
|
5068
|
+
TAG_RETRY = 0x4
|
5069
|
+
TAG_REDO = 0x5
|
5070
|
+
TAG_RAISE = 0x6
|
5071
|
+
TAG_THROW = 0x7
|
5072
|
+
TAG_FATAL = 0x8
|
5073
|
+
|
5074
|
+
attr_reader :type
|
5075
|
+
|
5076
|
+
def initialize(type)
|
5077
|
+
@type = type
|
5078
|
+
end
|
5079
|
+
|
5080
|
+
def disasm(fmt)
|
5081
|
+
fmt.instruction("throw", [fmt.object(type)])
|
5082
|
+
end
|
5083
|
+
|
5084
|
+
def to_a(_iseq)
|
5085
|
+
[:throw, type]
|
5086
|
+
end
|
5087
|
+
|
5088
|
+
def length
|
5089
|
+
2
|
5090
|
+
end
|
5091
|
+
|
5092
|
+
def pops
|
5093
|
+
1
|
5094
|
+
end
|
5095
|
+
|
5096
|
+
def pushes
|
5097
|
+
1
|
5098
|
+
end
|
5099
|
+
|
5100
|
+
def canonical
|
5101
|
+
self
|
5102
|
+
end
|
5103
|
+
|
5104
|
+
def call(vm)
|
5105
|
+
raise NotImplementedError, "throw"
|
5106
|
+
end
|
5107
|
+
end
|
5108
|
+
|
5109
|
+
# ### Summary
|
5110
|
+
#
|
5111
|
+
# `topn` pushes a single value onto the stack that is a copy of the value
|
5112
|
+
# within the stack that is `number` of slots down from the top.
|
5113
|
+
#
|
5114
|
+
# ### Usage
|
5115
|
+
#
|
5116
|
+
# ~~~ruby
|
5117
|
+
# case 3
|
5118
|
+
# when 1..5
|
5119
|
+
# puts "foo"
|
5120
|
+
# end
|
5121
|
+
# ~~~
|
5122
|
+
#
|
5123
|
+
class TopN
|
5124
|
+
attr_reader :number
|
5125
|
+
|
5126
|
+
def initialize(number)
|
5127
|
+
@number = number
|
5128
|
+
end
|
5129
|
+
|
5130
|
+
def disasm(fmt)
|
5131
|
+
fmt.instruction("topn", [fmt.object(number)])
|
5132
|
+
end
|
5133
|
+
|
5134
|
+
def to_a(_iseq)
|
5135
|
+
[:topn, number]
|
5136
|
+
end
|
5137
|
+
|
5138
|
+
def length
|
5139
|
+
2
|
5140
|
+
end
|
5141
|
+
|
5142
|
+
def pops
|
5143
|
+
0
|
5144
|
+
end
|
5145
|
+
|
5146
|
+
def pushes
|
5147
|
+
1
|
5148
|
+
end
|
5149
|
+
|
5150
|
+
def canonical
|
5151
|
+
self
|
5152
|
+
end
|
5153
|
+
|
5154
|
+
def call(vm)
|
5155
|
+
vm.push(vm.stack[-number - 1])
|
5156
|
+
end
|
5157
|
+
end
|
5158
|
+
|
5159
|
+
# ### Summary
|
5160
|
+
#
|
5161
|
+
# `toregexp` pops a number of values off the stack, combines them into a new
|
5162
|
+
# regular expression, and pushes the new regular expression onto the stack.
|
5163
|
+
#
|
5164
|
+
# ### Usage
|
5165
|
+
#
|
5166
|
+
# ~~~ruby
|
5167
|
+
# /foo #{bar}/
|
5168
|
+
# ~~~
|
5169
|
+
#
|
5170
|
+
class ToRegExp
|
5171
|
+
attr_reader :options, :length
|
5172
|
+
|
5173
|
+
def initialize(options, length)
|
5174
|
+
@options = options
|
5175
|
+
@length = length
|
5176
|
+
end
|
5177
|
+
|
5178
|
+
def disasm(fmt)
|
5179
|
+
fmt.instruction("toregexp", [fmt.object(options), fmt.object(length)])
|
5180
|
+
end
|
5181
|
+
|
5182
|
+
def to_a(_iseq)
|
5183
|
+
[:toregexp, options, length]
|
5184
|
+
end
|
5185
|
+
|
5186
|
+
def pops
|
5187
|
+
length
|
5188
|
+
end
|
5189
|
+
|
5190
|
+
def pushes
|
5191
|
+
1
|
5192
|
+
end
|
5193
|
+
|
5194
|
+
def canonical
|
5195
|
+
self
|
5196
|
+
end
|
5197
|
+
|
5198
|
+
def call(vm)
|
5199
|
+
vm.push(Regexp.new(vm.pop(length).join, options))
|
5200
|
+
end
|
5201
|
+
end
|
5202
|
+
end
|
5203
|
+
end
|