Skip to content

Commit

Permalink
re-implemented jumpreturn via exception (we have to pop from MRI st…
Browse files Browse the repository at this point in the history
…ack)
  • Loading branch information
iliabylich committed Dec 4, 2019
1 parent 73e2562 commit 6efb7b1
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 35 deletions.
11 changes: 2 additions & 9 deletions executor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ def _do_throw(throw_obj)
if throw_obj.is_a?(Exception)
raise throw_obj
else
# raise VM::LocalJumpError.new(throw_obj)
binding.irb
end
end

Expand All @@ -851,14 +851,7 @@ def execute_throw((throw_state))
case state
when 1
# return
frame = current_frame

until frame.can_return?
frame.exit!(:__unused)
frame = frame.parent_frame
end

frame.exit!(throw_obj)
raise VM::ReturnError, throw_obj
when 3
# next inside rescue/ensure, inside pop_frame,
# so current_frame is about to die
Expand Down
6 changes: 3 additions & 3 deletions spec/vm_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,9 @@ def test
it 'supports longjmp via return' do
assert_evaluates_like_mri(<<-RUBY)
def m
[1].each do |x|
[2].each do |y|
[3].each do |z|
[1, 10].each do |x|
[2, 20].each do |y|
[3, 30].each do |z|
return [x,y,z]
end
end
Expand Down
57 changes: 34 additions & 23 deletions vm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,27 @@ class VM
attr_accessor :debug_print_stack
attr_accessor :debug_print_rest_on_error

class LocalJumpError < ::LocalJumpError
class InternalError < ::Exception
def initialize(*)
super
set_backtrace(VM.instance.backtrace)
end
end

class MessageError < InternalError
attr_reader :value

def initialize(value)
@value = value
end
attr_reader :value
end

class InternalError < ::RuntimeError
def initialize(*)
super
set_backtrace(VM.instance.backtrace)
def message
"#{self.class}(#{@value.inspect})"
end
end

class ReturnError < MessageError; end

def initialize
@frame_stack = FrameStack.new
# @frame_stack.singleton_class.prepend(Module.new {
Expand Down Expand Up @@ -54,12 +61,27 @@ def execute(iseq, **payload)
if (before_eval = payload[:before_eval]); before_eval.call; end

__log { "\n\n--------- BEGIN #{current_frame.header} ---------" }
evaluate_last_frame

begin
evaluate_last_frame
rescue ReturnError => e
raise unless current_frame.can_return?

frame = current_frame

until frame.can_return?
frame.exit!(:__unused)
frame = frame.parent_frame
end

frame.exit!(e.value)
end

__log { "\n\n--------- END #{current_frame.header} ---------" }
ensure
result = pop_frame

if $! && $!.is_a?(InternalError)
if $!
raise
end

Expand Down Expand Up @@ -188,15 +210,7 @@ def evaluate_last_frame

current_insn = current_iseq.shift_insn

begin
execute_insn(current_insn)
rescue
if debug_print_rest_on_error
$debug.puts "--------------\nRest (for #{current_frame.pretty_name} in #{current_frame.file}):"
current_iseq.insns.each { |insn| p insn }
end
raise
end
execute_insn(current_insn)
end
end

Expand Down Expand Up @@ -272,12 +286,9 @@ def execute_array_insn(insn)

def with_error_handling
yield
rescue InternalError => e
raise
rescue Exception => e
if e.is_a?(InternalError)
# our error, just re-raise ie
raise
end

# error from the inerpreted code
clear_current_iseq
current_frame.current_error = e
Expand Down
1 change: 1 addition & 0 deletions vm/frames/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def has_returning?

def exit!(value)
VM.instance.__log { "... scheduling force [:leave] (on #{self.name} with #{value.inspect})" }
@returning = value

iseq.insns.clear
iseq.insns.push([:leave])
Expand Down
4 changes: 4 additions & 0 deletions vm/stack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def pop
@stack.pop
end

def empty?
@stack.empty?
end

def top
@stack.last
end
Expand Down

0 comments on commit 6efb7b1

Please sign in to comment.