Skip to content

Commit

Permalink
Add stack locals, fix current exception logic
Browse files Browse the repository at this point in the history
This adds the ability to have stack locals. These are local variables,
indexed purely by number, that don't follow ruby semantics. They can not
be closed over, and are allocated by adding extra slots at the end of
the operand stack.

The only use of these stack locals is to save and restore the current
exception when entering and exiting a rescue/ensure.
  • Loading branch information
Evan Phoenix committed Dec 27, 2009
1 parent bb8f168 commit a609348
Show file tree
Hide file tree
Showing 18 changed files with 163 additions and 80 deletions.
5 changes: 3 additions & 2 deletions lib/compiler/ast/control_flow.rb
Expand Up @@ -461,8 +461,9 @@ def bytecode(g, force=false)
g.push :nil
end

if g.state.rescue?
g.clear_exception
if lcl = g.state.rescue?
g.push_stack_local lcl
g.pop_exception
end

if g.state.block?
Expand Down
31 changes: 22 additions & 9 deletions lib/compiler/ast/exceptions.rb
Expand Up @@ -34,6 +34,11 @@ def bytecode(g)
# TODO: ?
g.new_label.set!

g.push_exception
current_exc = g.new_stack_local
g.set_stack_local current_exc
g.pop

g.state.push_ensure
@body.bytecode(g)
g.state.pop_ensure
Expand All @@ -42,9 +47,10 @@ def bytecode(g)
g.goto ok

ex.set!

g.push_exception

g.state.push_rescue
g.state.push_rescue(current_exc)
@ensure.bytecode(g)
g.state.pop_rescue
g.pop
Expand Down Expand Up @@ -93,8 +99,11 @@ def bytecode(g)
els = g.new_label
done = g.new_label

# Save the current exception into a local
# Save the current exception into a stack local
g.push_exception
current_exc = g.new_stack_local
g.set_stack_local current_exc
g.pop

g.retry.set!
ex = g.new_label
Expand All @@ -119,16 +128,17 @@ def bytecode(g)
g.pop_unwind

# Reset the outer exception
g.swap
g.push_stack_local current_exc
g.pop_exception

g.goto current_break
end

g.break = current_break
end

ex.set!
@rescue.bytecode(g, reraise, done)
@rescue.bytecode(g, reraise, done, current_exc)
reraise.set!
g.reraise

Expand All @@ -139,7 +149,7 @@ def bytecode(g)
end
done.set!

g.swap
g.push_stack_local current_exc
g.pop_exception
end
g.pop_modifiers
Expand Down Expand Up @@ -197,7 +207,7 @@ def assignment?(node)
return true if value.kind_of? GlobalVariableAccess and value.name == :$!
end

def bytecode(g, reraise, done)
def bytecode(g, reraise, done, current_exc)
pos(g)
body = g.new_label

Expand Down Expand Up @@ -229,18 +239,21 @@ def bytecode(g, reraise, done)
current_break = g.break
g.break = g.new_label

g.state.push_rescue
g.state.push_rescue(current_exc)
@body.bytecode(g)
g.state.pop_rescue

g.clear_exception
g.goto done

if g.break.used?
g.break.set!
g.clear_exception

g.swap
# Reset the outer exception
g.push_stack_local current_exc
g.pop_exception

if current_break
g.goto current_break
else
Expand All @@ -251,7 +264,7 @@ def bytecode(g, reraise, done)
g.break = current_break
if @next
if_false.set!
@next.bytecode(g, reraise, done)
@next.bytecode(g, reraise, done, current_exc)
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/compiler/ast/node.rb
Expand Up @@ -133,22 +133,22 @@ class State

def initialize(scope)
@scope = scope
@rescue = 0
@ensure = 0
@block = 0
@masgn = 0
@rescue = []
end

def push_rescue
@rescue += 1
def push_rescue(val)
@rescue.push(val)
end

def pop_rescue
@rescue -= 1 if rescue?
@rescue.pop if rescue?
end

def rescue?
@rescue > 0
@rescue.last
end

def push_ensure
Expand Down
10 changes: 9 additions & 1 deletion lib/compiler/generator.rb
Expand Up @@ -64,6 +64,8 @@ def initialize

@state = []
@generators = []

@stack_locals = 0
end

attr_reader :ip, :stream, :iseq, :literals
Expand Down Expand Up @@ -147,7 +149,7 @@ def package(klass)
cm.local_count = @local_count
cm.local_names = @local_names.to_tuple if @local_names

cm.stack_size = @stack_size
cm.stack_size = @stack_size + @stack_locals
cm.file = @file
cm.name = @name
cm.primitive = @primitive
Expand All @@ -169,6 +171,12 @@ def set_label_positions

# Helpers

def new_stack_local
idx = @stack_locals
@stack_locals += 1
return idx
end

def add(instruction, arg1=nil, arg2=nil)
@stream << InstructionSet[instruction].bytecode
length = 1
Expand Down
1 change: 1 addition & 0 deletions lib/compiler/printers.rb
Expand Up @@ -31,6 +31,7 @@ def match?(name)
def print_header(cm)
name = cm.name.inspect
size = (SEPARATOR_SIZE - name.size - 2) / 2
size = 1 if size <= 0
puts "\n#{"=" * size} #{name} #{"=" * (size + name.size % 2)}"
print "Arguments: "
print "#{cm.required_args} required, #{cm.total_args} total"
Expand Down
7 changes: 6 additions & 1 deletion spec/compiler/defn_spec.rb
Expand Up @@ -29,7 +29,10 @@ def m
ensure_noexc_lbl = d.new_label

d.setup_unwind ensure_exc_lbl

d.new_label.set!
exc = d.save_exception

d.push_literal :a
d.ensure_return
d.pop_unwind
Expand All @@ -38,7 +41,9 @@ def m
ensure_exc_lbl.set!
d.push_exception
d.push_literal :b
d.clear_exception

d.restore_exception exc

d.ret
d.pop
d.pop_exception
Expand Down
4 changes: 4 additions & 0 deletions spec/compiler/ensure_spec.rb
Expand Up @@ -20,6 +20,8 @@

top.set!

g.save_exception

g.push_modifiers
g.push :nil
g.pop_modifiers
Expand Down Expand Up @@ -129,6 +131,8 @@

top.set!

saved = save_exception()

g.push 14
g.pop
g.push 2
Expand Down
12 changes: 7 additions & 5 deletions spec/compiler/iter_spec.rb
Expand Up @@ -385,8 +385,10 @@
top_lbl = d.new_label
top_lbl.set!

e_saved = d.save_exception
d.push_modifiers
d.push_exception

r_saved = d.save_exception

retry_lbl.set!

Expand Down Expand Up @@ -427,8 +429,8 @@

reraise_lbl.set!
d.clear_exception
d.swap
d.pop_exception

d.restore_exception(r_saved)
d.raise_break

next_lbl.set!
Expand All @@ -437,8 +439,8 @@

else_lbl.set!
last_lbl.set!
d.swap
d.pop_exception

d.restore_exception r_saved
d.pop_modifiers

d.pop_unwind
Expand Down

0 comments on commit a609348

Please sign in to comment.