Bug 1259877 - Adjust Promise code to use Call instead of Invoke. r=till
authorJeff Walden <jwalden@mit.edu>
Fri, 25 Mar 2016 22:19:09 -0700
changeset 316927 69d4569f7601c7c4e700f29a31d9d252deed7b44
parent 316926 f5ca1d46b22ace7d9f1b3fe1f25a3ad0fcf5838f
child 316928 498e330e857deb78bb57d11231e0ab2ece7cf3aa
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1259877
milestone48.0a1
Bug 1259877 - Adjust Promise code to use Call instead of Invoke. r=till
js/src/builtin/Promise.cpp
js/src/jsapi.cpp
js/src/vm/Interpreter.h
js/src/vm/SelfHosting.cpp
js/src/vm/SelfHosting.h
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -111,27 +111,30 @@ PromiseObject::create(JSContext* cx, Han
     // (maybe wrapped) Promise constructor was called. They contain checks and
     // can unwrap the Promise if required.
     RootedValue resolvingFunctionsVal(cx);
     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CreateResolvingFunctions,
                                          &resolvingFunctionsVal))
     {
         return nullptr;
     }
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return nullptr;
-    args.setCallee(resolvingFunctionsVal);
-    args.setThis(UndefinedValue());
-    args[0].set(promiseVal);
+
+    RootedArrayObject resolvingFunctions(cx);
+    {
+        FixedInvokeArgs<1> args(cx);
+
+        args[0].set(promiseVal);
 
-    if (!Invoke(cx, args))
-        return nullptr;
+        RootedValue rval(cx);
+        if (!Call(cx, resolvingFunctionsVal, UndefinedHandleValue, args, &rval))
+            return nullptr;
 
-    RootedArrayObject resolvingFunctions(cx, &args.rval().toObject().as<ArrayObject>());
+        resolvingFunctions = &rval.toObject().as<ArrayObject>();
+    }
+
     RootedValue resolveVal(cx, resolvingFunctions->getDenseElement(0));
     MOZ_ASSERT(IsCallable(resolveVal));
     RootedValue rejectVal(cx, resolvingFunctions->getDenseElement(1));
     MOZ_ASSERT(IsCallable(rejectVal));
 
     // Need to wrap the resolution functions before storing them on the Promise.
     if (wrappedProto) {
         AutoCompartment ac(cx, promise);
@@ -145,40 +148,41 @@ PromiseObject::create(JSContext* cx, Han
         promise->setFixedSlot(PROMISE_RESOLVE_FUNCTION_SLOT, wrappedResolveVal);
         promise->setFixedSlot(PROMISE_REJECT_FUNCTION_SLOT, wrappedRejectVal);
     } else {
         promise->setFixedSlot(PROMISE_RESOLVE_FUNCTION_SLOT, resolveVal);
         promise->setFixedSlot(PROMISE_REJECT_FUNCTION_SLOT, rejectVal);
     }
 
     // Step 9.
-    InvokeArgs args2(cx);
-    if (!args2.init(2))
-        return nullptr;
-    args2.setCallee(ObjectValue(*executor));
-    args2.setThis(UndefinedValue());
-    args2[0].set(resolveVal);
-    args2[1].set(rejectVal);
-    bool success = Invoke(cx, args2);
+    bool success;
+    {
+        FixedInvokeArgs<2> args(cx);
+
+        args[0].set(resolveVal);
+        args[1].set(rejectVal);
+
+        RootedValue calleeOrRval(cx, ObjectValue(*executor));
+        success = Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval);
+    }
 
     // Step 10.
     if (!success) {
         RootedValue exceptionVal(cx);
         // Not much we can do about uncatchable exceptions, so just bail
         // for those.
         if (!cx->isExceptionPending() || !GetAndClearException(cx, &exceptionVal))
             return nullptr;
-        InvokeArgs args(cx);
-        if (!args.init(1))
-            return nullptr;
-        args.setCallee(rejectVal);
-        args.setThis(UndefinedValue());
+
+        FixedInvokeArgs<1> args(cx);
+
         args[0].set(exceptionVal);
 
-        if (!Invoke(cx, args))
+        // |rejectVal| is unused after this, so we can safely write to it.
+        if (!Call(cx, rejectVal, UndefinedHandleValue, args, &rejectVal))
             return nullptr;
     }
 
     JS::dbg::onNewPromise(cx, promise);
 
     // Step 11.
     return promise;
 }
