syntax_tree 5.0.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,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