syntax_tree 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +51 -0
- data/CHANGELOG.md +24 -1
- data/Gemfile.lock +9 -9
- data/README.md +5 -5
- data/lib/syntax_tree/cli.rb +8 -6
- data/lib/syntax_tree/dsl.rb +1004 -0
- data/lib/syntax_tree/formatter.rb +2 -2
- data/lib/syntax_tree/language_server.rb +2 -0
- data/lib/syntax_tree/node.rb +7 -7
- data/lib/syntax_tree/parser.rb +20 -21
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +459 -0
- data/lib/syntax_tree/yarv/bf.rb +179 -0
- data/lib/syntax_tree/yarv/compiler.rb +2287 -0
- data/lib/syntax_tree/yarv/decompiler.rb +254 -0
- data/lib/syntax_tree/yarv/disassembler.rb +211 -0
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1171 -0
- data/lib/syntax_tree/yarv/instructions.rb +5203 -0
- data/lib/syntax_tree/yarv/legacy.rb +192 -0
- data/lib/syntax_tree/yarv/local_table.rb +89 -0
- data/lib/syntax_tree/yarv.rb +287 -0
- data/lib/syntax_tree.rb +23 -1
- data/syntax_tree.gemspec +1 -1
- metadata +15 -4
@@ -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
|