syntax_tree 5.1.0 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +4 -0
- data/.github/workflows/auto-merge.yml +1 -1
- data/.github/workflows/main.yml +5 -2
- data/.gitmodules +6 -0
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +12 -1
- data/Gemfile.lock +7 -7
- data/Rakefile +7 -0
- data/exe/yarv +63 -0
- data/lib/syntax_tree/node.rb +19 -10
- data/lib/syntax_tree/parser.rb +1 -1
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +7 -7
- data/lib/syntax_tree/yarv/bf.rb +13 -16
- data/lib/syntax_tree/yarv/compiler.rb +24 -13
- data/lib/syntax_tree/yarv/decompiler.rb +9 -0
- data/lib/syntax_tree/yarv/disassembler.rb +3 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +190 -86
- data/lib/syntax_tree/yarv/instructions.rb +211 -42
- data/lib/syntax_tree/yarv/legacy.rb +26 -3
- data/lib/syntax_tree/yarv/vm.rb +624 -0
- data/lib/syntax_tree/yarv.rb +0 -269
- data/lib/syntax_tree.rb +1 -0
- metadata +7 -3
@@ -0,0 +1,624 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module SyntaxTree
|
6
|
+
# This module provides an object representation of the YARV bytecode.
|
7
|
+
module YARV
|
8
|
+
class VM
|
9
|
+
class Jump
|
10
|
+
attr_reader :label
|
11
|
+
|
12
|
+
def initialize(label)
|
13
|
+
@label = label
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Leave
|
18
|
+
attr_reader :value
|
19
|
+
|
20
|
+
def initialize(value)
|
21
|
+
@value = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Frame
|
26
|
+
attr_reader :iseq, :parent, :stack_index, :_self, :nesting, :svars
|
27
|
+
attr_accessor :line, :pc
|
28
|
+
|
29
|
+
def initialize(iseq, parent, stack_index, _self, nesting)
|
30
|
+
@iseq = iseq
|
31
|
+
@parent = parent
|
32
|
+
@stack_index = stack_index
|
33
|
+
@_self = _self
|
34
|
+
@nesting = nesting
|
35
|
+
|
36
|
+
@svars = {}
|
37
|
+
@line = iseq.line
|
38
|
+
@pc = 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class TopFrame < Frame
|
43
|
+
def initialize(iseq)
|
44
|
+
super(iseq, nil, 0, TOPLEVEL_BINDING.eval("self"), [Object])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class BlockFrame < Frame
|
49
|
+
def initialize(iseq, parent, stack_index)
|
50
|
+
super(iseq, parent, stack_index, parent._self, parent.nesting)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class MethodFrame < Frame
|
55
|
+
attr_reader :name, :block
|
56
|
+
|
57
|
+
def initialize(iseq, nesting, parent, stack_index, _self, name, block)
|
58
|
+
super(iseq, parent, stack_index, _self, nesting)
|
59
|
+
@name = name
|
60
|
+
@block = block
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class ClassFrame < Frame
|
65
|
+
def initialize(iseq, parent, stack_index, _self)
|
66
|
+
super(iseq, parent, stack_index, _self, parent.nesting + [_self])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class RescueFrame < Frame
|
71
|
+
def initialize(iseq, parent, stack_index)
|
72
|
+
super(iseq, parent, stack_index, parent._self, parent.nesting)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class ThrownError < StandardError
|
77
|
+
attr_reader :value
|
78
|
+
|
79
|
+
def initialize(value, backtrace)
|
80
|
+
super("This error was thrown by the Ruby VM.")
|
81
|
+
@value = value
|
82
|
+
set_backtrace(backtrace)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class ReturnError < ThrownError
|
87
|
+
end
|
88
|
+
|
89
|
+
class BreakError < ThrownError
|
90
|
+
end
|
91
|
+
|
92
|
+
class NextError < ThrownError
|
93
|
+
end
|
94
|
+
|
95
|
+
class FrozenCore
|
96
|
+
define_method("core#hash_merge_kwd") { |left, right| left.merge(right) }
|
97
|
+
|
98
|
+
define_method("core#hash_merge_ptr") do |hash, *values|
|
99
|
+
hash.merge(values.each_slice(2).to_h)
|
100
|
+
end
|
101
|
+
|
102
|
+
define_method("core#set_method_alias") do |clazz, new_name, old_name|
|
103
|
+
clazz.alias_method(new_name, old_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
define_method("core#set_variable_alias") do |new_name, old_name|
|
107
|
+
# Using eval here since there isn't a reflection API to be able to
|
108
|
+
# alias global variables.
|
109
|
+
eval("alias #{new_name} #{old_name}", binding, __FILE__, __LINE__)
|
110
|
+
end
|
111
|
+
|
112
|
+
define_method("core#set_postexe") { |&block| END { block.call } }
|
113
|
+
|
114
|
+
define_method("core#undef_method") do |clazz, name|
|
115
|
+
clazz.undef_method(name)
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# This is the main entrypoint for events firing in the VM, which allows
|
121
|
+
# us to implement tracing.
|
122
|
+
class NullEvents
|
123
|
+
def publish_frame_change(frame)
|
124
|
+
end
|
125
|
+
|
126
|
+
def publish_instruction(iseq, insn)
|
127
|
+
end
|
128
|
+
|
129
|
+
def publish_stack_change(stack)
|
130
|
+
end
|
131
|
+
|
132
|
+
def publish_tracepoint(event)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# This is a simple implementation of tracing that prints to STDOUT.
|
137
|
+
class STDOUTEvents
|
138
|
+
attr_reader :disassembler
|
139
|
+
|
140
|
+
def initialize
|
141
|
+
@disassembler = Disassembler.new
|
142
|
+
end
|
143
|
+
|
144
|
+
def publish_frame_change(frame)
|
145
|
+
puts "%-16s %s" % ["frame-change", "#{frame.iseq.file}@#{frame.line}"]
|
146
|
+
end
|
147
|
+
|
148
|
+
def publish_instruction(iseq, insn)
|
149
|
+
disassembler.current_iseq = iseq
|
150
|
+
puts "%-16s %s" % ["instruction", insn.disasm(disassembler)]
|
151
|
+
end
|
152
|
+
|
153
|
+
def publish_stack_change(stack)
|
154
|
+
puts "%-16s %s" % ["stack-change", stack.values.inspect]
|
155
|
+
end
|
156
|
+
|
157
|
+
def publish_tracepoint(event)
|
158
|
+
puts "%-16s %s" % ["tracepoint", event.inspect]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# This represents the global VM stack. It effectively is an array, but
|
163
|
+
# wraps mutating functions with instrumentation.
|
164
|
+
class Stack
|
165
|
+
attr_reader :events, :values
|
166
|
+
|
167
|
+
def initialize(events)
|
168
|
+
@events = events
|
169
|
+
@values = []
|
170
|
+
end
|
171
|
+
|
172
|
+
def concat(...)
|
173
|
+
values.concat(...).tap { events.publish_stack_change(self) }
|
174
|
+
end
|
175
|
+
|
176
|
+
def last
|
177
|
+
values.last
|
178
|
+
end
|
179
|
+
|
180
|
+
def length
|
181
|
+
values.length
|
182
|
+
end
|
183
|
+
|
184
|
+
def push(...)
|
185
|
+
values.push(...).tap { events.publish_stack_change(self) }
|
186
|
+
end
|
187
|
+
|
188
|
+
def pop(...)
|
189
|
+
values.pop(...).tap { events.publish_stack_change(self) }
|
190
|
+
end
|
191
|
+
|
192
|
+
def slice!(...)
|
193
|
+
values.slice!(...).tap { events.publish_stack_change(self) }
|
194
|
+
end
|
195
|
+
|
196
|
+
def [](...)
|
197
|
+
values.[](...)
|
198
|
+
end
|
199
|
+
|
200
|
+
def []=(...)
|
201
|
+
values.[]=(...).tap { events.publish_stack_change(self) }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
FROZEN_CORE = FrozenCore.new.freeze
|
206
|
+
|
207
|
+
extend Forwardable
|
208
|
+
|
209
|
+
attr_reader :events
|
210
|
+
|
211
|
+
attr_reader :stack
|
212
|
+
def_delegators :stack, :push, :pop
|
213
|
+
|
214
|
+
attr_reader :frame
|
215
|
+
|
216
|
+
def initialize(events = NullEvents.new)
|
217
|
+
@events = events
|
218
|
+
@stack = Stack.new(events)
|
219
|
+
@frame = nil
|
220
|
+
end
|
221
|
+
|
222
|
+
##########################################################################
|
223
|
+
# Helper methods for frames
|
224
|
+
##########################################################################
|
225
|
+
|
226
|
+
def run_frame(frame)
|
227
|
+
# First, set the current frame to the given value.
|
228
|
+
previous = @frame
|
229
|
+
@frame = frame
|
230
|
+
events.publish_frame_change(@frame)
|
231
|
+
|
232
|
+
# Next, set up the local table for the frame. This is actually incorrect
|
233
|
+
# as it could use the values already on the stack, but for now we're
|
234
|
+
# just doing this for simplicity.
|
235
|
+
stack.concat(Array.new(frame.iseq.local_table.size))
|
236
|
+
|
237
|
+
# Yield so that some frame-specific setup can be done.
|
238
|
+
start_label = yield if block_given?
|
239
|
+
frame.pc = frame.iseq.insns.index(start_label) if start_label
|
240
|
+
|
241
|
+
# Finally we can execute the instructions one at a time. If they return
|
242
|
+
# jumps or leaves we will handle those appropriately.
|
243
|
+
loop do
|
244
|
+
case (insn = frame.iseq.insns[frame.pc])
|
245
|
+
when Integer
|
246
|
+
frame.line = insn
|
247
|
+
frame.pc += 1
|
248
|
+
when Symbol
|
249
|
+
events.publish_tracepoint(insn)
|
250
|
+
frame.pc += 1
|
251
|
+
when InstructionSequence::Label
|
252
|
+
# skip labels
|
253
|
+
frame.pc += 1
|
254
|
+
else
|
255
|
+
begin
|
256
|
+
events.publish_instruction(frame.iseq, insn)
|
257
|
+
result = insn.call(self)
|
258
|
+
rescue ReturnError => error
|
259
|
+
raise if frame.iseq.type != :method
|
260
|
+
|
261
|
+
stack.slice!(frame.stack_index..)
|
262
|
+
@frame = frame.parent
|
263
|
+
events.publish_frame_change(@frame)
|
264
|
+
|
265
|
+
return error.value
|
266
|
+
rescue BreakError => error
|
267
|
+
raise if frame.iseq.type != :block
|
268
|
+
|
269
|
+
catch_entry =
|
270
|
+
find_catch_entry(frame, InstructionSequence::CatchBreak)
|
271
|
+
raise unless catch_entry
|
272
|
+
|
273
|
+
stack.slice!(
|
274
|
+
(
|
275
|
+
frame.stack_index + frame.iseq.local_table.size +
|
276
|
+
catch_entry.restore_sp
|
277
|
+
)..
|
278
|
+
)
|
279
|
+
@frame = frame
|
280
|
+
events.publish_frame_change(@frame)
|
281
|
+
|
282
|
+
frame.pc = frame.iseq.insns.index(catch_entry.exit_label)
|
283
|
+
push(result = error.value)
|
284
|
+
rescue NextError => error
|
285
|
+
raise if frame.iseq.type != :block
|
286
|
+
|
287
|
+
catch_entry =
|
288
|
+
find_catch_entry(frame, InstructionSequence::CatchNext)
|
289
|
+
raise unless catch_entry
|
290
|
+
|
291
|
+
stack.slice!(
|
292
|
+
(
|
293
|
+
frame.stack_index + frame.iseq.local_table.size +
|
294
|
+
catch_entry.restore_sp
|
295
|
+
)..
|
296
|
+
)
|
297
|
+
@frame = frame
|
298
|
+
events.publish_frame_change(@frame)
|
299
|
+
|
300
|
+
frame.pc = frame.iseq.insns.index(catch_entry.exit_label)
|
301
|
+
push(result = error.value)
|
302
|
+
rescue Exception => error
|
303
|
+
catch_entry =
|
304
|
+
find_catch_entry(frame, InstructionSequence::CatchRescue)
|
305
|
+
raise unless catch_entry
|
306
|
+
|
307
|
+
stack.slice!(
|
308
|
+
(
|
309
|
+
frame.stack_index + frame.iseq.local_table.size +
|
310
|
+
catch_entry.restore_sp
|
311
|
+
)..
|
312
|
+
)
|
313
|
+
@frame = frame
|
314
|
+
events.publish_frame_change(@frame)
|
315
|
+
|
316
|
+
frame.pc = frame.iseq.insns.index(catch_entry.exit_label)
|
317
|
+
push(result = run_rescue_frame(catch_entry.iseq, frame, error))
|
318
|
+
end
|
319
|
+
|
320
|
+
case result
|
321
|
+
when Jump
|
322
|
+
frame.pc = frame.iseq.insns.index(result.label) + 1
|
323
|
+
when Leave
|
324
|
+
# this shouldn't be necessary, but is because we're not handling
|
325
|
+
# the stack correctly at the moment
|
326
|
+
stack.slice!(frame.stack_index..)
|
327
|
+
|
328
|
+
# restore the previous frame
|
329
|
+
@frame = previous || frame.parent
|
330
|
+
events.publish_frame_change(@frame) if @frame
|
331
|
+
|
332
|
+
return result.value
|
333
|
+
else
|
334
|
+
frame.pc += 1
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def find_catch_entry(frame, type)
|
341
|
+
iseq = frame.iseq
|
342
|
+
iseq.catch_table.find do |catch_entry|
|
343
|
+
next unless catch_entry.is_a?(type)
|
344
|
+
|
345
|
+
begin_pc = iseq.insns.index(catch_entry.begin_label)
|
346
|
+
end_pc = iseq.insns.index(catch_entry.end_label)
|
347
|
+
|
348
|
+
(begin_pc...end_pc).cover?(frame.pc)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def run_top_frame(iseq)
|
353
|
+
run_frame(TopFrame.new(iseq))
|
354
|
+
end
|
355
|
+
|
356
|
+
def run_block_frame(iseq, frame, *args, **kwargs, &block)
|
357
|
+
run_frame(BlockFrame.new(iseq, frame, stack.length)) do
|
358
|
+
setup_arguments(iseq, args, kwargs, block)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def run_class_frame(iseq, clazz)
|
363
|
+
run_frame(ClassFrame.new(iseq, frame, stack.length, clazz))
|
364
|
+
end
|
365
|
+
|
366
|
+
def run_method_frame(name, nesting, iseq, _self, *args, **kwargs, &block)
|
367
|
+
run_frame(
|
368
|
+
MethodFrame.new(
|
369
|
+
iseq,
|
370
|
+
nesting,
|
371
|
+
frame,
|
372
|
+
stack.length,
|
373
|
+
_self,
|
374
|
+
name,
|
375
|
+
block
|
376
|
+
)
|
377
|
+
) { setup_arguments(iseq, args, kwargs, block) }
|
378
|
+
end
|
379
|
+
|
380
|
+
def run_rescue_frame(iseq, frame, error)
|
381
|
+
run_frame(RescueFrame.new(iseq, frame, stack.length)) do
|
382
|
+
local_set(0, 0, error)
|
383
|
+
nil
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def setup_arguments(iseq, args, kwargs, block)
|
388
|
+
locals = [*args]
|
389
|
+
local_index = 0
|
390
|
+
start_label = nil
|
391
|
+
|
392
|
+
# First, set up all of the leading arguments. These are positional and
|
393
|
+
# required arguments at the start of the argument list.
|
394
|
+
if (lead_num = iseq.argument_options[:lead_num])
|
395
|
+
lead_num.times do
|
396
|
+
local_set(local_index, 0, locals.shift)
|
397
|
+
local_index += 1
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
# Next, set up all of the optional arguments. The opt array contains
|
402
|
+
# the labels that the frame should start at if the optional is
|
403
|
+
# present. The last element of the array is the label that the frame
|
404
|
+
# should start at if all of the optional arguments are present.
|
405
|
+
if (opt = iseq.argument_options[:opt])
|
406
|
+
opt[0...-1].each do |label|
|
407
|
+
if locals.empty?
|
408
|
+
start_label = label
|
409
|
+
break
|
410
|
+
else
|
411
|
+
local_set(local_index, 0, locals.shift)
|
412
|
+
local_index += 1
|
413
|
+
end
|
414
|
+
|
415
|
+
start_label = opt.last if start_label.nil?
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# If there is a splat argument, then we'll set that up here. It will
|
420
|
+
# grab up all of the remaining positional arguments.
|
421
|
+
if (rest_start = iseq.argument_options[:rest_start])
|
422
|
+
if (post_start = iseq.argument_options[:post_start])
|
423
|
+
length = post_start - rest_start
|
424
|
+
local_set(local_index, 0, locals[0...length])
|
425
|
+
locals = locals[length..]
|
426
|
+
else
|
427
|
+
local_set(local_index, 0, locals.dup)
|
428
|
+
locals.clear
|
429
|
+
end
|
430
|
+
local_index += 1
|
431
|
+
end
|
432
|
+
|
433
|
+
# Next, set up any post arguments. These are positional arguments that
|
434
|
+
# come after the splat argument.
|
435
|
+
if (post_num = iseq.argument_options[:post_num])
|
436
|
+
post_num.times do
|
437
|
+
local_set(local_index, 0, locals.shift)
|
438
|
+
local_index += 1
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
if (keyword_option = iseq.argument_options[:keyword])
|
443
|
+
# First, set up the keyword bits array.
|
444
|
+
keyword_bits =
|
445
|
+
keyword_option.map do |config|
|
446
|
+
kwargs.key?(config.is_a?(Array) ? config[0] : config)
|
447
|
+
end
|
448
|
+
|
449
|
+
iseq.local_table.locals.each_with_index do |local, index|
|
450
|
+
# If this is the keyword bits local, then set it appropriately.
|
451
|
+
if local.name.is_a?(Integer)
|
452
|
+
local_set(index, 0, keyword_bits)
|
453
|
+
next
|
454
|
+
end
|
455
|
+
|
456
|
+
# First, find the configuration for this local in the keywords
|
457
|
+
# list if it exists.
|
458
|
+
name = local.name
|
459
|
+
config =
|
460
|
+
keyword_option.find do |keyword|
|
461
|
+
keyword.is_a?(Array) ? keyword[0] == name : keyword == name
|
462
|
+
end
|
463
|
+
|
464
|
+
# If the configuration doesn't exist, then the local is not a
|
465
|
+
# keyword local.
|
466
|
+
next unless config
|
467
|
+
|
468
|
+
if !config.is_a?(Array)
|
469
|
+
# required keyword
|
470
|
+
local_set(index, 0, kwargs.fetch(name))
|
471
|
+
elsif !config[1].nil?
|
472
|
+
# optional keyword with embedded default value
|
473
|
+
local_set(index, 0, kwargs.fetch(name, config[1]))
|
474
|
+
else
|
475
|
+
# optional keyword with expression default value
|
476
|
+
local_set(index, 0, kwargs[name])
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
local_set(local_index, 0, block) if iseq.argument_options[:block_start]
|
482
|
+
|
483
|
+
start_label
|
484
|
+
end
|
485
|
+
|
486
|
+
##########################################################################
|
487
|
+
# Helper methods for instructions
|
488
|
+
##########################################################################
|
489
|
+
|
490
|
+
def const_base
|
491
|
+
frame.nesting.last
|
492
|
+
end
|
493
|
+
|
494
|
+
def frame_at(level)
|
495
|
+
current = frame
|
496
|
+
level.times { current = current.parent }
|
497
|
+
current
|
498
|
+
end
|
499
|
+
|
500
|
+
def frame_svar
|
501
|
+
current = frame
|
502
|
+
current = current.parent while current.is_a?(BlockFrame)
|
503
|
+
current
|
504
|
+
end
|
505
|
+
|
506
|
+
def frame_yield
|
507
|
+
current = frame
|
508
|
+
current = current.parent until current.is_a?(MethodFrame)
|
509
|
+
current
|
510
|
+
end
|
511
|
+
|
512
|
+
def frozen_core
|
513
|
+
FROZEN_CORE
|
514
|
+
end
|
515
|
+
|
516
|
+
def jump(label)
|
517
|
+
Jump.new(label)
|
518
|
+
end
|
519
|
+
|
520
|
+
def leave
|
521
|
+
Leave.new(pop)
|
522
|
+
end
|
523
|
+
|
524
|
+
def local_get(index, level)
|
525
|
+
stack[frame_at(level).stack_index + index]
|
526
|
+
end
|
527
|
+
|
528
|
+
def local_set(index, level, value)
|
529
|
+
stack[frame_at(level).stack_index + index] = value
|
530
|
+
end
|
531
|
+
|
532
|
+
##########################################################################
|
533
|
+
# Methods for overriding runtime behavior
|
534
|
+
##########################################################################
|
535
|
+
|
536
|
+
DLEXT = ".#{RbConfig::CONFIG["DLEXT"]}"
|
537
|
+
SOEXT = ".#{RbConfig::CONFIG["SOEXT"]}"
|
538
|
+
|
539
|
+
def require_resolved(filepath)
|
540
|
+
$LOADED_FEATURES << filepath
|
541
|
+
iseq = RubyVM::InstructionSequence.compile_file(filepath)
|
542
|
+
run_top_frame(InstructionSequence.from(iseq.to_a))
|
543
|
+
end
|
544
|
+
|
545
|
+
def require_internal(filepath, loading: false)
|
546
|
+
case (extname = File.extname(filepath))
|
547
|
+
when ""
|
548
|
+
# search for all the extensions
|
549
|
+
searching = filepath
|
550
|
+
extensions = ["", ".rb", DLEXT, SOEXT]
|
551
|
+
when ".rb", DLEXT, SOEXT
|
552
|
+
# search only for the given extension name
|
553
|
+
searching = File.basename(filepath, extname)
|
554
|
+
extensions = [extname]
|
555
|
+
else
|
556
|
+
# we don't handle these extensions, raise a load error
|
557
|
+
raise LoadError, "cannot load such file -- #{filepath}"
|
558
|
+
end
|
559
|
+
|
560
|
+
if filepath.start_with?("/")
|
561
|
+
# absolute path, search only in the given directory
|
562
|
+
directories = [File.dirname(searching)]
|
563
|
+
searching = File.basename(searching)
|
564
|
+
else
|
565
|
+
# relative path, search in the load path
|
566
|
+
directories = $LOAD_PATH
|
567
|
+
end
|
568
|
+
|
569
|
+
directories.each do |directory|
|
570
|
+
extensions.each do |extension|
|
571
|
+
absolute_path = File.join(directory, "#{searching}#{extension}")
|
572
|
+
next unless File.exist?(absolute_path)
|
573
|
+
|
574
|
+
if !loading && $LOADED_FEATURES.include?(absolute_path)
|
575
|
+
return false
|
576
|
+
elsif extension == ".rb"
|
577
|
+
require_resolved(absolute_path)
|
578
|
+
return true
|
579
|
+
elsif loading
|
580
|
+
return Kernel.send(:yarv_load, filepath)
|
581
|
+
else
|
582
|
+
return Kernel.send(:yarv_require, filepath)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
if loading
|
588
|
+
Kernel.send(:yarv_load, filepath)
|
589
|
+
else
|
590
|
+
Kernel.send(:yarv_require, filepath)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
def require(filepath)
|
595
|
+
require_internal(filepath, loading: false)
|
596
|
+
end
|
597
|
+
|
598
|
+
def require_relative(filepath)
|
599
|
+
Kernel.yarv_require_relative(filepath)
|
600
|
+
end
|
601
|
+
|
602
|
+
def load(filepath)
|
603
|
+
require_internal(filepath, loading: true)
|
604
|
+
end
|
605
|
+
|
606
|
+
def eval(
|
607
|
+
source,
|
608
|
+
binding = TOPLEVEL_BINDING,
|
609
|
+
filename = "(eval)",
|
610
|
+
lineno = 1
|
611
|
+
)
|
612
|
+
Kernel.yarv_eval(source, binding, filename, lineno)
|
613
|
+
end
|
614
|
+
|
615
|
+
def throw(tag, value = nil)
|
616
|
+
Kernel.throw(tag, value)
|
617
|
+
end
|
618
|
+
|
619
|
+
def catch(tag, &block)
|
620
|
+
Kernel.catch(tag, &block)
|
621
|
+
end
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|