Bug 1259877 - Add CallFromStack for certain internal users, js::Call for general use, and mark js::Invoke as deprecated. r=efaust
authorJeff Walden <jwalden@mit.edu>
Mon, 21 Mar 2016 15:51:13 -0700
changeset 331013 b538123cb69fccccb70dd6afb50c7829349e2796
parent 331012 b9d51ed412a7ccf1ad49e7fb1bb998720e23a7fa
child 331014 b9cef6b587ac59d69506afda47df0b5b493949ed
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1259877
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1259877 - Add CallFromStack for certain internal users, js::Call for general use, and mark js::Invoke as deprecated. r=efaust
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -496,51 +496,81 @@ js::InternalCallOrConstruct(JSContext* c
     }
 
     bool ok = RunScript(cx, state);
 
     MOZ_ASSERT_IF(ok && construct, args.rval().isObject());
     return ok;
 }
 
-bool
-js::Invoke(JSContext* cx, const Value& thisv, const Value& fval, unsigned argc, const Value* argv,
-           MutableHandleValue rval)
-{
-    InvokeArgs args(cx);
-    if (!args.init(argc))
-        return false;
-
-    args.setCallee(fval);
-    args.setThis(thisv);
-    PodCopy(args.array(), argv, argc);
+static bool
+InternalCall(JSContext* cx, const AnyInvokeArgs& args)
+{
+    MOZ_ASSERT(args.array() + args.length() == args.end(),
+               "must pass calling arguments to a calling attempt");
 
     if (args.thisv().isObject()) {
-        /*
-         * We must call the thisValue hook in case we are not called from the
-         * interpreter, where a prior bytecode has computed an appropriate
-         * |this| already.  But don't do that if fval is a DOM function.
-         */
+        // We must call the thisValue hook in case we are not called from the
+        // interpreter, where a prior bytecode has computed an appropriate
+        // |this| already.  But don't do that if fval is a DOM function.
+        HandleValue fval = args.calleev();
         if (!fval.isObject() || !fval.toObject().is<JSFunction>() ||
             !fval.toObject().as<JSFunction>().isNative() ||
             !fval.toObject().as<JSFunction>().jitInfo() ||
             fval.toObject().as<JSFunction>().jitInfo()->needsOuterizedThisObject())
         {
             JSObject* thisObj = &args.thisv().toObject();
             args.mutableThisv().set(GetThisValue(thisObj));
         }
     }
 
-    if (!InternalCallOrConstruct(cx, args, NO_CONSTRUCT))
+    return InternalCallOrConstruct(cx, args, NO_CONSTRUCT);
+}
+
+static bool
+CallFromStack(JSContext* cx, const CallArgs& args)
+{
+    return InternalCall(cx, static_cast<const AnyInvokeArgs&>(args));
+}
+
+// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.12 Call.
+bool
+js::Call(JSContext* cx, HandleValue fval, HandleValue thisv, const AnyInvokeArgs& args,
+         MutableHandleValue rval)
+{
+    // Explicitly qualify these methods to bypass AnyInvokeArgs's deliberate
+    // shadowing.
+    args.CallArgs::setCallee(fval);
+    args.CallArgs::setThis(thisv);
+
+    if (!InternalCall(cx, args))
         return false;
 
     rval.set(args.rval());
     return true;
 }
 
+// DEPRECATED.  TO BE REMOVED.  DO NOT ADD NEW USES.
+bool
+js::Invoke(JSContext* cx, const Value& thisv, const Value& fval, unsigned argc, const Value* argv,
+           MutableHandleValue rval)
+{
+    RootedValue fv(cx, fval);
+    RootedValue tv(cx, thisv);
+
+    InvokeArgs args(cx);
+    if (!args.init(argc))
+        return false;
+
+    for (unsigned i = 0; i < argc; i++)
+        args[i].set(argv[i]);
+
+    return Call(cx, fv, tv, args, rval);
+}
+
 static bool
 InternalConstruct(JSContext* cx, const AnyConstructArgs& args)
 {
     MOZ_ASSERT(args.array() + args.length() + 1 == args.end(),
                "must pass constructing arguments to a construction attempt");
     MOZ_ASSERT(!JSFunction::class_.getConstruct());
 
     // Callers are responsible for enforcing these preconditions.
@@ -634,26 +664,32 @@ js::InternalConstructWithProvidedThis(JS
 
 bool
 js::CallGetter(JSContext* cx, HandleValue thisv, HandleValue getter, MutableHandleValue rval)
 {
     // Invoke could result in another try to get or set the same id again, see
     // bug 355497.
     JS_CHECK_RECURSION(cx, return false);
 
-    return Invoke(cx, thisv, getter, 0, nullptr, rval);
+    FixedInvokeArgs<0> args(cx);
+
+    return Call(cx, getter, thisv, args, rval);
 }
 
 bool
 js::CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter, HandleValue v)
 {
     JS_CHECK_RECURSION(cx, return false);
 
+    FixedInvokeArgs<1> args(cx);
+
+    args[0].set(v);
+
     RootedValue ignored(cx);
-    return Invoke(cx, thisv, setter, 1, v.address(), &ignored);
+    return Call(cx, setter, thisv, args, &ignored);
 }
 
 bool
 js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg,
                   const Value& newTargetValue, AbstractFramePtr evalInFrame,
                   Value* result)
 {
     MOZ_ASSERT_IF(script->isGlobalCode(),
@@ -2723,17 +2759,17 @@ CASE(JSOP_STRICTEVAL)
     static_assert(JSOP_EVAL_LENGTH == JSOP_STRICTEVAL_LENGTH,
                   "eval and stricteval must be the same size");
 
     CallArgs args = CallArgsFromSp(GET_ARGC(REGS.pc), REGS.sp);
     if (REGS.fp()->scopeChain()->global().valueIsEval(args.calleev())) {
         if (!DirectEval(cx, args.get(0), args.rval()))
             goto error;
     } else {
-        if (!InternalInvoke(cx, args))
+        if (!CallFromStack(cx, args))
             goto error;
     }
 
     REGS.sp = args.spAfterCall();
     TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
 }
 END_CASE(JSOP_EVAL)
 
@@ -2805,17 +2841,17 @@ CASE(JSOP_FUNCALL)
             if (!ConstructFromStack(cx, args))
                 goto error;
         } else {
             if (*REGS.pc == JSOP_CALLITER && args.calleev().isPrimitive()) {
                 MOZ_ASSERT(args.length() == 0, "thisv must be on top of the stack");
                 ReportValueError(cx, JSMSG_NOT_ITERABLE, -1, args.thisv(), nullptr);
                 goto error;
             }
-            if (!InternalInvoke(cx, args))
+            if (!CallFromStack(cx, args))
                 goto error;
         }
         Value* newsp = args.spAfterCall();
         TypeScript::Monitor(cx, script, REGS.pc, newsp[-1]);
         REGS.sp = newsp;
         ADVANCE_AND_DISPATCH(JSOP_CALL_LENGTH);
     }
 
