syntax_tree 5.0.1 → 5.1.0

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