Skip to content

Commit

Permalink
Use compiler nodes to process super calls (and defined super)
Browse files Browse the repository at this point in the history
  • Loading branch information
adambeynon committed Oct 22, 2013
1 parent 6791fa8 commit 05c1a26
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 63 deletions.
1 change: 1 addition & 0 deletions lib/opal/nodes.rb
Expand Up @@ -10,3 +10,4 @@
require 'opal/nodes/yield'
require 'opal/nodes/rescue'
require 'opal/nodes/case'
require 'opal/nodes/super'
3 changes: 1 addition & 2 deletions lib/opal/nodes/logic.rb
Expand Up @@ -232,8 +232,7 @@ def compile_ivar
end

def compile_super
push @parser.process_super(value, @level, :skip_call)
wrap '((', ') != null ? "super" : nil)'
push expr(s(:defined_super, value))
end

def compile_yield
Expand Down
95 changes: 95 additions & 0 deletions lib/opal/nodes/super.rb
@@ -0,0 +1,95 @@
require 'opal/nodes/base'

module Opal
class Parser

# This base class is used just to child the find_super_dispatcher method
# body. This is then used by actual super calls, or a defined?(super) style
# call.
class BaseSuperNode < Node
children :arglist, :iter

def compile_dispatcher
if arglist or iter
iter = expr(iter_sexp)
else
scope.uses_block!
iter = '$iter'
end
if scope.def?
scope.uses_block!
scope_name = scope.identify!
class_name = scope.parent.name || 'self._klass._proto'

if scope.defs
push "$opal.find_super_dispatcher(this, '#{scope.mid.to_s}', #{scope_name}, "
push iter
push ", #{class_name})"
else
push "$opal.find_super_dispatcher(self, '#{scope.mid.to_s}', #{scope_name}, "
push iter
push ")"
end
elsif scope.iter?
chain, cur_defn, mid = scope.get_super_chain
trys = chain.map { |c| "#{c}._def" }.join(' || ')

push "$opal.find_iter_super_dispatcher(self, #{mid}, (#{trys} || #{cur_defn}), null)"
else
raise "Cannot call super() from outside a method block"
end
end

def args
arglist || s(:arglist)
end

def iter_sexp
iter || s(:js_tmp, 'null')
end
end

class DefinedSuperNode < BaseSuperNode
def compile
# insert method body to find super method
self.compile_dispatcher

wrap '((', ') != null ? "super" : nil)'
end
end

class SuperNode < BaseSuperNode
children :arglist, :iter

def compile
if arglist or iter
splat = has_splat?
args = expr(self.args)

unless splat
args = [fragment('['), args, fragment(']')]
end
else
if scope.def?
scope.uses_zuper = true
args = fragment('$zuper')
else
args = fragment('$slice.call(arguments)')
end
end

# compile our call to runtime to get super method
self.compile_dispatcher

push ".apply(self, "
push(*args)
push ")"
end

def has_splat?
args.children.any? { |child| child.type == :splat }
end

end
end
end
65 changes: 4 additions & 61 deletions lib/opal/parser.rb
Expand Up @@ -124,6 +124,10 @@ def add_handler(klass, *types)
add_handler CaseNode, :case
add_handler WhenNode, :when

# super
add_handler SuperNode, :super
add_handler DefinedSuperNode, :defined_super

# Final generated javascript for this parser
attr_reader :result

Expand Down Expand Up @@ -1118,66 +1122,5 @@ def js_falsy(sexp)
result
end
end

# super a, b, c
#
# s(:super, arg1, arg2, ...)
def process_super(sexp, level, skip_call=false)
args, iter = sexp[0], sexp[1]

if (args or iter) and not(skip_call)
if iter
iter = process(iter)
else
iter = f("null")
end

args ||= s(:arglist)

splat = args[1..-1].any? { |s| s.first == :splat }
args = process args

unless splat
args = [f("["), args, f("]")]
end
else
@scope.uses_block!

iter = f("$iter")

if @scope.def?
@scope.uses_zuper = true
args = f("$zuper", sexp)
else
args = f("$slice.call(arguments)", sexp)
end
end

if @scope.type == :def
@scope.uses_block!
scope = @scope.identify!
cls_name = @scope.parent.name || "self._klass._proto"

if @scope.defs
_super = [f("$opal.find_super_dispatcher(this, #{@scope.mid.to_s.inspect}, #{scope}, ", sexp), iter, f(", #{cls_name})", sexp)]
else
_super = [f("$opal.find_super_dispatcher(self, #{@scope.mid.to_s.inspect}, #{scope}, ", sexp), iter, f(")", sexp)]
end

unless skip_call
_super += [f('.apply(this, ', sexp), args, f(')', sexp)]
end
_super

elsif @scope.type == :iter
chain, cur_defn, mid = @scope.get_super_chain
trys = chain.map { |c| "#{c}._def" }.join ' || '
super_method = "$opal.find_iter_super_dispatcher(self, #{mid}, (#{trys} || #{cur_defn}), null)"
skip_call ? [f("(#{super_method})")] :
[f("(#{super_method}).apply(self, ", sexp), args, f(")", sexp)]
else
skip_call ? [f("null")] : raise("Cannot call super() from outside a method block")
end
end
end
end

0 comments on commit 05c1a26

Please sign in to comment.