class CodeTools::AST::Rescue

Attributes

body[RW]
else[RW]
rescue[RW]

Public Class Methods

new(line, body, rescue_body, else_body) click to toggle source
# File lib/rubinius/code/ast/exceptions.rb, line 181
def initialize(line, body, rescue_body, else_body)
  @line = line
  @body = body
  @rescue = rescue_body
  @else = else_body
end

Public Instance Methods

bytecode(g) click to toggle source
# File lib/rubinius/code/ast/exceptions.rb, line 188
def bytecode(g)
  pos(g)

  g.push_modifiers
  if @body.nil?
    if @else.nil?
      # Stupid. No body and no else.
      g.push_tagged_nil 0
    else
      # Only an else, run it.
      @else.bytecode(g)
    end
  else
    outer_retry = g.retry

    this_retry = g.new_label
    reraise = g.new_label
    els     = g.new_label
    done    = g.new_label

    # Save the current exception into a stack local
    g.push_exception_state
    outer_exc_state = g.new_stack_local
    g.set_stack_local outer_exc_state
    g.pop

    this_retry.set!
    ex = g.new_label
    g.setup_unwind ex, RescueType

    # TODO: ?
    g.new_label.set!

    if current_break = g.break
      # Make a break available to use, which we'll use to
      # lazily generate a cleanup area
      g.break = g.new_label
    end

    # Setup a lazy cleanup area for next'ing out of the handler
    current_next = g.next
    g.next = g.new_label

    # Use a lazy label to patch up prematuraly leaving a begin
    # body via retry.
    if outer_retry
      g.retry = g.new_label
    end

    # Also handle redo unwinding through the rescue
    if current_redo = g.redo
      g.redo = g.new_label
    end

    @body.bytecode(g)
    g.pop_unwind
    g.goto els

    if current_break
      if g.break.used?
        g.break.set!
        g.pop_unwind

        # Reset the outer exception
        g.push_stack_local outer_exc_state
        g.restore_exception_state

        g.goto current_break
      end

      g.break = current_break
    end

    if g.next.used?
      g.next.set!
      g.pop_unwind

      # Reset the outer exception
      g.push_stack_local outer_exc_state
      g.restore_exception_state

      if current_next
        g.goto current_next
      else
        g.ret
      end
    end

    g.next = current_next

    if current_redo
      if g.redo.used?
        g.redo.set!
        g.pop_unwind

        # Reset the outer exception
        g.push_stack_local outer_exc_state
        g.restore_exception_state

        g.goto current_redo
      end

      g.redo = current_redo
    end

    if outer_retry
      if g.retry.used?
        g.retry.set!
        g.pop_unwind

        # Reset the outer exception
        g.push_stack_local outer_exc_state
        g.restore_exception_state

        g.goto outer_retry
      end

      g.retry = outer_retry
    end

    g.set_line 0

    # We jump here if an exception has occured in the body
    ex.set!

    # Expose the retry label here only, not before this.
    g.retry = this_retry

    # Save exception state to use in reraise
    g.push_exception_state

    raised_exc_state = g.new_stack_local
    g.set_stack_local raised_exc_state
    g.pop

    # Save the current exception, so that calling #=== can't trample
    # it.
    g.push_current_exception

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

    # Restore the exception state we saved and the reraise. The act
    # of checking if an exception matches can run any code, which
    # can easily trample on the current exception.
    #
    # Remove the direct exception so we can get to the state
    g.pop

    # Restore the state and reraise
    g.push_stack_local raised_exc_state
    g.restore_exception_state
    g.reraise

    els.set!
    if @else
      g.pop
      @else.bytecode(g)
    end

    g.set_line 0
    done.set!

    g.push_stack_local outer_exc_state
    g.restore_exception_state
  end
  g.pop_modifiers
end
defined(g) click to toggle source
# File lib/rubinius/code/ast/exceptions.rb, line 357
def defined(g)
  @body.defined(g) if @body
  g.push_literal "nil"
end
to_sexp() click to toggle source
# File lib/rubinius/code/ast/exceptions.rb, line 362
def to_sexp
  sexp = [:rescue, @body.to_sexp, @rescue.to_sexp]
  sexp << @else.to_sexp if @else
  sexp
end