syntax_tree 5.0.1 → 5.1.0

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