class Opal::Nodes::CallNode
def self.add_special(name, options = {}, &handler)
def self.add_special(name, options = {}, &handler) SPECIALS[name] = options define_method("handle_#{name}", &handler) end
def auto_await?
def auto_await? awaited_set = compiler.async_await awaited_set && awaited_set != true && awaited_set.match?(meth.to_s) end
def call_is_writer_that_needs_handling?
def call_is_writer_that_needs_handling? (expr? || recv?) && (meth.to_s =~ /^\w+=$/ || meth == :[]=) end
def compile
def compile # handle some methods specially # some special methods need to skip compilation, so we pass the default as a block handle_special do compiler.record_method_call meth with_wrapper do if using_eval? # if trying to access an lvar in eval or irb mode compile_eval_var elsif using_irb? # if trying to access an lvar in irb mode compile_irb_var else default_compile end end end end
def compile_arguments(skip_comma = false)
def compile_arguments(skip_comma = false) push ', ' unless skip_comma if @with_writer_temp push @with_writer_temp elsif splat? push expr(arglist) elsif arglist.children.empty? push '[]' else push '[', expr(arglist), ']' end end
def compile_block_pass
def compile_block_pass if iter push ', ', expr(iter) end end
def compile_eval_var
def compile_eval_var push meth.to_s end
def compile_irb_var
def compile_irb_var with_temp do |tmp| lvar = meth call = s(:send, s(:self), meth.intern, s(:arglist)) ref = "(typeof #{lvar} !== 'undefined') ? #{lvar} : " push "((#{tmp} = Opal.irb_vars.#{lvar}) == null ? ", ref, expr(call), " : #{tmp})" end end
def compile_method_name
def compile_method_name push ", '#{meth}'" end
def compile_receiver
def compile_receiver push @conditional_recvr || recv(receiver_sexp) end
def compile_refinements
def compile_refinements refinements = scope.collect_refinements_temps.map { |i| s(:js_tmp, i) } push expr(s(:array, *refinements)), ', ' end
def compile_simple_call_chain
def compile_simple_call_chain compile_receiver push method_jsid, '(', expr(arglist), ')' end
def compile_using_refined_send
Opal.refined_send(a, 'b', [c], block, [[Opal.MyRefinements]])
a.b(c, &block)
@example
Compiles method call using `Opal.refined_send`
def compile_using_refined_send helper :refined_send push '$refined_send(' compile_refinements compile_receiver compile_method_name compile_arguments compile_block_pass push ')' end
def compile_using_send
Opal.send(a, 'b', [c], block)
a.b(c, &block)
@example
Compiles method call using `Opal.send`
def compile_using_send helper :send push '$send(' compile_receiver compile_method_name compile_arguments compile_block_pass push ')' end
def csend?
def csend? @sexp.type == :csend end
def default_compile
def default_compile if auto_await? push '(await ' scope.await_encountered = true end push_closure(Closure::SEND) if iter_has_break? if invoke_using_refinement? compile_using_refined_send elsif invoke_using_send? compile_using_send else compile_simple_call_chain end pop_closure if iter_has_break? if auto_await? push ')' end end
def handle_conditional_send
def handle_conditional_send # temporary variable that stores method receiver receiver_temp = scope.new_temp push "#{receiver_temp} = ", expr(receiver_sexp) # execute the sexp only if the receiver isn't nil push ", (#{receiver_temp} === nil || #{receiver_temp} == null) ? nil : " @conditional_recvr = receiver_temp yield wrap '(', ')' end
def handle_special(&compile_default)
this method. If this method returns nil, then the method will continue
Handle "special" method calls, e.g. require(). Subclasses can override
def handle_special(&compile_default) if SPECIALS.include? meth method = method("handle_#{meth}") method.arity == 1 ? method[compile_default] : method[] else yield # i.e. compile_default.call end end
def handle_writer
def handle_writer with_temp do |temp| push "(#{temp} = " compile_arguments(true) push ", " @with_writer_temp = temp yield @with_writer_temp = false push ", " push "#{temp}[#{temp}.length - 1])" end end
def initialize(*)
def initialize(*) super @recvr, @meth, *args = *@sexp *rest, last_arg = *args if last_arg && %i[iter block_pass].include?(last_arg.type) @iter = last_arg args = rest else @iter = nil end @arglist = s(:arglist, *args) end
def invoke_using_refinement?
def invoke_using_refinement? !scope.scope.collect_refinements_temps.empty? end
def invoke_using_send?
- See: #compile_arguments -
def invoke_using_send? iter || splat? || call_is_writer_that_needs_handling? end
def iter_has_break?
def iter_has_break? return false unless iter iter.meta[:has_break] end
def method_jsid
def method_jsid mid_to_jsid meth.to_s end
def push_nesting?
def push_nesting? recv = children.first children.size == 2 && ( # only receiver and method recv.nil? || ( # and no receiver recv.type == :const && # or receiver recv.children.last == :Module # is Module ) ) end
def receiver_sexp
def receiver_sexp recvr || s(:self) end
def sexp_with_arglist
def sexp_with_arglist @sexp.updated(nil, [recvr, meth, arglist]) end
def splat?
def splat? arglist.children.any? { |a| a.type == :splat } end
def using_eval?
def using_eval? @compiler.eval? && scope.top? && @compiler.scope_variables.include?(meth) end
def using_irb?
a variable reference in irb mode in top scope might be a var ref,
def using_irb? @compiler.irb? && scope.top? && variable_like? end
def using_refinement(arg)
def using_refinement(arg) prev, curr = *scope.refinements_temp if prev push "(#{curr} = #{prev}.slice(), #{curr}.push(", expr(arg), "), #{scope.self})" else push "(#{curr} = [", expr(arg), "], #{scope.self})" end end
def variable_like?
def variable_like? arglist == s(:arglist) && recvr.nil? && iter.nil? end
def with_wrapper(&block)
def with_wrapper(&block) if csend? && !@conditional_recvr handle_conditional_send do with_wrapper(&block) end elsif call_is_writer_that_needs_handling? handle_writer(&block) else yield end end