@@ -4611,45 +4647,35 @@ js::SpreadCallOperation(JSContext* cx, H
             return false;
 
         RootedObject obj(cx);
         if (!Construct(cx, callee, cargs, newTarget, &obj))
             return false;
         res.setObject(*obj);
     } else {
         InvokeArgs args(cx);
-
         if (!args.init(length))
             return false;
 
-        args.setCallee(callee);
-        args.setThis(thisv);
-
         if (!GetElements(cx, aobj, length, args.array()))
             return false;
 
-        switch (op) {
-          case JSOP_SPREADCALL:
-            if (!Invoke(cx, args))
+        if ((op == JSOP_SPREADEVAL || op == JSOP_STRICTSPREADEVAL) &&
+            cx->global()->valueIsEval(callee))
+        {
+            if (!DirectEval(cx, args.get(0), res))
                 return false;
-            res.set(args.rval());
-            break;
-          case JSOP_SPREADEVAL:
-          case JSOP_STRICTSPREADEVAL:
-            if (cx->global()->valueIsEval(callee)) {
-                if (!DirectEval(cx, args.get(0), res))
-                    return false;
-            } else {
-                if (!Invoke(cx, args))
-                    return false;
-                res.set(args.rval());
-            }
-            break;
-          default:
-            MOZ_CRASH("bad spread opcode");
+        } else {
+            MOZ_ASSERT(op == JSOP_SPREADCALL ||
+                       op == JSOP_SPREADEVAL ||
+                       op == JSOP_STRICTSPREADEVAL,
+                       "bad spread opcode");
+
+            if (!Call(cx, callee, thisv, args, res))
+                return false;
         }
     }
 
     TypeScript::Monitor(cx, script, pc, res);
     return true;
 }
 
 bool
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -78,38 +78,49 @@ Invoke(JSContext* cx, const AnyInvokeArg
  * patch stack).
  */
 inline bool
 InternalInvoke(JSContext* cx, const CallArgs& args)
 {
     return InternalCallOrConstruct(cx, args, NO_CONSTRUCT);
 }
 