@@ -357,41 +361,39 @@ bool
 PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
 {
     if (this->getFixedSlot(PROMISE_STATE_SLOT).toInt32() != unsigned(JS::PromiseState::Pending))
         return true;
 
     RootedValue funVal(cx, this->getReservedSlot(PROMISE_RESOLVE_FUNCTION_SLOT));
     MOZ_ASSERT(funVal.toObject().is<JSFunction>());
 
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return false;
-    args.setCallee(funVal);
-    args.setThis(UndefinedValue());
+    FixedInvokeArgs<1> args(cx);
+
     args[0].set(resolutionValue);
-    return Invoke(cx, args);
+
+    RootedValue dummy(cx);
+    return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
 }
 
 bool
 PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
 {
     if (this->getFixedSlot(PROMISE_STATE_SLOT).toInt32() != unsigned(JS::PromiseState::Pending))
         return true;
 
     RootedValue funVal(cx, this->getReservedSlot(PROMISE_REJECT_FUNCTION_SLOT));
     MOZ_ASSERT(funVal.toObject().is<JSFunction>());
 
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return false;
-    args.setCallee(funVal);
-    args.setThis(UndefinedValue());
+    FixedInvokeArgs<1> args(cx);
+
     args[0].set(rejectionValue);
-    return Invoke(cx, args);
+
+    RootedValue dummy(cx);
+    return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
 }
 
 } // namespace js
 
 static JSObject*
 CreatePromisePrototype(JSContext* cx, JSProtoKey key)
 {
     return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4730,49 +4730,59 @@ JS_PUBLIC_API(JSObject*)
 JS::GetPromiseResolutionSite(JS::HandleObject promise)
 {
     return promise->as<PromiseObject>().resolutionSite();
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue)
 {
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return nullptr;
     RootedObject promiseCtor(cx, GetPromiseConstructor(cx));
     if (!promiseCtor)
         return nullptr;
-    args.setThis(ObjectValue(*promiseCtor));
-    args[0].set(resolutionValue);
-
-    if (!CallSelfHostedFunction(cx, "Promise_static_resolve", args))
-        return nullptr;
-    MOZ_ASSERT(args.rval().isObject());
-    JSObject* obj = &args.rval().toObject();
+
+    JSObject* obj;
+    {
+        FixedInvokeArgs<1> args(cx);
+
+        args[0].set(resolutionValue);
+
+        RootedValue thisvOrRval(cx, ObjectValue(*promiseCtor));
+        if (!CallSelfHostedFunction(cx, "Promise_static_resolve", thisvOrRval, args, &thisvOrRval))
+            return nullptr;
+
+        MOZ_ASSERT(thisvOrRval.isObject());
+        obj = &thisvOrRval.toObject();
+    }
+
     MOZ_ASSERT(obj->is<PromiseObject>());
     return obj;
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue)
 {
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return nullptr;
     RootedObject promiseCtor(cx, GetPromiseConstructor(cx));
     if (!promiseCtor)
         return nullptr;
-    args.setThis(ObjectValue(*promiseCtor));
-    args[0].set(rejectionValue);
-
-    if (!CallSelfHostedFunction(cx, "Promise_static_reject", args))
-        return nullptr;
-    MOZ_ASSERT(args.rval().isObject());
-    JSObject* obj = &args.rval().toObject();
+
+    JSObject* obj;
+    {
+        FixedInvokeArgs<1> args(cx);
+
+        args[0].set(rejectionValue);
+
+        RootedValue thisvOrRval(cx, ObjectValue(*promiseCtor));
+        if (!CallSelfHostedFunction(cx, "Promise_static_reject", thisvOrRval, args, &thisvOrRval))
+            return nullptr;
+
+        MOZ_ASSERT(thisvOrRval.isObject());
+        obj = &thisvOrRval.toObject();
+    }
+
     MOZ_ASSERT(obj->is<PromiseObject>());
     return obj;
 }
 
 JS_PUBLIC_API(bool)
 JS::ResolvePromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue resolutionValue)
 {
     MOZ_ASSERT(promise->is<PromiseObject>());
@@ -4788,47 +4798,54 @@ JS::RejectPromise(JSContext* cx, JS::Han
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseThen(JSContext* cx, JS::HandleObject promise,
                             JS::HandleObject onResolve, JS::HandleObject onReject)
 {
     MOZ_ASSERT(promise->is<PromiseObject>());
     MOZ_ASSERT(onResolve == nullptr || IsCallable(onResolve));
     MOZ_ASSERT(onReject == nullptr || IsCallable(onReject));
-    InvokeArgs args(cx);
-    if (!args.init(2))
-        return nullptr;
-    args.setThis(ObjectValue(*promise));
-    args[0].setObjectOrNull(onResolve);
-    args[1].setObjectOrNull(onReject);
-
-    if (!CallSelfHostedFunction(cx, "Promise_then", args))
-        return nullptr;
-    MOZ_ASSERT(args.rval().isObject());
-    JSObject* obj = &args.rval().toObject();
+
+    JSObject* obj;
+    {
+        FixedInvokeArgs<2> args(cx);
+
+        args[0].setObjectOrNull(onResolve);
+        args[1].setObjectOrNull(onReject);
+
+        RootedValue thisvOrRval(cx, ObjectValue(*promise));
+        if (!CallSelfHostedFunction(cx, "Promise_then", thisvOrRval, args, &thisvOrRval))
+            return nullptr;
+
+        MOZ_ASSERT(thisvOrRval.isObject());
+        obj = &thisvOrRval.toObject();
+    }
+
     MOZ_ASSERT(obj->is<PromiseObject>());
     return obj;
 }
 
 JS_PUBLIC_API(bool)
 JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promise,
                         JS::HandleObject onResolve, JS::HandleObject onReject)
 {
     MOZ_ASSERT(promise->is<PromiseObject>());
     MOZ_ASSERT(IsCallable(onResolve));
     MOZ_ASSERT(IsCallable(onReject));
-    InvokeArgs args(cx);
-    if (!args.init(4))
-        return false;
+
+    FixedInvokeArgs<4> args(cx);
+
     args[0].setObject(*promise);
     args[1].setNull();
     args[2].setObject(*onResolve);
     args[3].setObject(*onReject);
 
-    return js::CallSelfHostedFunction(cx, "EnqueuePromiseReactions", args);
+    RootedValue dummy(cx);
+    return CallSelfHostedFunction(cx, "EnqueuePromiseReactions", UndefinedHandleValue, args,
+                                  &dummy);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
 {
     RootedArrayObject arr(cx, NewDenseFullyAllocatedArray(cx, promises.length()));
     if (!arr)
         return nullptr;
@@ -4838,25 +4855,30 @@ JS::GetWaitForAllPromise(JSContext* cx, 
         JSObject* obj = promises[i];
         if (IsWrapper(obj))
             obj = UncheckedUnwrap(obj);
         MOZ_ASSERT(obj->is<PromiseObject>());
 #endif
         arr->setDenseElement(i, ObjectValue(*promises[i]));
     }
 
-    InvokeArgs args(cx);
-    if (!args.init(1))
-        return nullptr;
-    args[0].setObject(*arr);
-
-    if (!js::CallSelfHostedFunction(cx, "GetWaitForAllPromise", args))
-        return nullptr;
-    MOZ_ASSERT(args.rval().isObject());
-    JSObject* obj = &args.rval().toObject();
+    JSObject* obj;
+    {
+        FixedInvokeArgs<1> args(cx);
+
+        args[0].setObject(*arr);
+
+        RootedValue thisvOrRval(cx, UndefinedValue());
+        if (!CallSelfHostedFunction(cx, "GetWaitForAllPromise", thisvOrRval, args, &thisvOrRval))
+            return nullptr;
+
+        MOZ_ASSERT(thisvOrRval.isObject());
+        obj = &thisvOrRval.toObject();
+    }
+
     MOZ_ASSERT(obj->is<PromiseObject>());
     return obj;
 }
 
 JS_PUBLIC_API(void)
 JS_RequestInterruptCallback(JSRuntime* rt)
 {
     rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -60,23 +60,16 @@ ValueToCallable(JSContext* cx, HandleVal
  *       |args.thisv()|, likely by the interpreter when pushing |this| onto the
  *       stack.  If you're not sure whether |GetThisValue| processing has been
  *       performed, use |Invoke|.
  */
 extern bool
 InternalCallOrConstruct(JSContext* cx, const CallArgs& args,
                         MaybeConstruct construct);
 
-/* A call operation that'll be rewritten later in this patch stack. */
-inline bool
-Invoke(JSContext* cx, const AnyInvokeArgs& args)
-{
-    return InternalCallOrConstruct(cx, args, NO_CONSTRUCT);
-}
-
 /*
  * These helpers take care of the infinite-recursion check necessary for
  * getter/setter calls.
  */
 extern bool
 CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter, MutableHandleValue rval);
 
 extern bool
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1714,34 +1714,36 @@ CallSelfHostedNonGenericMethod(JSContext
 
     for (size_t i = 0; i < args.length() - 1; i++)
         args2[i].set(args[i]);
 
     return js::Call(cx, selfHostedFun, args.thisv(), args2, args.rval());
 }
 
 bool
-js::CallSelfHostedFunction(JSContext* cx, const char* name, InvokeArgs& args)
+js::CallSelfHostedFunction(JSContext* cx, const char* name, HandleValue thisv,
+                           const AnyInvokeArgs& args, MutableHandleValue rval)
 {
     RootedAtom funAtom(cx, Atomize(cx, name, strlen(name)));
     if (!funAtom)
         return false;
     RootedPropertyName funName(cx, funAtom->asPropertyName());
-    return CallSelfHostedFunction(cx, funName, args);
+    return CallSelfHostedFunction(cx, funName, thisv, args, rval);
 }
 
 bool
-js::CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, InvokeArgs& args)
+js::CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, HandleValue thisv,
+                           const AnyInvokeArgs& args, MutableHandleValue rval)
 {
     RootedValue fun(cx);
     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &fun))
         return false;
     MOZ_ASSERT(fun.toObject().is<JSFunction>());
