class Atomy::Method

Attributes

branches[R]
name[R]

Public Class Methods

new(name) click to toggle source
# File lib/atomy/method.rb, line 84
def initialize(name)
  @name = name
  @branches = []
end

Public Instance Methods

add_branch(branch) click to toggle source
# File lib/atomy/method.rb, line 91
def add_branch(branch)
  @branches << branch

  if branch.method?
    @@tick += 1
    branch.name = :"#{@name}:branch:#{@@tick}"
  end

  branch
end
build() click to toggle source
# File lib/atomy/method.rb, line 102
def build
  Atomy::Compiler.package(:__wrapper__) do |gen|
    gen.name = @name

    pre, default, splat, post = argument_form
    gen.total_args = pre + default + post
    gen.required_args = pre + post
    gen.splat_index = splat
    gen.post_args = post

    arg = 0
    pre.times do
      gen.state.scope.new_local(:"arg:#{arg}")
      arg += 1
    end

    default.times do
      gen.state.scope.new_local(:"arg:#{arg}")
      arg += 1
    end

    if gen.splat_index
      gen.state.scope.new_local(:"arg:splat")
    end

    post.times do
      gen.state.scope.new_local(:"arg:#{arg}")
      arg += 1
    end

    done = gen.new_label

    build_branches(gen, done)

    try_super(gen, done) unless @name == :initialize
    raise_mismatch(gen)

    gen.push_nil

    done.set!
  end.tap do |cm|
    cm.scope = Rubinius::LexicalScope.new(Object)
  end
end

Private Instance Methods

argument_form() click to toggle source
# File lib/atomy/method.rb, line 162
def argument_form
  return [0, 0, nil, 0] if @branches.empty?

  raise InconsistentArgumentForms unless uniform_argument_forms?

  [
    @branches.collect(&:pre_arguments_count).first,
    @branches.collect(&:default_arguments_count).max,
    @branches.collect(&:splat_index).first,
    @branches.collect(&:post_arguments_count).first,
  ]
end
build_branches(gen, done) click to toggle source
# File lib/atomy/method.rb, line 175
def build_branches(gen, done)
  @branches.each do |b|
    skip = gen.new_label

    # check for too few arguments
    gen.passed_arg(b.pre_arguments_count - 1)
    gen.goto_if_false(skip)

    # check for too many arguments
    unless b.splat_index
      gen.passed_arg(b.pre_arguments_count + b.default_arguments_count + b.post_arguments_count)
      gen.goto_if_true(skip)
    end

    if b.receiver
      gen.push_self
      b.receiver.inline_matches?(gen)
      gen.goto_if_false(skip)
    end

    arg = 0
    b.arguments.each do |pat|
      gen.push_local(arg)
      pat.inline_matches?(gen)
      gen.goto_if_false(skip)

      arg += 1
    end

    b.default_arguments.each do |pat|
      skip_check = gen.new_label

      gen.push_local(arg)
      gen.goto_if_undefined(skip_check)

      gen.push_local(arg)
      pat.inline_matches?(gen)
      gen.goto_if_false(skip)

      skip_check.set!

      arg += 1
    end

    if b.splat_argument
      gen.push_local(b.splat_index)
      b.splat_argument.inline_matches?(gen)
      gen.goto_if_false(skip)

      arg += 1
    end

    b.post_arguments.each do |pat|
      gen.push_local(arg)
      pat.inline_matches?(gen)
      gen.goto_if_false(skip)

      arg += 1
    end

    if b.proc_argument
      gen.push_proc
      b.proc_argument.inline_matches?(gen)
      gen.goto_if_false(skip)
    end

    branch_args = 0
    method_arg = 0

    if pat = b.receiver
      gen.push_literal(pat)
      gen.push_self
      branch_args += 2
    end

    b.arguments.each do |pat|
      gen.push_literal(pat)
      gen.push_local(method_arg)
      method_arg += 1
      branch_args += 2
    end

    b.default_arguments.each do |pat|
      gen.push_literal(pat)
      gen.push_local(method_arg)
      method_arg += 1
      branch_args += 2
    end

    if pat = b.splat_argument
      gen.push_literal(pat)
      gen.push_local(b.splat_index)
      method_arg += 1
      branch_args += 2
    end

    b.post_arguments.each do |pat|
      gen.push_literal(pat)
      gen.push_local(method_arg)
      method_arg += 1
      branch_args += 2
    end

    if pat = b.proc_argument
      gen.push_literal(pat)
      gen.push_proc
      branch_args += 2
    end

    if b.name
      gen.push_self
      gen.move_down(branch_args)

      gen.send(b.name, branch_args, true)
    else
      gen.push_literal(b.as_method)
      gen.move_down(branch_args)

      gen.push_literal(@name)
      gen.move_down(branch_args)

      gen.push_literal(b.body.lexical_scope.module)
      gen.move_down(branch_args)

      gen.push_self
      gen.move_down(branch_args)

      gen.make_array(branch_args)

      gen.push_nil

      gen.send(:invoke, 5)
    end

    gen.goto(done)

    skip.set!
  end
end
raise_mismatch(gen) click to toggle source
# File lib/atomy/method.rb, line 329
def raise_mismatch(gen)
  gen.push_cpath_top
  gen.find_const(:Atomy)
  gen.find_const(:MessageMismatch)
  gen.push_literal(@name)
  gen.push_self
  gen.send(:new, 2)
  gen.raise_exc
end
try_super(gen, done) click to toggle source
# File lib/atomy/method.rb, line 315
def try_super(gen, done)
  no_super = gen.new_label

  gen.invoke_primitive(:vm_check_super_callable, 0)
  gen.goto_if_false(no_super)

  gen.push_proc
  gen.zsuper(@name)

  gen.goto(done)

  no_super.set!
end
uniform_argument_forms?() click to toggle source
# File lib/atomy/method.rb, line 149
def uniform_argument_forms?
  return true if @branches.empty?

  return false unless @branches.collect(&:pre_arguments_count).uniq.size == 1
  return false unless @branches.collect(&:splat_index).uniq.size == 1
  return false unless @branches.collect(&:post_arguments_count).uniq.size == 1

  # permit varying default argument counts; as long as the rest are the
  # same it's unambiguous

  true
end