-/*
- * Attempt to call a value with the provided |this| and arguments.  This
- * function places no requirements on the caller: it may be called at any time,
- * and it takes care of copying |fval|, |thisv|, and arguments onto the stack.
- */
+// DEPRECATED.  TO BE REMOVED.  DO NOT ADD NEW USES.
 extern bool
 Invoke(JSContext* cx, const Value& thisv, const Value& fval, unsigned argc, const Value* argv,
        MutableHandleValue rval);
 
 /*
  * 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
 CallSetter(JSContext* cx, HandleValue thisv, HandleValue setter, HandleValue rval);
 
+// ES7 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 7.3.12 Call(F, V, argumentsList).
+// All parameters are required, hopefully forcing callers to be careful not to
+// (say) blindly pass callee as |newTarget| when a different value should have
+// been passed.  Behavior is unspecified if any element of |args| isn't initialized.
+//
+// |rval| is written to *only* after |fval| and |thisv| have been consumed, so
+// |rval| *may* alias either argument.
+extern bool
+Call(JSContext* cx, HandleValue fval, HandleValue thisv, const AnyInvokeArgs& args,
+     MutableHandleValue rval);
+
 // ES6 7.3.13 Construct(F, argumentsList, newTarget).  All parameters are
 // required, hopefully forcing callers to be careful not to (say) blindly pass
 // callee as |newTarget| when a different value should have been passed.
+// Behavior is unspecified if any element of |args| isn't initialized.
+//
+// |rval| is written to *only* after |fval| and |newTarget| have been consumed,
+// so |rval| *may* alias either argument.
 //
 // NOTE: As with the ES6 spec operation, it's the caller's responsibility to
 //       ensure |fval| and |newTarget| are both |IsConstructor|.
 extern bool
 Construct(JSContext* cx, HandleValue fval, const AnyConstructArgs& args, HandleValue newTarget,
           MutableHandleObject objp);
 
 // Check that in the given |args|, which must be |args.isConstructing()|, that
@@ -122,16 +133,19 @@ Construct(JSContext* cx, HandleValue fva
 // on the JS stack, or at least in carefully-rooted memory. The vast majority of
 // potential users should instead use ConstructArgs in concert with Construct().
 extern bool
 ConstructFromStack(JSContext* cx, const CallArgs& args);
 
 // Call Construct(fval, args, newTarget), but use the given |thisv| as |this|
 // during construction of |fval|.
 //
+// |rval| is written to *only* after |fval|, |thisv|, and |newTarget| have been
+// consumed, so |rval| *may* alias any of these arguments.
+//
 // This method exists only for very rare cases where a |this| was created
 // caller-side for construction of |fval|: basically only for JITs using
 // |CreateThis|.  If that's not you, use Construct()!
 extern bool
 InternalConstructWithProvidedThis(JSContext* cx, HandleValue fval, HandleValue thisv,
                                   const AnyConstructArgs& args, HandleValue newTarget,
                                   MutableHandleValue rval);