Bug 1338920 - Support JSOP_SPREADCALL in Ion
MozReview-Commit-ID: 1WOhrGAedLi
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2008,16 +2008,19 @@ IonBuilder::inspectOpcode(JSOp op)
return jsop_initelem_getter_setter();
case JSOP_FUNCALL:
return jsop_funcall(GET_ARGC(pc));
case JSOP_FUNAPPLY:
return jsop_funapply(GET_ARGC(pc));
+ case JSOP_SPREADCALL:
+ return jsop_spreadcall();
+
case JSOP_CALL:
case JSOP_CALL_IGNORES_RV:
case JSOP_CALLITER:
case JSOP_NEW:
case JSOP_SUPERCALL:
MOZ_TRY(jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL,
(JSOp)*pc == JSOP_CALL_IGNORES_RV));
if (op == JSOP_CALLITER) {
@@ -5047,16 +5050,55 @@ IonBuilder::jsop_funapply(uint32_t argc)
return abort(AbortReason::Disable, "fun.apply speculation failed");
}
// Use funapply that definitely uses |arguments|
return jsop_funapplyarguments(argc);
}
AbortReasonOr<Ok>
+IonBuilder::jsop_spreadcall()
+{
+ // The arguments array is constructed by a JSOP_SPREADCALLARRAY and not
+ // leaked to user. The complications of spread call iterator behaviour are
+ // handled when the user objects are expanded and copied into this hidden
+ // array.
+
+#ifdef DEBUG
+ // If we know class, ensure it is what we expected
+ MDefinition* argument = current->peek(-1);
+ if (TemporaryTypeSet* objTypes = argument->resultTypeSet())
+ if (const Class* clasp = objTypes->getKnownClass(constraints()))
+ MOZ_ASSERT(clasp == &ArrayObject::class_);
+#endif
+
+ MDefinition* argArr = current->pop();
+ MDefinition* argThis = current->pop();
+ MDefinition* argFunc = current->pop();
+
+ // Extract call target.
+ TemporaryTypeSet* funTypes = argFunc->resultTypeSet();
+ JSFunction* target = getSingleCallTarget(funTypes);
+ WrappedFunction* wrappedTarget = target ? new(alloc()) WrappedFunction(target) : nullptr;
+
+ // Dense elements of argument array
+ MElements* elements = MElements::New(alloc(), argArr);
+ current->add(elements);
+
+ MApplyArray* apply = MApplyArray::New(alloc(), wrappedTarget, argFunc, elements, argThis);
+ current->add(apply);
+ current->push(apply);
+ MOZ_TRY(resumeAfter(apply));
+
+ // TypeBarrier the call result
+ TemporaryTypeSet* types = bytecodeTypes(pc);
+ return pushTypeBarrier(apply, types, BarrierKind::TypeSet);
+}
+
+AbortReasonOr<Ok>
IonBuilder::jsop_funapplyarray(uint32_t argc)
{
MOZ_ASSERT(argc == 2);
int funcDepth = -((int)argc + 1);
// Extract call target.
TemporaryTypeSet* funTypes = current->peek(funcDepth)->resultTypeSet();
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -499,16 +499,17 @@ class IonBuilder
AbortReasonOr<Ok> jsop_notearg();
AbortReasonOr<Ok> jsop_throwsetconst();
AbortReasonOr<Ok> jsop_checklexical();
AbortReasonOr<Ok> jsop_checkaliasedlexical(EnvironmentCoordinate ec);
AbortReasonOr<Ok> jsop_funcall(uint32_t argc);
AbortReasonOr<Ok> jsop_funapply(uint32_t argc);
AbortReasonOr<Ok> jsop_funapplyarguments(uint32_t argc);
AbortReasonOr<Ok> jsop_funapplyarray(uint32_t argc);
+ AbortReasonOr<Ok> jsop_spreadcall();
AbortReasonOr<Ok> jsop_call(uint32_t argc, bool constructing, bool ignoresReturnValue);
AbortReasonOr<Ok> jsop_eval(uint32_t argc);
AbortReasonOr<Ok> jsop_label();
AbortReasonOr<Ok> jsop_andor(JSOp op);
AbortReasonOr<Ok> jsop_dup2();
AbortReasonOr<Ok> jsop_loophead(jsbytecode* pc);
AbortReasonOr<Ok> jsop_compare(JSOp op);
AbortReasonOr<Ok> jsop_compare(JSOp op, MDefinition* left, MDefinition* right);