syntax_tree 5.0.0 → 5.1.0

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