syntax_tree 5.0.1 → 5.2.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,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