syntax_tree 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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