-    args.setCallee(fun);
-    return Invoke(cx, args);
+
+    return Call(cx, fun, thisv, args, rval);
 }
 
 template<typename T>
 bool
 Is(HandleValue v)
 {
     return v.isObject() && v.toObject().is<T>();
 }
@@ -1972,27 +1974,22 @@ intrinsic_RejectUnwrappedPromise(JSConte
             return false;
     }
 
     RootedAtom atom(cx, Atomize(cx, "RejectPromise", strlen("RejectPromise")));
     if (!atom)
         return false;
     RootedPropertyName name(cx, atom->asPropertyName());
 
-    InvokeArgs args2(cx);
-    if (!args2.init(2))
-        return false;
+    FixedInvokeArgs<2> args2(cx);
+
     args2[0].setObject(*promise);
     args2[1].set(reasonVal);
 
-    if (!CallSelfHostedFunction(cx, name, args2))
-        return false;
-
-    args.rval().set(args2.rval());
-    return true;
+    return CallSelfHostedFunction(cx, name, UndefinedHandleValue, args2, args.rval());
 }
 
 static bool
 intrinsic_IsWrappedPromiseObject(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
--- a/js/src/vm/SelfHosting.h
+++ b/js/src/vm/SelfHosting.h
@@ -29,19 +29,21 @@ IsCallSelfHostedNonGenericMethod(NativeI
 bool
 ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args);
 
 /* Get the compile options used when compiling self hosted code. */
 void
 FillSelfHostingCompileOptions(JS::CompileOptions& options);
 
 bool
-CallSelfHostedFunction(JSContext* cx, char const* name, InvokeArgs& args);
+CallSelfHostedFunction(JSContext* cx, char const* name, HandleValue thisv,
+                       const AnyInvokeArgs& args, MutableHandleValue rval);
 
 bool
-CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, InvokeArgs& args);
+CallSelfHostedFunction(JSContext* cx, HandlePropertyName name, HandleValue thisv,
+                       const AnyInvokeArgs& args, MutableHandleValue rval);
 
 bool
 intrinsic_StringSplitString(JSContext* cx, unsigned argc, JS::Value* vp);
 
 } /* namespace js */
 
 #endif /* vm_SelfHosting_h_ */