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,2298 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module YARV
5
+ # This class is an experiment in transforming Syntax Tree nodes into their
6
+ # corresponding YARV instruction sequences. It attempts to mirror the
7
+ # behavior of RubyVM::InstructionSequence.compile.
8
+ #
9
+ # You use this as with any other visitor. First you parse code into a tree,
10
+ # then you visit it with this compiler. Visiting the root node of the tree
11
+ # will return a SyntaxTree::Visitor::Compiler::InstructionSequence object.
12
+ # With that object you can call #to_a on it, which will return a serialized
13
+ # form of the instruction sequence as an array. This array _should_ mirror
14
+ # the array given by RubyVM::InstructionSequence#to_a.
15
+ #
16
+ # As an example, here is how you would compile a single expression:
17
+ #
18
+ # program = SyntaxTree.parse("1 + 2")
19
+ # program.accept(SyntaxTree::YARV::Compiler.new).to_a
20
+ #
21
+ # [
22
+ # "YARVInstructionSequence/SimpleDataFormat",
23
+ # 3,
24
+ # 1,
25
+ # 1,
26
+ # {:arg_size=>0, :local_size=>0, :stack_max=>2},
27
+ # "<compiled>",
28
+ # "<compiled>",
29
+ # "<compiled>",
30
+ # 1,
31
+ # :top,
32
+ # [],
33
+ # {},
34
+ # [],
35
+ # [
36
+ # [:putobject_INT2FIX_1_],
37
+ # [:putobject, 2],
38
+ # [:opt_plus, {:mid=>:+, :flag=>16, :orig_argc=>1}],
39
+ # [:leave]
40
+ # ]
41
+ # ]
42
+ #
43
+ # Note that this is the same output as calling:
44
+ #
45
+ # RubyVM::InstructionSequence.compile("1 + 2").to_a
46
+ #
47
+ class Compiler < BasicVisitor
48
+ # This represents a set of options that can be passed to the compiler to
49
+ # control how it compiles the code. It mirrors the options that can be
50
+ # passed to RubyVM::InstructionSequence.compile, except it only includes
51
+ # options that actually change the behavior.
52
+ class Options
53
+ def initialize(
54
+ frozen_string_literal: false,
55
+ inline_const_cache: true,
56
+ operands_unification: true,
57
+ peephole_optimization: true,
58
+ specialized_instruction: true,
59
+ tailcall_optimization: false
60
+ )
61
+ @frozen_string_literal = frozen_string_literal
62
+ @inline_const_cache = inline_const_cache
63
+ @operands_unification = operands_unification
64
+ @peephole_optimization = peephole_optimization
65
+ @specialized_instruction = specialized_instruction
66
+ @tailcall_optimization = tailcall_optimization
67
+ end
68
+
69
+ def to_hash
70
+ {
71
+ frozen_string_literal: @frozen_string_literal,
72
+ inline_const_cache: @inline_const_cache,
73
+ operands_unification: @operands_unification,
74
+ peephole_optimization: @peephole_optimization,
75
+ specialized_instruction: @specialized_instruction,
76
+ tailcall_optimization: @tailcall_optimization
77
+ }
78
+ end
79
+
80
+ def frozen_string_literal!
81
+ @frozen_string_literal = true
82
+ end
83
+
84
+ def frozen_string_literal?
85
+ @frozen_string_literal
86
+ end
87
+
88
+ def inline_const_cache?
89
+ @inline_const_cache
90
+ end
91
+
92
+ def operands_unification?
93
+ @operands_unification
94
+ end
95
+
96
+ def peephole_optimization?
97
+ @peephole_optimization
98
+ end
99
+
100
+ def specialized_instruction?
101
+ @specialized_instruction
102
+ end
103
+
104
+ def tailcall_optimization?
105
+ @tailcall_optimization
106
+ end
107
+ end
108
+
109
+ # This visitor is responsible for converting Syntax Tree nodes into their
110
+ # corresponding Ruby structures. This is used to convert the operands of
111
+ # some instructions like putobject that push a Ruby object directly onto
112
+ # the stack. It is only used when the entire structure can be represented
113
+ # at compile-time, as opposed to constructed at run-time.
114
+ class RubyVisitor < BasicVisitor
115
+ # This error is raised whenever a node cannot be converted into a Ruby
116
+ # object at compile-time.
117
+ class CompilationError < StandardError
118
+ end
119
+
120
+ # This will attempt to compile the given node. If it's possible, then
121
+ # it will return the compiled object. Otherwise it will return nil.
122
+ def self.compile(node)
123
+ node.accept(new)
124
+ rescue CompilationError
125
+ end
126
+
127
+ def visit_array(node)
128
+ node.contents ? visit_all(node.contents.parts) : []
129
+ end
130
+
131
+ def visit_bare_assoc_hash(node)
132
+ node.assocs.to_h do |assoc|
133
+ # We can only convert regular key-value pairs. A double splat **
134
+ # operator means it has to be converted at run-time.
135
+ raise CompilationError unless assoc.is_a?(Assoc)
136
+ [visit(assoc.key), visit(assoc.value)]
137
+ end
138
+ end
139
+
140
+ def visit_float(node)
141
+ node.value.to_f
142
+ end
143
+
144
+ alias visit_hash visit_bare_assoc_hash
145
+
146
+ def visit_imaginary(node)
147
+ node.value.to_c
148
+ end
149
+
150
+ def visit_int(node)
151
+ case (value = node.value)
152
+ when /^0b/
153
+ value[2..].to_i(2)
154
+ when /^0o/
155
+ value[2..].to_i(8)
156
+ when /^0d/
157
+ value[2..].to_i
158
+ when /^0x/
159
+ value[2..].to_i(16)
160
+ else
161
+ value.to_i
162
+ end
163
+ end
164
+
165
+ def visit_label(node)
166
+ node.value.chomp(":").to_sym
167
+ end
168
+
169
+ def visit_mrhs(node)
170
+ visit_all(node.parts)
171
+ end
172
+
173
+ def visit_qsymbols(node)
174
+ node.elements.map { |element| visit(element).to_sym }
175
+ end
176
+
177
+ def visit_qwords(node)
178
+ visit_all(node.elements)
179
+ end
180
+
181
+ def visit_range(node)
182
+ left, right = [visit(node.left), visit(node.right)]
183
+ node.operator.value === ".." ? left..right : left...right
184
+ end
185
+
186
+ def visit_rational(node)
187
+ node.value.to_r
188
+ end
189
+
190
+ def visit_regexp_literal(node)
191
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
192
+ Regexp.new(node.parts.first.value, visit_regexp_literal_flags(node))
193
+ else
194
+ # Any interpolation of expressions or variables will result in the
195
+ # regular expression being constructed at run-time.
196
+ raise CompilationError
197
+ end
198
+ end
199
+
200
+ # This isn't actually a visit method, though maybe it should be. It is
201
+ # responsible for converting the set of string options on a regular
202
+ # expression into its equivalent integer.
203
+ def visit_regexp_literal_flags(node)
204
+ node
205
+ .options
206
+ .chars
207
+ .inject(0) do |accum, option|
208
+ accum |
209
+ case option
210
+ when "i"
211
+ Regexp::IGNORECASE
212
+ when "x"
213
+ Regexp::EXTENDED
214
+ when "m"
215
+ Regexp::MULTILINE
216
+ else
217
+ raise "Unknown regexp option: #{option}"
218
+ end
219
+ end
220
+ end
221
+
222
+ def visit_symbol_literal(node)
223
+ node.value.value.to_sym
224
+ end
225
+
226
+ def visit_symbols(node)
227
+ node.elements.map { |element| visit(element).to_sym }
228
+ end
229
+
230
+ def visit_tstring_content(node)
231
+ node.value
232
+ end
233
+
234
+ def visit_var_ref(node)
235
+ raise CompilationError unless node.value.is_a?(Kw)
236
+
237
+ case node.value.value
238
+ when "nil"
239
+ nil
240
+ when "true"
241
+ true
242
+ when "false"
243
+ false
244
+ else
245
+ raise CompilationError
246
+ end
247
+ end
248
+
249
+ def visit_word(node)
250
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
251
+ node.parts.first.value
252
+ else
253
+ # Any interpolation of expressions or variables will result in the
254
+ # string being constructed at run-time.
255
+ raise CompilationError
256
+ end
257
+ end
258
+
259
+ def visit_words(node)
260
+ visit_all(node.elements)
261
+ end
262
+
263
+ def visit_unsupported(_node)
264
+ raise CompilationError
265
+ end
266
+
267
+ # Please forgive the metaprogramming here. This is used to create visit
268
+ # methods for every node that we did not explicitly handle. By default
269
+ # each of these methods will raise a CompilationError.
270
+ handled = instance_methods(false)
271
+ (Visitor.instance_methods(false) - handled).each do |method|
272
+ alias_method method, :visit_unsupported
273
+ end
274
+ end
275
+
276
+ # These options mirror the compilation options that we currently support
277
+ # that can be also passed to RubyVM::InstructionSequence.compile.
278
+ attr_reader :options
279
+
280
+ # The current instruction sequence that is being compiled.
281
+ attr_reader :iseq
282
+
283
+ # A boolean to track if we're currently compiling the last statement
284
+ # within a set of statements. This information is necessary to determine
285
+ # if we need to return the value of the last statement.
286
+ attr_reader :last_statement
287
+
288
+ def initialize(options)
289
+ @options = options
290
+ @iseq = nil
291
+ @last_statement = false
292
+ end
293
+
294
+ def visit_BEGIN(node)
295
+ visit(node.statements)
296
+ end
297
+
298
+ def visit_CHAR(node)
299
+ if options.frozen_string_literal?
300
+ iseq.putobject(node.value[1..])
301
+ else
302
+ iseq.putstring(node.value[1..])
303
+ end
304
+ end
305
+
306
+ def visit_END(node)
307
+ start_line = node.location.start_line
308
+ once_iseq =
309
+ with_child_iseq(iseq.block_child_iseq(start_line)) do
310
+ postexe_iseq =
311
+ with_child_iseq(iseq.block_child_iseq(start_line)) do
312
+ iseq.event(:RUBY_EVENT_B_CALL)
313
+
314
+ *statements, last_statement = node.statements.body
315
+ visit_all(statements)
316
+ with_last_statement { visit(last_statement) }
317
+
318
+ iseq.event(:RUBY_EVENT_B_RETURN)
319
+ iseq.leave
320
+ end
321
+
322
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
323
+ iseq.send(
324
+ YARV.calldata(:"core#set_postexe", 0, CallData::CALL_FCALL),
325
+ postexe_iseq
326
+ )
327
+ iseq.leave
328
+ end
329
+
330
+ iseq.once(once_iseq, iseq.inline_storage)
331
+ iseq.pop
332
+ end
333
+
334
+ def visit_alias(node)
335
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
336
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CBASE)
337
+ visit(node.left)
338
+ visit(node.right)
339
+ iseq.send(YARV.calldata(:"core#set_method_alias", 3))
340
+ end
341
+
342
+ def visit_aref(node)
343
+ calldata = YARV.calldata(:[], 1)
344
+ visit(node.collection)
345
+
346
+ if !options.frozen_string_literal? &&
347
+ options.specialized_instruction? && (node.index.parts.length == 1)
348
+ arg = node.index.parts.first
349
+
350
+ if arg.is_a?(StringLiteral) && (arg.parts.length == 1)
351
+ string_part = arg.parts.first
352
+
353
+ if string_part.is_a?(TStringContent)
354
+ iseq.opt_aref_with(string_part.value, calldata)
355
+ return
356
+ end
357
+ end
358
+ end
359
+
360
+ visit(node.index)
361
+ iseq.send(calldata)
362
+ end
363
+
364
+ def visit_arg_block(node)
365
+ visit(node.value)
366
+ end
367
+
368
+ def visit_arg_paren(node)
369
+ visit(node.arguments)
370
+ end
371
+
372
+ def visit_arg_star(node)
373
+ visit(node.value)
374
+ iseq.splatarray(false)
375
+ end
376
+
377
+ def visit_args(node)
378
+ visit_all(node.parts)
379
+ end
380
+
381
+ def visit_array(node)
382
+ if (compiled = RubyVisitor.compile(node))
383
+ iseq.duparray(compiled)
384
+ elsif node.contents && node.contents.parts.length == 1 &&
385
+ node.contents.parts.first.is_a?(BareAssocHash) &&
386
+ node.contents.parts.first.assocs.length == 1 &&
387
+ node.contents.parts.first.assocs.first.is_a?(AssocSplat)
388
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
389
+ iseq.newhash(0)
390
+ visit(node.contents.parts.first)
391
+ iseq.send(YARV.calldata(:"core#hash_merge_kwd", 2))
392
+ iseq.newarraykwsplat(1)
393
+ else
394
+ length = 0
395
+
396
+ node.contents.parts.each do |part|
397
+ if part.is_a?(ArgStar)
398
+ if length > 0
399
+ iseq.newarray(length)
400
+ length = 0
401
+ end
402
+
403
+ visit(part.value)
404
+ iseq.concatarray
405
+ else
406
+ visit(part)
407
+ length += 1
408
+ end
409
+ end
410
+
411
+ iseq.newarray(length) if length > 0
412
+ iseq.concatarray if length > 0 && length != node.contents.parts.length
413
+ end
414
+ end
415
+
416
+ def visit_aryptn(node)
417
+ end
418
+
419
+ def visit_assign(node)
420
+ case node.target
421
+ when ARefField
422
+ calldata = YARV.calldata(:[]=, 2)
423
+
424
+ if !options.frozen_string_literal? &&
425
+ options.specialized_instruction? &&
426
+ (node.target.index.parts.length == 1)
427
+ arg = node.target.index.parts.first
428
+
429
+ if arg.is_a?(StringLiteral) && (arg.parts.length == 1)
430
+ string_part = arg.parts.first
431
+
432
+ if string_part.is_a?(TStringContent)
433
+ visit(node.target.collection)
434
+ visit(node.value)
435
+ iseq.swap
436
+ iseq.topn(1)
437
+ iseq.opt_aset_with(string_part.value, calldata)
438
+ iseq.pop
439
+ return
440
+ end
441
+ end
442
+ end
443
+
444
+ iseq.putnil
445
+ visit(node.target.collection)
446
+ visit(node.target.index)
447
+ visit(node.value)
448
+ iseq.setn(3)
449
+ iseq.send(calldata)
450
+ iseq.pop
451
+ when ConstPathField
452
+ names = constant_names(node.target)
453
+ name = names.pop
454
+
455
+ if RUBY_VERSION >= "3.2"
456
+ iseq.opt_getconstant_path(names)
457
+ visit(node.value)
458
+ iseq.swap
459
+ iseq.topn(1)
460
+ iseq.swap
461
+ iseq.setconstant(name)
462
+ else
463
+ visit(node.value)
464
+ iseq.dup if last_statement?
465
+ iseq.opt_getconstant_path(names)
466
+ iseq.setconstant(name)
467
+ end
468
+ when Field
469
+ iseq.putnil
470
+ visit(node.target)
471
+ visit(node.value)
472
+ iseq.setn(2)
473
+ iseq.send(YARV.calldata(:"#{node.target.name.value}=", 1))
474
+ iseq.pop
475
+ when TopConstField
476
+ name = node.target.constant.value.to_sym
477
+
478
+ if RUBY_VERSION >= "3.2"
479
+ iseq.putobject(Object)
480
+ visit(node.value)
481
+ iseq.swap
482
+ iseq.topn(1)
483
+ iseq.swap
484
+ iseq.setconstant(name)
485
+ else
486
+ visit(node.value)
487
+ iseq.dup if last_statement?
488
+ iseq.putobject(Object)
489
+ iseq.setconstant(name)
490
+ end
491
+ when VarField
492
+ visit(node.value)
493
+ iseq.dup if last_statement?
494
+
495
+ case node.target.value
496
+ when Const
497
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE)
498
+ iseq.setconstant(node.target.value.value.to_sym)
499
+ when CVar
500
+ iseq.setclassvariable(node.target.value.value.to_sym)
501
+ when GVar
502
+ iseq.setglobal(node.target.value.value.to_sym)
503
+ when Ident
504
+ lookup = visit(node.target)
505
+
506
+ if lookup.local.is_a?(LocalTable::BlockLocal)
507
+ iseq.setblockparam(lookup.index, lookup.level)
508
+ else
509
+ iseq.setlocal(lookup.index, lookup.level)
510
+ end
511
+ when IVar
512
+ iseq.setinstancevariable(node.target.value.value.to_sym)
513
+ end
514
+ end
515
+ end
516
+
517
+ def visit_assoc(node)
518
+ visit(node.key)
519
+ visit(node.value)
520
+ end
521
+
522
+ def visit_assoc_splat(node)
523
+ visit(node.value)
524
+ end
525
+
526
+ def visit_backref(node)
527
+ iseq.getspecial(GetSpecial::SVAR_BACKREF, node.value[1..].to_i << 1)
528
+ end
529
+
530
+ def visit_bare_assoc_hash(node)
531
+ if (compiled = RubyVisitor.compile(node))
532
+ iseq.duphash(compiled)
533
+ else
534
+ visit_all(node.assocs)
535
+ end
536
+ end
537
+
538
+ def visit_begin(node)
539
+ end
540
+
541
+ def visit_binary(node)
542
+ case node.operator
543
+ when :"&&"
544
+ done_label = iseq.label
545
+
546
+ visit(node.left)
547
+ iseq.dup
548
+ iseq.branchunless(done_label)
549
+
550
+ iseq.pop
551
+ visit(node.right)
552
+ iseq.push(done_label)
553
+ when :"||"
554
+ visit(node.left)
555
+ iseq.dup
556
+
557
+ skip_right_label = iseq.label
558
+ iseq.branchif(skip_right_label)
559
+ iseq.pop
560
+
561
+ visit(node.right)
562
+ iseq.push(skip_right_label)
563
+ else
564
+ visit(node.left)
565
+ visit(node.right)
566
+ iseq.send(YARV.calldata(node.operator, 1))
567
+ end
568
+ end
569
+
570
+ def visit_block(node)
571
+ with_child_iseq(iseq.block_child_iseq(node.location.start_line)) do
572
+ iseq.event(:RUBY_EVENT_B_CALL)
573
+ visit(node.block_var)
574
+ visit(node.bodystmt)
575
+ iseq.event(:RUBY_EVENT_B_RETURN)
576
+ iseq.leave
577
+ end
578
+ end
579
+
580
+ def visit_block_var(node)
581
+ params = node.params
582
+
583
+ if params.requireds.length == 1 && params.optionals.empty? &&
584
+ !params.rest && params.posts.empty? && params.keywords.empty? &&
585
+ !params.keyword_rest && !params.block
586
+ iseq.argument_options[:ambiguous_param0] = true
587
+ end
588
+
589
+ visit(node.params)
590
+
591
+ node.locals.each { |local| iseq.local_table.plain(local.value.to_sym) }
592
+ end
593
+
594
+ def visit_blockarg(node)
595
+ iseq.argument_options[:block_start] = iseq.argument_size
596
+ iseq.local_table.block(node.name.value.to_sym)
597
+ iseq.argument_size += 1
598
+ end
599
+
600
+ def visit_bodystmt(node)
601
+ visit(node.statements)
602
+ end
603
+
604
+ def visit_break(node)
605
+ end
606
+
607
+ def visit_call(node)
608
+ if node.is_a?(CallNode)
609
+ return(
610
+ visit_call(
611
+ CommandCall.new(
612
+ receiver: node.receiver,
613
+ operator: node.operator,
614
+ message: node.message,
615
+ arguments: node.arguments,
616
+ block: nil,
617
+ location: node.location
618
+ )
619
+ )
620
+ )
621
+ end
622
+
623
+ # Track whether or not this is a method call on a block proxy receiver.
624
+ # If it is, we can potentially do tailcall optimizations on it.
625
+ block_receiver = false
626
+
627
+ if node.receiver
628
+ if node.receiver.is_a?(VarRef)
629
+ lookup = iseq.local_variable(node.receiver.value.value.to_sym)
630
+
631
+ if lookup.local.is_a?(LocalTable::BlockLocal)
632
+ iseq.getblockparamproxy(lookup.index, lookup.level)
633
+ block_receiver = true
634
+ else
635
+ visit(node.receiver)
636
+ end
637
+ else
638
+ visit(node.receiver)
639
+ end
640
+ else
641
+ iseq.putself
642
+ end
643
+
644
+ after_call_label = nil
645
+ if node.operator&.value == "&."
646
+ iseq.dup
647
+ after_call_label = iseq.label
648
+ iseq.branchnil(after_call_label)
649
+ end
650
+
651
+ arg_parts = argument_parts(node.arguments)
652
+ argc = arg_parts.length
653
+ flag = 0
654
+
655
+ arg_parts.each do |arg_part|
656
+ case arg_part
657
+ when ArgBlock
658
+ argc -= 1
659
+ flag |= CallData::CALL_ARGS_BLOCKARG
660
+ visit(arg_part)
661
+ when ArgStar
662
+ flag |= CallData::CALL_ARGS_SPLAT
663
+ visit(arg_part)
664
+ when ArgsForward
665
+ flag |= CallData::CALL_TAILCALL if options.tailcall_optimization?
666
+
667
+ flag |= CallData::CALL_ARGS_SPLAT
668
+ lookup = iseq.local_table.find(:*)
669
+ iseq.getlocal(lookup.index, lookup.level)
670
+ iseq.splatarray(arg_parts.length != 1)
671
+
672
+ flag |= CallData::CALL_ARGS_BLOCKARG
673
+ lookup = iseq.local_table.find(:&)
674
+ iseq.getblockparamproxy(lookup.index, lookup.level)
675
+ when BareAssocHash
676
+ flag |= CallData::CALL_KW_SPLAT
677
+ visit(arg_part)
678
+ else
679
+ visit(arg_part)
680
+ end
681
+ end
682
+
683
+ block_iseq = visit(node.block) if node.block
684
+
685
+ # If there's no block and we don't already have any special flags set,
686
+ # then we can safely call this simple arguments. Note that has to be the
687
+ # first flag we set after looking at the arguments to get the flags
688
+ # correct.
689
+ flag |= CallData::CALL_ARGS_SIMPLE if block_iseq.nil? && flag == 0
690
+
691
+ # If there's no receiver, then this is an "fcall".
692
+ flag |= CallData::CALL_FCALL if node.receiver.nil?
693
+
694
+ # If we're calling a method on the passed block object and we have
695
+ # tailcall optimizations turned on, then we can set the tailcall flag.
696
+ if block_receiver && options.tailcall_optimization?
697
+ flag |= CallData::CALL_TAILCALL
698
+ end
699
+
700
+ iseq.send(
701
+ YARV.calldata(node.message.value.to_sym, argc, flag),
702
+ block_iseq
703
+ )
704
+ iseq.event(after_call_label) if after_call_label
705
+ end
706
+
707
+ def visit_case(node)
708
+ visit(node.value) if node.value
709
+
710
+ clauses = []
711
+ else_clause = nil
712
+ current = node.consequent
713
+
714
+ while current
715
+ clauses << current
716
+
717
+ if (current = current.consequent).is_a?(Else)
718
+ else_clause = current
719
+ break
720
+ end
721
+ end
722
+
723
+ branches =
724
+ clauses.map do |clause|
725
+ visit(clause.arguments)
726
+ iseq.topn(1)
727
+ iseq.send(
728
+ YARV.calldata(
729
+ :===,
730
+ 1,
731
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE
732
+ )
733
+ )
734
+
735
+ label = iseq.label
736
+ iseq.branchif(label)
737
+ [clause, label]
738
+ end
739
+
740
+ iseq.pop
741
+ else_clause ? visit(else_clause) : iseq.putnil
742
+ iseq.leave
743
+
744
+ branches.each_with_index do |(clause, label), index|
745
+ iseq.leave if index != 0
746
+ iseq.push(label)
747
+ iseq.pop
748
+ visit(clause)
749
+ end
750
+ end
751
+
752
+ def visit_class(node)
753
+ name = node.constant.constant.value.to_sym
754
+ class_iseq =
755
+ with_child_iseq(
756
+ iseq.class_child_iseq(name, node.location.start_line)
757
+ ) do
758
+ iseq.event(:RUBY_EVENT_CLASS)
759
+ visit(node.bodystmt)
760
+ iseq.event(:RUBY_EVENT_END)
761
+ iseq.leave
762
+ end
763
+
764
+ flags = DefineClass::TYPE_CLASS
765
+
766
+ case node.constant
767
+ when ConstPathRef
768
+ flags |= DefineClass::FLAG_SCOPED
769
+ visit(node.constant.parent)
770
+ when ConstRef
771
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE)
772
+ when TopConstRef
773
+ flags |= DefineClass::FLAG_SCOPED
774
+ iseq.putobject(Object)
775
+ end
776
+
777
+ if node.superclass
778
+ flags |= DefineClass::FLAG_HAS_SUPERCLASS
779
+ visit(node.superclass)
780
+ else
781
+ iseq.putnil
782
+ end
783
+
784
+ iseq.defineclass(name, class_iseq, flags)
785
+ end
786
+
787
+ def visit_command(node)
788
+ visit_call(
789
+ CommandCall.new(
790
+ receiver: nil,
791
+ operator: nil,
792
+ message: node.message,
793
+ arguments: node.arguments,
794
+ block: node.block,
795
+ location: node.location
796
+ )
797
+ )
798
+ end
799
+
800
+ def visit_command_call(node)
801
+ visit_call(
802
+ CommandCall.new(
803
+ receiver: node.receiver,
804
+ operator: node.operator,
805
+ message: node.message,
806
+ arguments: node.arguments,
807
+ block: node.block,
808
+ location: node.location
809
+ )
810
+ )
811
+ end
812
+
813
+ def visit_const_path_field(node)
814
+ visit(node.parent)
815
+ end
816
+
817
+ def visit_const_path_ref(node)
818
+ names = constant_names(node)
819
+ iseq.opt_getconstant_path(names)
820
+ end
821
+
822
+ def visit_def(node)
823
+ name = node.name.value.to_sym
824
+ method_iseq =
825
+ iseq.method_child_iseq(name.to_s, node.location.start_line)
826
+
827
+ with_child_iseq(method_iseq) do
828
+ visit(node.params) if node.params
829
+ iseq.event(:RUBY_EVENT_CALL)
830
+ visit(node.bodystmt)
831
+ iseq.event(:RUBY_EVENT_RETURN)
832
+ iseq.leave
833
+ end
834
+
835
+ if node.target
836
+ visit(node.target)
837
+ iseq.definesmethod(name, method_iseq)
838
+ else
839
+ iseq.definemethod(name, method_iseq)
840
+ end
841
+
842
+ iseq.putobject(name)
843
+ end
844
+
845
+ def visit_defined(node)
846
+ case node.value
847
+ when Assign
848
+ # If we're assigning to a local variable, then we need to make sure
849
+ # that we put it into the local table.
850
+ if node.value.target.is_a?(VarField) &&
851
+ node.value.target.value.is_a?(Ident)
852
+ iseq.local_table.plain(node.value.target.value.value.to_sym)
853
+ end
854
+
855
+ iseq.putobject("assignment")
856
+ when VarRef
857
+ value = node.value.value
858
+ name = value.value.to_sym
859
+
860
+ case value
861
+ when Const
862
+ iseq.putnil
863
+ iseq.defined(Defined::TYPE_CONST, name, "constant")
864
+ when CVar
865
+ iseq.putnil
866
+ iseq.defined(Defined::TYPE_CVAR, name, "class variable")
867
+ when GVar
868
+ iseq.putnil
869
+ iseq.defined(Defined::TYPE_GVAR, name, "global-variable")
870
+ when Ident
871
+ iseq.putobject("local-variable")
872
+ when IVar
873
+ iseq.putnil
874
+ iseq.defined(Defined::TYPE_IVAR, name, "instance-variable")
875
+ when Kw
876
+ case name
877
+ when :false
878
+ iseq.putobject("false")
879
+ when :nil
880
+ iseq.putobject("nil")
881
+ when :self
882
+ iseq.putobject("self")
883
+ when :true
884
+ iseq.putobject("true")
885
+ end
886
+ end
887
+ when VCall
888
+ iseq.putself
889
+
890
+ name = node.value.value.value.to_sym
891
+ iseq.defined(Defined::TYPE_FUNC, name, "method")
892
+ when YieldNode
893
+ iseq.putnil
894
+ iseq.defined(Defined::TYPE_YIELD, false, "yield")
895
+ when ZSuper
896
+ iseq.putnil
897
+ iseq.defined(Defined::TYPE_ZSUPER, false, "super")
898
+ else
899
+ iseq.putobject("expression")
900
+ end
901
+ end
902
+
903
+ def visit_dyna_symbol(node)
904
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
905
+ iseq.putobject(node.parts.first.value.to_sym)
906
+ end
907
+ end
908
+
909
+ def visit_else(node)
910
+ visit(node.statements)
911
+ iseq.pop unless last_statement?
912
+ end
913
+
914
+ def visit_elsif(node)
915
+ visit_if(
916
+ IfNode.new(
917
+ predicate: node.predicate,
918
+ statements: node.statements,
919
+ consequent: node.consequent,
920
+ location: node.location
921
+ )
922
+ )
923
+ end
924
+
925
+ def visit_ensure(node)
926
+ end
927
+
928
+ def visit_field(node)
929
+ visit(node.parent)
930
+ end
931
+
932
+ def visit_float(node)
933
+ iseq.putobject(node.accept(RubyVisitor.new))
934
+ end
935
+
936
+ def visit_fndptn(node)
937
+ end
938
+
939
+ def visit_for(node)
940
+ visit(node.collection)
941
+
942
+ name = node.index.value.value.to_sym
943
+ iseq.local_table.plain(name)
944
+
945
+ block_iseq =
946
+ with_child_iseq(
947
+ iseq.block_child_iseq(node.statements.location.start_line)
948
+ ) do
949
+ iseq.argument_options[:lead_num] ||= 0
950
+ iseq.argument_options[:lead_num] += 1
951
+ iseq.argument_options[:ambiguous_param0] = true
952
+
953
+ iseq.argument_size += 1
954
+ iseq.local_table.plain(2)
955
+
956
+ iseq.getlocal(0, 0)
957
+
958
+ local_variable = iseq.local_variable(name)
959
+ iseq.setlocal(local_variable.index, local_variable.level)
960
+
961
+ iseq.event(:RUBY_EVENT_B_CALL)
962
+ iseq.nop
963
+
964
+ visit(node.statements)
965
+ iseq.event(:RUBY_EVENT_B_RETURN)
966
+ iseq.leave
967
+ end
968
+
969
+ iseq.send(YARV.calldata(:each, 0, 0), block_iseq)
970
+ end
971
+
972
+ def visit_hash(node)
973
+ if (compiled = RubyVisitor.compile(node))
974
+ iseq.duphash(compiled)
975
+ else
976
+ visit_all(node.assocs)
977
+ iseq.newhash(node.assocs.length * 2)
978
+ end
979
+ end
980
+
981
+ def visit_hshptn(node)
982
+ end
983
+
984
+ def visit_heredoc(node)
985
+ if node.beginning.value.end_with?("`")
986
+ visit_xstring_literal(node)
987
+ elsif node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
988
+ visit(node.parts.first)
989
+ else
990
+ length = visit_string_parts(node)
991
+ iseq.concatstrings(length)
992
+ end
993
+ end
994
+
995
+ def visit_if(node)
996
+ if node.predicate.is_a?(RangeNode)
997
+ true_label = iseq.label
998
+ false_label = iseq.label
999
+ end_label = iseq.label
1000
+
1001
+ iseq.getspecial(GetSpecial::SVAR_FLIPFLOP_START, 0)
1002
+ iseq.branchif(true_label)
1003
+
1004
+ visit(node.predicate.left)
1005
+ iseq.branchunless(end_label)
1006
+
1007
+ iseq.putobject(true)
1008
+ iseq.setspecial(GetSpecial::SVAR_FLIPFLOP_START)
1009
+
1010
+ iseq.push(true_label)
1011
+ visit(node.predicate.right)
1012
+ iseq.branchunless(false_label)
1013
+
1014
+ iseq.putobject(false)
1015
+ iseq.setspecial(GetSpecial::SVAR_FLIPFLOP_START)
1016
+
1017
+ iseq.push(false_label)
1018
+ visit(node.statements)
1019
+ iseq.leave
1020
+ iseq.push(end_label)
1021
+ iseq.putnil
1022
+ else
1023
+ consequent_label = iseq.label
1024
+
1025
+ visit(node.predicate)
1026
+ iseq.branchunless(consequent_label)
1027
+ visit(node.statements)
1028
+
1029
+ if last_statement?
1030
+ iseq.leave
1031
+ iseq.push(consequent_label)
1032
+ node.consequent ? visit(node.consequent) : iseq.putnil
1033
+ else
1034
+ iseq.pop
1035
+
1036
+ if node.consequent
1037
+ done_label = iseq.label
1038
+ iseq.jump(done_label)
1039
+ iseq.push(consequent_label)
1040
+ visit(node.consequent)
1041
+ iseq.push(done_label)
1042
+ else
1043
+ iseq.push(consequent_label)
1044
+ end
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ def visit_if_op(node)
1050
+ visit_if(
1051
+ IfNode.new(
1052
+ predicate: node.predicate,
1053
+ statements: node.truthy,
1054
+ consequent:
1055
+ Else.new(
1056
+ keyword: Kw.new(value: "else", location: Location.default),
1057
+ statements: node.falsy,
1058
+ location: Location.default
1059
+ ),
1060
+ location: Location.default
1061
+ )
1062
+ )
1063
+ end
1064
+
1065
+ def visit_imaginary(node)
1066
+ iseq.putobject(node.accept(RubyVisitor.new))
1067
+ end
1068
+
1069
+ def visit_int(node)
1070
+ iseq.putobject(node.accept(RubyVisitor.new))
1071
+ end
1072
+
1073
+ def visit_kwrest_param(node)
1074
+ iseq.argument_options[:kwrest] = iseq.argument_size
1075
+ iseq.argument_size += 1
1076
+ iseq.local_table.plain(node.name.value.to_sym)
1077
+ end
1078
+
1079
+ def visit_label(node)
1080
+ iseq.putobject(node.accept(RubyVisitor.new))
1081
+ end
1082
+
1083
+ def visit_lambda(node)
1084
+ lambda_iseq =
1085
+ with_child_iseq(iseq.block_child_iseq(node.location.start_line)) do
1086
+ iseq.event(:RUBY_EVENT_B_CALL)
1087
+ visit(node.params)
1088
+ visit(node.statements)
1089
+ iseq.event(:RUBY_EVENT_B_RETURN)
1090
+ iseq.leave
1091
+ end
1092
+
1093
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
1094
+ iseq.send(YARV.calldata(:lambda, 0, CallData::CALL_FCALL), lambda_iseq)
1095
+ end
1096
+
1097
+ def visit_lambda_var(node)
1098
+ visit_block_var(node)
1099
+ end
1100
+
1101
+ def visit_massign(node)
1102
+ visit(node.value)
1103
+ iseq.dup
1104
+ visit(node.target)
1105
+ end
1106
+
1107
+ def visit_method_add_block(node)
1108
+ visit_call(
1109
+ CommandCall.new(
1110
+ receiver: node.call.receiver,
1111
+ operator: node.call.operator,
1112
+ message: node.call.message,
1113
+ arguments: node.call.arguments,
1114
+ block: node.block,
1115
+ location: node.location
1116
+ )
1117
+ )
1118
+ end
1119
+
1120
+ def visit_mlhs(node)
1121
+ lookups = []
1122
+ node.parts.each do |part|
1123
+ case part
1124
+ when VarField
1125
+ lookups << visit(part)
1126
+ end
1127
+ end
1128
+
1129
+ iseq.expandarray(lookups.length, 0)
1130
+ lookups.each { |lookup| iseq.setlocal(lookup.index, lookup.level) }
1131
+ end
1132
+
1133
+ def visit_module(node)
1134
+ name = node.constant.constant.value.to_sym
1135
+ module_iseq =
1136
+ with_child_iseq(
1137
+ iseq.module_child_iseq(name, node.location.start_line)
1138
+ ) do
1139
+ iseq.event(:RUBY_EVENT_CLASS)
1140
+ visit(node.bodystmt)
1141
+ iseq.event(:RUBY_EVENT_END)
1142
+ iseq.leave
1143
+ end
1144
+
1145
+ flags = DefineClass::TYPE_MODULE
1146
+
1147
+ case node.constant
1148
+ when ConstPathRef
1149
+ flags |= DefineClass::FLAG_SCOPED
1150
+ visit(node.constant.parent)
1151
+ when ConstRef
1152
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE)
1153
+ when TopConstRef
1154
+ flags |= DefineClass::FLAG_SCOPED
1155
+ iseq.putobject(Object)
1156
+ end
1157
+
1158
+ iseq.putnil
1159
+ iseq.defineclass(name, module_iseq, flags)
1160
+ end
1161
+
1162
+ def visit_mrhs(node)
1163
+ if (compiled = RubyVisitor.compile(node))
1164
+ iseq.duparray(compiled)
1165
+ else
1166
+ visit_all(node.parts)
1167
+ iseq.newarray(node.parts.length)
1168
+ end
1169
+ end
1170
+
1171
+ def visit_next(node)
1172
+ end
1173
+
1174
+ def visit_not(node)
1175
+ visit(node.statement)
1176
+ iseq.send(YARV.calldata(:!))
1177
+ end
1178
+
1179
+ def visit_opassign(node)
1180
+ flag = CallData::CALL_ARGS_SIMPLE
1181
+ if node.target.is_a?(ConstPathField) || node.target.is_a?(TopConstField)
1182
+ flag |= CallData::CALL_FCALL
1183
+ end
1184
+
1185
+ case (operator = node.operator.value.chomp("=").to_sym)
1186
+ when :"&&"
1187
+ done_label = iseq.label
1188
+
1189
+ with_opassign(node) do
1190
+ iseq.dup
1191
+ iseq.branchunless(done_label)
1192
+ iseq.pop
1193
+ visit(node.value)
1194
+ end
1195
+
1196
+ case node.target
1197
+ when ARefField
1198
+ iseq.leave
1199
+ iseq.push(done_label)
1200
+ iseq.setn(3)
1201
+ iseq.adjuststack(3)
1202
+ when ConstPathField, TopConstField
1203
+ iseq.push(done_label)
1204
+ iseq.swap
1205
+ iseq.pop
1206
+ else
1207
+ iseq.push(done_label)
1208
+ end
1209
+ when :"||"
1210
+ if node.target.is_a?(ConstPathField) ||
1211
+ node.target.is_a?(TopConstField)
1212
+ opassign_defined(node)
1213
+ iseq.swap
1214
+ iseq.pop
1215
+ elsif node.target.is_a?(VarField) &&
1216
+ [Const, CVar, GVar].include?(node.target.value.class)
1217
+ opassign_defined(node)
1218
+ else
1219
+ skip_value_label = iseq.label
1220
+
1221
+ with_opassign(node) do
1222
+ iseq.dup
1223
+ iseq.branchif(skip_value_label)
1224
+ iseq.pop
1225
+ visit(node.value)
1226
+ end
1227
+
1228
+ if node.target.is_a?(ARefField)
1229
+ iseq.leave
1230
+ iseq.push(skip_value_label)
1231
+ iseq.setn(3)
1232
+ iseq.adjuststack(3)
1233
+ else
1234
+ iseq.push(skip_value_label)
1235
+ end
1236
+ end
1237
+ else
1238
+ with_opassign(node) do
1239
+ visit(node.value)
1240
+ iseq.send(YARV.calldata(operator, 1, flag))
1241
+ end
1242
+ end
1243
+ end
1244
+
1245
+ def visit_params(node)
1246
+ if node.requireds.any?
1247
+ iseq.argument_options[:lead_num] = 0
1248
+
1249
+ node.requireds.each do |required|
1250
+ iseq.local_table.plain(required.value.to_sym)
1251
+ iseq.argument_size += 1
1252
+ iseq.argument_options[:lead_num] += 1
1253
+ end
1254
+ end
1255
+
1256
+ node.optionals.each do |(optional, value)|
1257
+ index = iseq.local_table.size
1258
+ name = optional.value.to_sym
1259
+
1260
+ iseq.local_table.plain(name)
1261
+ iseq.argument_size += 1
1262
+
1263
+ unless iseq.argument_options.key?(:opt)
1264
+ start_label = iseq.label
1265
+ iseq.push(start_label)
1266
+ iseq.argument_options[:opt] = [start_label]
1267
+ end
1268
+
1269
+ visit(value)
1270
+ iseq.setlocal(index, 0)
1271
+
1272
+ arg_given_label = iseq.label
1273
+ iseq.push(arg_given_label)
1274
+ iseq.argument_options[:opt] << arg_given_label
1275
+ end
1276
+
1277
+ visit(node.rest) if node.rest
1278
+
1279
+ if node.posts.any?
1280
+ iseq.argument_options[:post_start] = iseq.argument_size
1281
+ iseq.argument_options[:post_num] = 0
1282
+
1283
+ node.posts.each do |post|
1284
+ iseq.local_table.plain(post.value.to_sym)
1285
+ iseq.argument_size += 1
1286
+ iseq.argument_options[:post_num] += 1
1287
+ end
1288
+ end
1289
+
1290
+ if node.keywords.any?
1291
+ iseq.argument_options[:kwbits] = 0
1292
+ iseq.argument_options[:keyword] = []
1293
+
1294
+ keyword_bits_name = node.keyword_rest ? 3 : 2
1295
+ iseq.argument_size += 1
1296
+ keyword_bits_index = iseq.local_table.locals.size + node.keywords.size
1297
+
1298
+ node.keywords.each_with_index do |(keyword, value), keyword_index|
1299
+ name = keyword.value.chomp(":").to_sym
1300
+ index = iseq.local_table.size
1301
+
1302
+ iseq.local_table.plain(name)
1303
+ iseq.argument_size += 1
1304
+ iseq.argument_options[:kwbits] += 1
1305
+
1306
+ if value.nil?
1307
+ iseq.argument_options[:keyword] << name
1308
+ elsif (compiled = RubyVisitor.compile(value))
1309
+ iseq.argument_options[:keyword] << [name, compiled]
1310
+ else
1311
+ skip_value_label = iseq.label
1312
+
1313
+ iseq.argument_options[:keyword] << [name]
1314
+ iseq.checkkeyword(keyword_bits_index, keyword_index)
1315
+ iseq.branchif(skip_value_label)
1316
+ visit(value)
1317
+ iseq.setlocal(index, 0)
1318
+ iseq.push(skip_value_label)
1319
+ end
1320
+ end
1321
+
1322
+ iseq.local_table.plain(keyword_bits_name)
1323
+ end
1324
+
1325
+ if node.keyword_rest.is_a?(ArgsForward)
1326
+ if RUBY_VERSION >= "3.2"
1327
+ iseq.local_table.plain(:*)
1328
+ iseq.local_table.plain(:&)
1329
+ iseq.local_table.plain(:"...")
1330
+
1331
+ iseq.argument_options[:rest_start] = iseq.argument_size
1332
+ iseq.argument_options[:block_start] = iseq.argument_size + 1
1333
+
1334
+ iseq.argument_size += 2
1335
+ else
1336
+ iseq.local_table.plain(:*)
1337
+ iseq.local_table.plain(:&)
1338
+
1339
+ iseq.argument_options[:rest_start] = iseq.argument_size
1340
+ iseq.argument_options[:block_start] = iseq.argument_size + 1
1341
+
1342
+ iseq.argument_size += 2
1343
+ end
1344
+ elsif node.keyword_rest
1345
+ visit(node.keyword_rest)
1346
+ end
1347
+
1348
+ visit(node.block) if node.block
1349
+ end
1350
+
1351
+ def visit_paren(node)
1352
+ visit(node.contents)
1353
+ end
1354
+
1355
+ def visit_pinned_begin(node)
1356
+ end
1357
+
1358
+ def visit_pinned_var_ref(node)
1359
+ end
1360
+
1361
+ def visit_program(node)
1362
+ node.statements.body.each do |statement|
1363
+ break unless statement.is_a?(Comment)
1364
+
1365
+ if statement.value == "# frozen_string_literal: true"
1366
+ options.frozen_string_literal!
1367
+ end
1368
+ end
1369
+
1370
+ preexes = []
1371
+ statements = []
1372
+
1373
+ node.statements.body.each do |statement|
1374
+ case statement
1375
+ when Comment, EmbDoc, EndContent, VoidStmt
1376
+ # ignore
1377
+ when BEGINBlock
1378
+ preexes << statement
1379
+ else
1380
+ statements << statement
1381
+ end
1382
+ end
1383
+
1384
+ top_iseq =
1385
+ InstructionSequence.new(
1386
+ "<compiled>",
1387
+ "<compiled>",
1388
+ 1,
1389
+ :top,
1390
+ nil,
1391
+ options
1392
+ )
1393
+
1394
+ with_child_iseq(top_iseq) do
1395
+ visit_all(preexes)
1396
+
1397
+ if statements.empty?
1398
+ iseq.putnil
1399
+ else
1400
+ *statements, last_statement = statements
1401
+ visit_all(statements)
1402
+ with_last_statement { visit(last_statement) }
1403
+ end
1404
+
1405
+ iseq.leave
1406
+ end
1407
+
1408
+ top_iseq.compile!
1409
+ top_iseq
1410
+ end
1411
+
1412
+ def visit_qsymbols(node)
1413
+ iseq.duparray(node.accept(RubyVisitor.new))
1414
+ end
1415
+
1416
+ def visit_qwords(node)
1417
+ if options.frozen_string_literal?
1418
+ iseq.duparray(node.accept(RubyVisitor.new))
1419
+ else
1420
+ visit_all(node.elements)
1421
+ iseq.newarray(node.elements.length)
1422
+ end
1423
+ end
1424
+
1425
+ def visit_range(node)
1426
+ if (compiled = RubyVisitor.compile(node))
1427
+ iseq.putobject(compiled)
1428
+ else
1429
+ visit(node.left)
1430
+ visit(node.right)
1431
+ iseq.newrange(node.operator.value == ".." ? 0 : 1)
1432
+ end
1433
+ end
1434
+
1435
+ def visit_rassign(node)
1436
+ iseq.putnil
1437
+
1438
+ if node.operator.is_a?(Kw)
1439
+ match_label = iseq.label
1440
+
1441
+ visit(node.value)
1442
+ iseq.dup
1443
+
1444
+ visit_pattern(node.pattern, match_label)
1445
+
1446
+ iseq.pop
1447
+ iseq.pop
1448
+ iseq.putobject(false)
1449
+ iseq.leave
1450
+
1451
+ iseq.push(match_label)
1452
+ iseq.adjuststack(2)
1453
+ iseq.putobject(true)
1454
+ else
1455
+ no_key_label = iseq.label
1456
+ end_leave_label = iseq.label
1457
+ end_label = iseq.label
1458
+
1459
+ iseq.putnil
1460
+ iseq.putobject(false)
1461
+ iseq.putnil
1462
+ iseq.putnil
1463
+ visit(node.value)
1464
+ iseq.dup
1465
+
1466
+ visit_pattern(node.pattern, end_label)
1467
+
1468
+ # First we're going to push the core onto the stack, then we'll check
1469
+ # if the value to match is truthy. If it is, we'll jump down to raise
1470
+ # NoMatchingPatternKeyError. Otherwise we'll raise
1471
+ # NoMatchingPatternError.
1472
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
1473
+ iseq.topn(4)
1474
+ iseq.branchif(no_key_label)
1475
+
1476
+ # Here we're going to raise NoMatchingPatternError.
1477
+ iseq.putobject(NoMatchingPatternError)
1478
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
1479
+ iseq.putobject("%p: %s")
1480
+ iseq.topn(4)
1481
+ iseq.topn(7)
1482
+ iseq.send(YARV.calldata(:"core#sprintf", 3))
1483
+ iseq.send(YARV.calldata(:"core#raise", 2))
1484
+ iseq.jump(end_leave_label)
1485
+
1486
+ # Here we're going to raise NoMatchingPatternKeyError.
1487
+ iseq.push(no_key_label)
1488
+ iseq.putobject(NoMatchingPatternKeyError)
1489
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
1490
+ iseq.putobject("%p: %s")
1491
+ iseq.topn(4)
1492
+ iseq.topn(7)
1493
+ iseq.send(YARV.calldata(:"core#sprintf", 3))
1494
+ iseq.topn(7)
1495
+ iseq.topn(9)
1496
+ iseq.send(
1497
+ YARV.calldata(:new, 1, CallData::CALL_KWARG, %i[matchee key])
1498
+ )
1499
+ iseq.send(YARV.calldata(:"core#raise", 1))
1500
+
1501
+ iseq.push(end_leave_label)
1502
+ iseq.adjuststack(7)
1503
+ iseq.putnil
1504
+ iseq.leave
1505
+
1506
+ iseq.push(end_label)
1507
+ iseq.adjuststack(6)
1508
+ iseq.putnil
1509
+ end
1510
+ end
1511
+
1512
+ def visit_rational(node)
1513
+ iseq.putobject(node.accept(RubyVisitor.new))
1514
+ end
1515
+
1516
+ def visit_redo(node)
1517
+ end
1518
+
1519
+ def visit_regexp_literal(node)
1520
+ if (compiled = RubyVisitor.compile(node))
1521
+ iseq.putobject(compiled)
1522
+ else
1523
+ flags = RubyVisitor.new.visit_regexp_literal_flags(node)
1524
+ length = visit_string_parts(node)
1525
+ iseq.toregexp(flags, length)
1526
+ end
1527
+ end
1528
+
1529
+ def visit_rescue(node)
1530
+ end
1531
+
1532
+ def visit_rescue_ex(node)
1533
+ end
1534
+
1535
+ def visit_rescue_mod(node)
1536
+ end
1537
+
1538
+ def visit_rest_param(node)
1539
+ iseq.local_table.plain(node.name.value.to_sym)
1540
+ iseq.argument_options[:rest_start] = iseq.argument_size
1541
+ iseq.argument_size += 1
1542
+ end
1543
+
1544
+ def visit_retry(node)
1545
+ end
1546
+
1547
+ def visit_return(node)
1548
+ end
1549
+
1550
+ def visit_sclass(node)
1551
+ visit(node.target)
1552
+ iseq.putnil
1553
+
1554
+ singleton_iseq =
1555
+ with_child_iseq(
1556
+ iseq.singleton_class_child_iseq(node.location.start_line)
1557
+ ) do
1558
+ iseq.event(:RUBY_EVENT_CLASS)
1559
+ visit(node.bodystmt)
1560
+ iseq.event(:RUBY_EVENT_END)
1561
+ iseq.leave
1562
+ end
1563
+
1564
+ iseq.defineclass(
1565
+ :singletonclass,
1566
+ singleton_iseq,
1567
+ DefineClass::TYPE_SINGLETON_CLASS
1568
+ )
1569
+ end
1570
+
1571
+ def visit_statements(node)
1572
+ statements =
1573
+ node.body.select do |statement|
1574
+ case statement
1575
+ when Comment, EmbDoc, EndContent, VoidStmt
1576
+ false
1577
+ else
1578
+ true
1579
+ end
1580
+ end
1581
+
1582
+ statements.empty? ? iseq.putnil : visit_all(statements)
1583
+ end
1584
+
1585
+ def visit_string_concat(node)
1586
+ value = node.left.parts.first.value + node.right.parts.first.value
1587
+
1588
+ visit_string_literal(
1589
+ StringLiteral.new(
1590
+ parts: [TStringContent.new(value: value, location: node.location)],
1591
+ quote: node.left.quote,
1592
+ location: node.location
1593
+ )
1594
+ )
1595
+ end
1596
+
1597
+ def visit_string_embexpr(node)
1598
+ visit(node.statements)
1599
+ end
1600
+
1601
+ def visit_string_literal(node)
1602
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
1603
+ visit(node.parts.first)
1604
+ else
1605
+ length = visit_string_parts(node)
1606
+ iseq.concatstrings(length)
1607
+ end
1608
+ end
1609
+
1610
+ def visit_super(node)
1611
+ iseq.putself
1612
+ visit(node.arguments)
1613
+ iseq.invokesuper(
1614
+ YARV.calldata(
1615
+ nil,
1616
+ argument_parts(node.arguments).length,
1617
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE |
1618
+ CallData::CALL_SUPER
1619
+ ),
1620
+ nil
1621
+ )
1622
+ end
1623
+
1624
+ def visit_symbol_literal(node)
1625
+ iseq.putobject(node.accept(RubyVisitor.new))
1626
+ end
1627
+
1628
+ def visit_symbols(node)
1629
+ if (compiled = RubyVisitor.compile(node))
1630
+ iseq.duparray(compiled)
1631
+ else
1632
+ node.elements.each do |element|
1633
+ if element.parts.length == 1 &&
1634
+ element.parts.first.is_a?(TStringContent)
1635
+ iseq.putobject(element.parts.first.value.to_sym)
1636
+ else
1637
+ length = visit_string_parts(element)
1638
+ iseq.concatstrings(length)
1639
+ iseq.intern
1640
+ end
1641
+ end
1642
+
1643
+ iseq.newarray(node.elements.length)
1644
+ end
1645
+ end
1646
+
1647
+ def visit_top_const_ref(node)
1648
+ iseq.opt_getconstant_path(constant_names(node))
1649
+ end
1650
+
1651
+ def visit_tstring_content(node)
1652
+ if options.frozen_string_literal?
1653
+ iseq.putobject(node.accept(RubyVisitor.new))
1654
+ else
1655
+ iseq.putstring(node.accept(RubyVisitor.new))
1656
+ end
1657
+ end
1658
+
1659
+ def visit_unary(node)
1660
+ method_id =
1661
+ case node.operator
1662
+ when "+", "-"
1663
+ "#{node.operator}@"
1664
+ else
1665
+ node.operator
1666
+ end
1667
+
1668
+ visit_call(
1669
+ CommandCall.new(
1670
+ receiver: node.statement,
1671
+ operator: nil,
1672
+ message: Ident.new(value: method_id, location: Location.default),
1673
+ arguments: nil,
1674
+ block: nil,
1675
+ location: Location.default
1676
+ )
1677
+ )
1678
+ end
1679
+
1680
+ def visit_undef(node)
1681
+ node.symbols.each_with_index do |symbol, index|
1682
+ iseq.pop if index != 0
1683
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
1684
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CBASE)
1685
+ visit(symbol)
1686
+ iseq.send(YARV.calldata(:"core#undef_method", 2))
1687
+ end
1688
+ end
1689
+
1690
+ def visit_unless(node)
1691
+ statements_label = iseq.label
1692
+
1693
+ visit(node.predicate)
1694
+ iseq.branchunless(statements_label)
1695
+ node.consequent ? visit(node.consequent) : iseq.putnil
1696
+
1697
+ if last_statement?
1698
+ iseq.leave
1699
+ iseq.push(statements_label)
1700
+ visit(node.statements)
1701
+ else
1702
+ iseq.pop
1703
+
1704
+ if node.consequent
1705
+ done_label = iseq.label
1706
+ iseq.jump(done_label)
1707
+ iseq.push(statements_label)
1708
+ visit(node.consequent)
1709
+ iseq.push(done_label)
1710
+ else
1711
+ iseq.push(statements_label)
1712
+ end
1713
+ end
1714
+ end
1715
+
1716
+ def visit_until(node)
1717
+ predicate_label = iseq.label
1718
+ statements_label = iseq.label
1719
+
1720
+ iseq.jump(predicate_label)
1721
+ iseq.putnil
1722
+ iseq.pop
1723
+ iseq.jump(predicate_label)
1724
+
1725
+ iseq.push(statements_label)
1726
+ visit(node.statements)
1727
+ iseq.pop
1728
+
1729
+ iseq.push(predicate_label)
1730
+ visit(node.predicate)
1731
+ iseq.branchunless(statements_label)
1732
+ iseq.putnil if last_statement?
1733
+ end
1734
+
1735
+ def visit_var_field(node)
1736
+ case node.value
1737
+ when CVar, IVar
1738
+ name = node.value.value.to_sym
1739
+ iseq.inline_storage_for(name)
1740
+ when Ident
1741
+ name = node.value.value.to_sym
1742
+
1743
+ if (local_variable = iseq.local_variable(name))
1744
+ local_variable
1745
+ else
1746
+ iseq.local_table.plain(name)
1747
+ iseq.local_variable(name)
1748
+ end
1749
+ end
1750
+ end
1751
+
1752
+ def visit_var_ref(node)
1753
+ case node.value
1754
+ when Const
1755
+ iseq.opt_getconstant_path(constant_names(node))
1756
+ when CVar
1757
+ name = node.value.value.to_sym
1758
+ iseq.getclassvariable(name)
1759
+ when GVar
1760
+ iseq.getglobal(node.value.value.to_sym)
1761
+ when Ident
1762
+ lookup = iseq.local_variable(node.value.value.to_sym)
1763
+
1764
+ case lookup.local
1765
+ when LocalTable::BlockLocal
1766
+ iseq.getblockparam(lookup.index, lookup.level)
1767
+ when LocalTable::PlainLocal
1768
+ iseq.getlocal(lookup.index, lookup.level)
1769
+ end
1770
+ when IVar
1771
+ name = node.value.value.to_sym
1772
+ iseq.getinstancevariable(name)
1773
+ when Kw
1774
+ case node.value.value
1775
+ when "false"
1776
+ iseq.putobject(false)
1777
+ when "nil"
1778
+ iseq.putnil
1779
+ when "self"
1780
+ iseq.putself
1781
+ when "true"
1782
+ iseq.putobject(true)
1783
+ end
1784
+ end
1785
+ end
1786
+
1787
+ def visit_vcall(node)
1788
+ iseq.putself
1789
+ iseq.send(
1790
+ YARV.calldata(
1791
+ node.value.value.to_sym,
1792
+ 0,
1793
+ CallData::CALL_FCALL | CallData::CALL_VCALL |
1794
+ CallData::CALL_ARGS_SIMPLE
1795
+ )
1796
+ )
1797
+ end
1798
+
1799
+ def visit_when(node)
1800
+ visit(node.statements)
1801
+ end
1802
+
1803
+ def visit_while(node)
1804
+ predicate_label = iseq.label
1805
+ statements_label = iseq.label
1806
+
1807
+ iseq.jump(predicate_label)
1808
+ iseq.putnil
1809
+ iseq.pop
1810
+ iseq.jump(predicate_label)
1811
+
1812
+ iseq.push(statements_label)
1813
+ visit(node.statements)
1814
+ iseq.pop
1815
+
1816
+ iseq.push(predicate_label)
1817
+ visit(node.predicate)
1818
+ iseq.branchif(statements_label)
1819
+ iseq.putnil if last_statement?
1820
+ end
1821
+
1822
+ def visit_word(node)
1823
+ if node.parts.length == 1 && node.parts.first.is_a?(TStringContent)
1824
+ visit(node.parts.first)
1825
+ else
1826
+ length = visit_string_parts(node)
1827
+ iseq.concatstrings(length)
1828
+ end
1829
+ end
1830
+
1831
+ def visit_words(node)
1832
+ if options.frozen_string_literal? &&
1833
+ (compiled = RubyVisitor.compile(node))
1834
+ iseq.duparray(compiled)
1835
+ else
1836
+ visit_all(node.elements)
1837
+ iseq.newarray(node.elements.length)
1838
+ end
1839
+ end
1840
+
1841
+ def visit_xstring_literal(node)
1842
+ iseq.putself
1843
+ length = visit_string_parts(node)
1844
+ iseq.concatstrings(node.parts.length) if length > 1
1845
+ iseq.send(
1846
+ YARV.calldata(
1847
+ :`,
1848
+ 1,
1849
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE
1850
+ )
1851
+ )
1852
+ end
1853
+
1854
+ def visit_yield(node)
1855
+ parts = argument_parts(node.arguments)
1856
+ visit_all(parts)
1857
+ iseq.invokeblock(YARV.calldata(nil, parts.length))
1858
+ end
1859
+
1860
+ def visit_zsuper(_node)
1861
+ iseq.putself
1862
+ iseq.invokesuper(
1863
+ YARV.calldata(
1864
+ nil,
1865
+ 0,
1866
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE |
1867
+ CallData::CALL_SUPER | CallData::CALL_ZSUPER
1868
+ ),
1869
+ nil
1870
+ )
1871
+ end
1872
+
1873
+ private
1874
+
1875
+ # This is a helper that is used in places where arguments may be present
1876
+ # or they may be wrapped in parentheses. It's meant to descend down the
1877
+ # tree and return an array of argument nodes.
1878
+ def argument_parts(node)
1879
+ case node
1880
+ when nil
1881
+ []
1882
+ when Args
1883
+ node.parts
1884
+ when ArgParen
1885
+ if node.arguments.is_a?(ArgsForward)
1886
+ [node.arguments]
1887
+ else
1888
+ node.arguments.parts
1889
+ end
1890
+ when Paren
1891
+ node.contents.parts
1892
+ end
1893
+ end
1894
+
1895
+ # Constant names when they are being assigned or referenced come in as a
1896
+ # tree, but it's more convenient to work with them as an array. This
1897
+ # method converts them into that array. This is nice because it's the
1898
+ # operand that goes to opt_getconstant_path in Ruby 3.2.
1899
+ def constant_names(node)
1900
+ current = node
1901
+ names = []
1902
+
1903
+ while current.is_a?(ConstPathField) || current.is_a?(ConstPathRef)
1904
+ names.unshift(current.constant.value.to_sym)
1905
+ current = current.parent
1906
+ end
1907
+
1908
+ case current
1909
+ when VarField, VarRef
1910
+ names.unshift(current.value.value.to_sym)
1911
+ when TopConstRef
1912
+ names.unshift(current.constant.value.to_sym)
1913
+ names.unshift(:"")
1914
+ end
1915
+
1916
+ names
1917
+ end
1918
+
1919
+ # For the most part when an OpAssign (operator assignment) node with a ||=
1920
+ # operator is being compiled it's a matter of reading the target, checking
1921
+ # if the value should be evaluated, evaluating it if so, and then writing
1922
+ # the result back to the target.
1923
+ #
1924
+ # However, in certain kinds of assignments (X, ::X, X::Y, @@x, and $x) we
1925
+ # first check if the value is defined using the defined instruction. I
1926
+ # don't know why it is necessary, and suspect that it isn't.
1927
+ def opassign_defined(node)
1928
+ value_label = iseq.label
1929
+ skip_value_label = iseq.label
1930
+
1931
+ case node.target
1932
+ when ConstPathField
1933
+ visit(node.target.parent)
1934
+ name = node.target.constant.value.to_sym
1935
+
1936
+ iseq.dup
1937
+ iseq.defined(Defined::TYPE_CONST_FROM, name, true)
1938
+ when TopConstField
1939
+ name = node.target.constant.value.to_sym
1940
+
1941
+ iseq.putobject(Object)
1942
+ iseq.dup
1943
+ iseq.defined(Defined::TYPE_CONST_FROM, name, true)
1944
+ when VarField
1945
+ name = node.target.value.value.to_sym
1946
+ iseq.putnil
1947
+
1948
+ case node.target.value
1949
+ when Const
1950
+ iseq.defined(Defined::TYPE_CONST, name, true)
1951
+ when CVar
1952
+ iseq.defined(Defined::TYPE_CVAR, name, true)
1953
+ when GVar
1954
+ iseq.defined(Defined::TYPE_GVAR, name, true)
1955
+ end
1956
+ end
1957
+
1958
+ iseq.branchunless(value_label)
1959
+
1960
+ case node.target
1961
+ when ConstPathField, TopConstField
1962
+ iseq.dup
1963
+ iseq.putobject(true)
1964
+ iseq.getconstant(name)
1965
+ when VarField
1966
+ case node.target.value
1967
+ when Const
1968
+ iseq.opt_getconstant_path(constant_names(node.target))
1969
+ when CVar
1970
+ iseq.getclassvariable(name)
1971
+ when GVar
1972
+ iseq.getglobal(name)
1973
+ end
1974
+ end
1975
+
1976
+ iseq.dup
1977
+ iseq.branchif(skip_value_label)
1978
+
1979
+ iseq.pop
1980
+ iseq.push(value_label)
1981
+ visit(node.value)
1982
+
1983
+ case node.target
1984
+ when ConstPathField, TopConstField
1985
+ iseq.dupn(2)
1986
+ iseq.swap
1987
+ iseq.setconstant(name)
1988
+ when VarField
1989
+ iseq.dup
1990
+
1991
+ case node.target.value
1992
+ when Const
1993
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE)
1994
+ iseq.setconstant(name)
1995
+ when CVar
1996
+ iseq.setclassvariable(name)
1997
+ when GVar
1998
+ iseq.setglobal(name)
1999
+ end
2000
+ end
2001
+
2002
+ iseq.push(skip_value_label)
2003
+ end
2004
+
2005
+ # Whenever a value is interpolated into a string-like structure, these
2006
+ # three instructions are pushed.
2007
+ def push_interpolate
2008
+ iseq.dup
2009
+ iseq.objtostring(
2010
+ YARV.calldata(
2011
+ :to_s,
2012
+ 0,
2013
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE
2014
+ )
2015
+ )
2016
+ iseq.anytostring
2017
+ end
2018
+
2019
+ # Visit a type of pattern in a pattern match.
2020
+ def visit_pattern(node, end_label)
2021
+ case node
2022
+ when AryPtn
2023
+ length_label = iseq.label
2024
+ match_failure_label = iseq.label
2025
+ match_error_label = iseq.label
2026
+
2027
+ # If there's a constant, then check if we match against that constant
2028
+ # or not first. Branch to failure if we don't.
2029
+ if node.constant
2030
+ iseq.dup
2031
+ visit(node.constant)
2032
+ iseq.checkmatch(CheckMatch::VM_CHECKMATCH_TYPE_CASE)
2033
+ iseq.branchunless(match_failure_label)
2034
+ end
2035
+
2036
+ # First, check if the #deconstruct cache is nil. If it is, we're going
2037
+ # to call #deconstruct on the object and cache the result.
2038
+ iseq.topn(2)
2039
+ deconstruct_label = iseq.label
2040
+ iseq.branchnil(deconstruct_label)
2041
+
2042
+ # Next, ensure that the cached value was cached correctly, otherwise
2043
+ # fail the match.
2044
+ iseq.topn(2)
2045
+ iseq.branchunless(match_failure_label)
2046
+
2047
+ # Since we have a valid cached value, we can skip past the part where
2048
+ # we call #deconstruct on the object.
2049
+ iseq.pop
2050
+ iseq.topn(1)
2051
+ iseq.jump(length_label)
2052
+
2053
+ # Check if the object responds to #deconstruct, fail the match
2054
+ # otherwise.
2055
+ iseq.event(deconstruct_label)
2056
+ iseq.dup
2057
+ iseq.putobject(:deconstruct)
2058
+ iseq.send(YARV.calldata(:respond_to?, 1))
2059
+ iseq.setn(3)
2060
+ iseq.branchunless(match_failure_label)
2061
+
2062
+ # Call #deconstruct and ensure that it's an array, raise an error
2063
+ # otherwise.
2064
+ iseq.send(YARV.calldata(:deconstruct))
2065
+ iseq.setn(2)
2066
+ iseq.dup
2067
+ iseq.checktype(CheckType::TYPE_ARRAY)
2068
+ iseq.branchunless(match_error_label)
2069
+
2070
+ # Ensure that the deconstructed array has the correct size, fail the
2071
+ # match otherwise.
2072
+ iseq.push(length_label)
2073
+ iseq.dup
2074
+ iseq.send(YARV.calldata(:length))
2075
+ iseq.putobject(node.requireds.length)
2076
+ iseq.send(YARV.calldata(:==, 1))
2077
+ iseq.branchunless(match_failure_label)
2078
+
2079
+ # For each required element, check if the deconstructed array contains
2080
+ # the element, otherwise jump out to the top-level match failure.
2081
+ iseq.dup
2082
+ node.requireds.each_with_index do |required, index|
2083
+ iseq.putobject(index)
2084
+ iseq.send(YARV.calldata(:[], 1))
2085
+
2086
+ case required
2087
+ when VarField
2088
+ lookup = visit(required)
2089
+ iseq.setlocal(lookup.index, lookup.level)
2090
+ else
2091
+ visit(required)
2092
+ iseq.checkmatch(CheckMatch::VM_CHECKMATCH_TYPE_CASE)
2093
+ iseq.branchunless(match_failure_label)
2094
+ end
2095
+
2096
+ if index < node.requireds.length - 1
2097
+ iseq.dup
2098
+ else
2099
+ iseq.pop
2100
+ iseq.jump(end_label)
2101
+ end
2102
+ end
2103
+
2104
+ # Set up the routine here to raise an error to indicate that the type
2105
+ # of the deconstructed array was incorrect.
2106
+ iseq.push(match_error_label)
2107
+ iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE)
2108
+ iseq.putobject(TypeError)
2109
+ iseq.putobject("deconstruct must return Array")
2110
+ iseq.send(YARV.calldata(:"core#raise", 2))
2111
+ iseq.pop
2112
+
2113
+ # Patch all of the match failures to jump here so that we pop a final
2114
+ # value before returning to the parent node.
2115
+ iseq.push(match_failure_label)
2116
+ iseq.pop
2117
+ when VarField
2118
+ lookup = visit(node)
2119
+ iseq.setlocal(lookup.index, lookup.level)
2120
+ iseq.jump(end_label)
2121
+ end
2122
+ end
2123
+
2124
+ # There are a lot of nodes in the AST that act as contains of parts of
2125
+ # strings. This includes things like string literals, regular expressions,
2126
+ # heredocs, etc. This method will visit all the parts of a string within
2127
+ # those containers.
2128
+ def visit_string_parts(node)
2129
+ length = 0
2130
+
2131
+ unless node.parts.first.is_a?(TStringContent)
2132
+ iseq.putobject("")
2133
+ length += 1
2134
+ end
2135
+
2136
+ node.parts.each do |part|
2137
+ case part
2138
+ when StringDVar
2139
+ visit(part.variable)
2140
+ push_interpolate
2141
+ when StringEmbExpr
2142
+ visit(part)
2143
+ push_interpolate
2144
+ when TStringContent
2145
+ iseq.putobject(part.accept(RubyVisitor.new))
2146
+ end
2147
+
2148
+ length += 1
2149
+ end
2150
+
2151
+ length
2152
+ end
2153
+
2154
+ # The current instruction sequence that we're compiling is always stored
2155
+ # on the compiler. When we descend into a node that has its own
2156
+ # instruction sequence, this method can be called to temporarily set the
2157
+ # new value of the instruction sequence, yield, and then set it back.
2158
+ def with_child_iseq(child_iseq)
2159
+ parent_iseq = iseq
2160
+
2161
+ begin
2162
+ @iseq = child_iseq
2163
+ yield
2164
+ child_iseq
2165
+ ensure
2166
+ @iseq = parent_iseq
2167
+ end
2168
+ end
2169
+
2170
+ # When we're compiling the last statement of a set of statements within a
2171
+ # scope, the instructions sometimes change from pops to leaves. These
2172
+ # kinds of peephole optimizations can reduce the overall number of
2173
+ # instructions. Therefore, we keep track of whether we're compiling the
2174
+ # last statement of a scope and allow visit methods to query that
2175
+ # information.
2176
+ def with_last_statement
2177
+ previous = @last_statement
2178
+ @last_statement = true
2179
+
2180
+ begin
2181
+ yield
2182
+ ensure
2183
+ @last_statement = previous
2184
+ end
2185
+ end
2186
+
2187
+ def last_statement?
2188
+ @last_statement
2189
+ end
2190
+
2191
+ # OpAssign nodes can have a number of different kinds of nodes as their
2192
+ # "target" (i.e., the left-hand side of the assignment). When compiling
2193
+ # these nodes we typically need to first fetch the current value of the
2194
+ # variable, then perform some kind of action, then store the result back
2195
+ # into the variable. This method handles that by first fetching the value,
2196
+ # then yielding to the block, then storing the result.
2197
+ def with_opassign(node)
2198
+ case node.target
2199
+ when ARefField
2200
+ iseq.putnil
2201
+ visit(node.target.collection)
2202
+ visit(node.target.index)
2203
+
2204
+ iseq.dupn(2)
2205
+ iseq.send(YARV.calldata(:[], 1))
2206
+
2207
+ yield
2208
+
2209
+ iseq.setn(3)
2210
+ iseq.send(YARV.calldata(:[]=, 2))
2211
+ iseq.pop
2212
+ when ConstPathField
2213
+ name = node.target.constant.value.to_sym
2214
+
2215
+ visit(node.target.parent)
2216
+ iseq.dup
2217
+ iseq.putobject(true)
2218
+ iseq.getconstant(name)
2219
+
2220
+ yield
2221
+
2222
+ if node.operator.value == "&&="
2223
+ iseq.dupn(2)
2224
+ else
2225
+ iseq.swap
2226
+ iseq.topn(1)
2227
+ end
2228
+
2229
+ iseq.swap
2230
+ iseq.setconstant(name)
2231
+ when TopConstField
2232
+ name = node.target.constant.value.to_sym
2233
+
2234
+ iseq.putobject(Object)
2235
+ iseq.dup
2236
+ iseq.putobject(true)
2237
+ iseq.getconstant(name)
2238
+
2239
+ yield
2240
+
2241
+ if node.operator.value == "&&="
2242
+ iseq.dupn(2)
2243
+ else
2244
+ iseq.swap
2245
+ iseq.topn(1)
2246
+ end
2247
+
2248
+ iseq.swap
2249
+ iseq.setconstant(name)
2250
+ when VarField
2251
+ case node.target.value
2252
+ when Const
2253
+ names = constant_names(node.target)
2254
+ iseq.opt_getconstant_path(names)
2255
+
2256
+ yield
2257
+
2258
+ iseq.dup
2259
+ iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE)
2260
+ iseq.setconstant(names.last)
2261
+ when CVar
2262
+ name = node.target.value.value.to_sym
2263
+ iseq.getclassvariable(name)
2264
+
2265
+ yield
2266
+
2267
+ iseq.dup
2268
+ iseq.setclassvariable(name)
2269
+ when GVar
2270
+ name = node.target.value.value.to_sym
2271
+ iseq.getglobal(name)
2272
+
2273
+ yield
2274
+
2275
+ iseq.dup
2276
+ iseq.setglobal(name)
2277
+ when Ident
2278
+ local_variable = visit(node.target)
2279
+ iseq.getlocal(local_variable.index, local_variable.level)
2280
+
2281
+ yield
2282
+
2283
+ iseq.dup
2284
+ iseq.setlocal(local_variable.index, local_variable.level)
2285
+ when IVar
2286
+ name = node.target.value.value.to_sym
2287
+ iseq.getinstancevariable(name)
2288
+
2289
+ yield
2290
+
2291
+ iseq.dup
2292
+ iseq.setinstancevariable(name)
2293
+ end
2294
+ end
2295
+ end
2296
+ end
2297
+ end
2298
+ end