syntax_tree 5.0.1 → 5.2.0

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