class CodeTools::AST::OpAssignElement

Attributes

arguments[RW]
op[RW]
receiver[RW]
value[RW]

Public Class Methods

new(line, receiver, arguments, op, value) click to toggle source
# File lib/rubinius/code/ast/operators.rb, line 107
def initialize(line, receiver, arguments, op, value)
  @line = line
  @receiver = receiver
  @op = op
  arguments = nil if arguments.is_a?(EmptyArray)
  @arguments = Arguments.new line, arguments
  @value = value
end

Public Instance Methods

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

  # X: Snippet used for explanation: h[:a] += 3
  # X: given h = { :a => 2 }
  # X: Pull h onto the stack
  @receiver.bytecode(g)
  # X: Pull :a in
  @arguments.bytecode(g)
  recv_stack = @arguments.stack_size + 1

  # Dup the receiver and arguments to use later
  g.dup_many recv_stack

  #
  # X: Call [](:a) on h
  #
  # @arguments.size will be 1

  if @arguments.splat?
    g.push_tagged_nil 0
    g.send_with_splat :[], @arguments.size
  else
    g.send :[], @arguments.size
  end

  # X: 2 is now on the top of the stack (TOS)

  # A special case, where we use the value as boolean
  if @op == :or or @op == :and
    fnd = g.new_label
    fin = g.new_label
    assign = g.new_label

    # We dup the value from [] to leave it as the value of the
    # expression

    g.dup
    if @op == :or
      g.goto_if_true fnd
    else
      g.goto_if_false fnd
    end

    # Ok, take the extra copy off and pull the value onto the stack
    g.pop

    # The receiver and arguments are still on the stack

    old_break = g.break
    new_break = g.new_label
    g.break = new_break

    @value.bytecode(g)

    g.goto assign

    new_break.set!
    if old_break
      g.pop_many recv_stack + 1
      g.push_tagged_nil 0
      g.goto old_break
    end

    g.break = old_break

    assign.set!

    # retain the rhs as the expression value
    g.dup
    g.move_down recv_stack + 1

    if @arguments.splat?
      g.send :push, 1
      g.push_tagged_nil 0
      g.send_with_splat :[]=, @arguments.size
    else
      g.send :[]=, @arguments.size + 1
    end
    g.pop

    # Leaves the value we moved down the stack on the top
    g.goto fin

    fnd.set!

    # Clean up the stack but retain return value from :[]
    g.move_down recv_stack
    g.pop_many recv_stack

    fin.set!
  else
    assign = g.new_label

    old_break = g.break
    new_break = g.new_label
    g.break = new_break

    # @op is something like + or -
    # We pull in @value to the stack
    @value.bytecode(g)
    # X: 3 TOS

    g.goto assign

    new_break.set!
    if old_break
      g.pop_many recv_stack + 2
      g.push_tagged_nil 0
      g.goto old_break
    end

    g.break = old_break

    assign.set!

    # ... then call it as an argument to @or, called on the return
    # from [].
    # X: 2 + 3

    g.send @op, 1
    # X: 5 TOS

    # The new value is on the stack now. It is the last argument to the call
    # to []= because your dupd versions of recv and arguments are still on the stack.

    # retain the rhs as the expression value
    g.dup
    g.move_down recv_stack + 1

    # X: Call []=(:a, 5) on h
    if @arguments.splat?
      g.send :push, 1
      g.push_tagged_nil 0
      g.send_with_splat :[]=, @arguments.size
    else
      g.send :[]=, @arguments.size + 1
    end
    g.pop
  end
end
to_sexp() click to toggle source
# File lib/rubinius/code/ast/operators.rb, line 258
def to_sexp
  arguments = [:arglist]

  case @arguments
  when PushArguments
    arguments << @arguments.to_sexp
  else
    arguments += @arguments.to_sexp
  end

  case @op
  when :or
    op = :"||"
  when :and
    op = :"&&"
  else
    op = @op
  end
  [:op_asgn1, @receiver.to_sexp, arguments, op, @value.to_sexp]
end