class Opal::Nodes::CallNode
Constants
- OPERATORS
Operators that get optimized by compiler
- SPECIALS
Attributes
Public Class Methods
# File lib/opal/nodes/call.rb, line 20 def self.add_special(name, options = {}, &handler) SPECIALS[name] = options define_method("handle_#{name}", &handler) end
Opal::Nodes::Base::new
# File lib/opal/nodes/call.rb, line 25 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
Public Instance Methods
# File lib/opal/nodes/call.rb, line 41 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
Private Instance Methods
# File lib/opal/nodes/call.rb, line 237 def auto_await? awaited_set = compiler.async_await awaited_set && awaited_set != true && awaited_set.match?(meth.to_s) end
# File lib/opal/nodes/call.rb, line 465 def call_is_writer_that_needs_handling? (expr? || recv?) && (meth.to_s =~ /^\w+=$/ || meth == :[]=) end
# File lib/opal/nodes/call.rb, line 163 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
# File lib/opal/nodes/call.rb, line 177 def compile_block_pass if iter push ', ', expr(iter) end end
# File lib/opal/nodes/call.rb, line 215 def compile_eval_var push meth.to_s end
Used to generate the code to use this sexp as an ivar var reference
# File lib/opal/nodes/call.rb, line 206 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
# File lib/opal/nodes/call.rb, line 159 def compile_method_name push ", '#{meth}'" end
# File lib/opal/nodes/call.rb, line 155 def compile_receiver push @conditional_recvr || recv(receiver_sexp) end
# File lib/opal/nodes/call.rb, line 183 def compile_refinements refinements = scope.collect_refinements_temps.map { |i| s(:js_tmp, i) } push expr(s(:array, *refinements)), ', ' end
# File lib/opal/nodes/call.rb, line 188 def compile_simple_call_chain compile_receiver push method_jsid, '(', expr(arglist), ')' end
Compiles method call using ‘Opal.refined_send`
@example
a.b(c, &block) Opal.refined_send(a, 'b', [c], block, [[Opal.MyRefinements]])
# File lib/opal/nodes/call.rb, line 143 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
Compiles method call using ‘Opal.send`
@example
a.b(c, &block) Opal.send(a, 'b', [c], block)
# File lib/opal/nodes/call.rb, line 125 def compile_using_send helper :send push '$send(' compile_receiver compile_method_name compile_arguments compile_block_pass push ')' end
Is it a conditional send, ie. ‘foo&.bar`?
# File lib/opal/nodes/call.rb, line 93 def csend? @sexp.type == :csend end
# File lib/opal/nodes/call.rb, line 97 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
Handle safe-operator calls: foo&.bar / foo&.bar ||= baz / …
# File lib/opal/nodes/call.rb, line 470 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
Handle “special” method calls, e.g. require(). Subclasses can override this method. If this method returns nil, then the method will continue to be generated by CallNode
.
# File lib/opal/nodes/call.rb, line 246 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
# File lib/opal/nodes/call.rb, line 482 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
# File lib/opal/nodes/call.rb, line 88 def invoke_using_refinement? !scope.scope.collect_refinements_temps.empty? end
Opal
has a runtime helper ‘Opal.send_method_name’ that assigns provided block to a ‘$$p’ property of the method body and invokes a method using ‘apply’.
We have to compile a method call using this ‘Opal.send_method_name’ when a method:
-
takes a splat
-
takes a block
Arguments that contain splat must be handled in a different way. @see compile_arguments
When a method takes a block we have to calculate all arguments before assigning ‘$$p’ property (that stores a passed block) to a method body. This is some kind of protection from method calls like ‘a(a {}) { 1 }’.
# File lib/opal/nodes/call.rb, line 84 def invoke_using_send? iter || splat? || call_is_writer_that_needs_handling? end
# File lib/opal/nodes/call.rb, line 63 def iter_has_break? return false unless iter iter.meta[:has_break] end
# File lib/opal/nodes/call.rb, line 201 def method_jsid mid_to_jsid meth.to_s end
# File lib/opal/nodes/call.rb, line 442 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
# File lib/opal/nodes/call.rb, line 197 def receiver_sexp recvr || s(:self) end
# File lib/opal/nodes/call.rb, line 233 def sexp_with_arglist @sexp.updated(nil, [recvr, meth, arglist]) end
# File lib/opal/nodes/call.rb, line 193 def splat? arglist.children.any? { |a| a.type == :splat } end
# File lib/opal/nodes/call.rb, line 225 def using_eval? @compiler.eval? && scope.top? && @compiler.scope_variables.include?(meth) end
a variable reference in irb mode in top scope might be a var ref, or it might be a method call
# File lib/opal/nodes/call.rb, line 221 def using_irb? @compiler.irb? && scope.top? && variable_like? end
# File lib/opal/nodes/call.rb, line 355 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
# File lib/opal/nodes/call.rb, line 229 def variable_like? arglist == s(:arglist) && recvr.nil? && iter.nil? end
# File lib/opal/nodes/call.rb, line 453 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