Bug 670071 - make Invoke + InvokeArgsGuard less error prone (r=waldo)
authorLuke Wagner <luke@mozilla.com>
Fri, 05 Aug 2011 08:22:53 -0700
changeset 75520 73aafae10571356aa17af47e922f1af6118de217
parent 75519 796b8466d549dc5db8df06f33210f8a1f02aaaf2
child 75521 46b3d16fc427f98b441e734997dd6c649bb9f03c
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerswaldo
bugs670071
milestone9.0a1
Bug 670071 - make Invoke + InvokeArgsGuard less error prone (r=waldo)
js/src/jit-test/tests/basic/bug666448.js
js/src/jit-test/tests/basic/testStackIter.js
js/src/jsapi.cpp
js/src/jscntxtinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsproxy.cpp
js/src/jsreflect.cpp
js/src/jsscopeinlines.h
js/src/jsstr.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/vm/Debugger.cpp
js/src/vm/Stack.h
--- a/js/src/jit-test/tests/basic/testStackIter.js
+++ b/js/src/jit-test/tests/basic/testStackIter.js
@@ -123,16 +123,22 @@ var obj = { valueOf:(function valueOf() 
 var obj = { valueOf:(function valueOf() {
     assertStackIs([valueOf, "bound(valueOf)", "bound(valueOf)", "bound(valueOf)",
                    Math.sin, Array.prototype.sort, "global-code"]);
 }).bind().bind().bind() };
 [obj, obj].sort(Math.sin);
 
 /***********/
 
+var proxy = Proxy.createFunction({}, function f() { assertStackIs([f, "global-code"]) });
+proxy();
+new proxy();
+
+/***********/
+
 for (var i = 0; i < 10; ++i) {
     /* No loss for scripts. */
     (function f() {
         assertStackIs([f, Function.prototype.apply, 'global-code']);
     }).apply(null, {});
     (function f() {
         assertStackIs([f, Function.prototype.call, 'global-code']);
     }).call(null);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4885,17 +4885,17 @@ JS_DecompileFunctionBody(JSContext *cx, 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteScript(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, scriptObj);
 
-    JSBool ok = ExternalExecute(cx, scriptObj->getScript(), *obj, Valueify(rval));
+    JSBool ok = Execute(cx, scriptObj->getScript(), *obj, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ExecuteScriptVersion(JSContext *cx, JSObject *obj, JSObject *scriptObj, jsval *rval,
                         JSVersion version)
 {
@@ -4919,17 +4919,17 @@ EvaluateUCScriptForPrincipalsCommon(JSCo
                                                : TCF_COMPILE_N_GO,
                                                chars, length, filename, lineno, compileVersion);
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return false;
     }
     JS_ASSERT(script->getVersion() == compileVersion);
 
-    bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
+    bool ok = Execute(cx, script, *obj, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     js_DestroyScript(cx, script, 5);
     return ok;
 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
@@ -4999,18 +4999,18 @@ JS_EvaluateScript(JSContext *cx, JSObjec
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv,
                 jsval *rval)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fun, JSValueArray(argv, argc));
-    JSBool ok = ExternalInvoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), argc,
-                               Valueify(argv), Valueify(rval));
+    JSBool ok = Invoke(cx, ObjectOrNullValue(obj), ObjectValue(*fun), argc, Valueify(argv),
+                       Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, jsval *argv,
                     jsval *rval)
 {
@@ -5018,62 +5018,61 @@ JS_CallFunctionName(JSContext *cx, JSObj
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, JSValueArray(argv, argc));
 
     AutoValueRooter tvr(cx);
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     JSBool ok =
         atom &&
         js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, tvr.addr()) &&
-        ExternalInvoke(cx, ObjectOrNullValue(obj), tvr.value(), argc, Valueify(argv),
-                       Valueify(rval));
+        Invoke(cx, ObjectOrNullValue(obj), tvr.value(), argc, Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv,
                      jsval *rval)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, fval, JSValueArray(argv, argc));
-    JSBool ok = ExternalInvoke(cx, ObjectOrNullValue(obj), Valueify(fval), argc, Valueify(argv),
-                               Valueify(rval));
+    JSBool ok = Invoke(cx, ObjectOrNullValue(obj), Valueify(fval), argc, Valueify(argv),
+                       Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 namespace JS {
 
 JS_PUBLIC_API(bool)
 Call(JSContext *cx, jsval thisv, jsval fval, uintN argc, jsval *argv, jsval *rval)
 {
     JSBool ok;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, thisv, fval, JSValueArray(argv, argc));
-    ok = ExternalInvoke(cx, Valueify(thisv), Valueify(fval), argc, Valueify(argv), Valueify(rval));
+    ok = Invoke(cx, Valueify(thisv), Valueify(fval), argc, Valueify(argv), Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
     return ok;
 }
 
 } // namespace JS
 
 JS_PUBLIC_API(JSObject *)
 JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, ctor, JSValueArray(argv, argc));
 
     // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW
     // is not a simple variation of JSOP_CALL. We have to determine what class
     // of object to create, create it, and clamp the return value to an object,
-    // among other details. js_InvokeConstructor does the hard work.
+    // among other details. InvokeConstructor does the hard work.
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return NULL;
 
     args.calleev().setObject(*ctor);
     args.thisv().setNull();
     memcpy(args.argv(), argv, argc * sizeof(jsval));
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -344,17 +344,17 @@ CallJSPropertyOpSetter(JSContext *cx, js
     return op(cx, obj, id, strict, vp);
 }
 
 inline bool
 CallSetter(JSContext *cx, JSObject *obj, jsid id, js::StrictPropertyOp op, uintN attrs,
            uintN shortid, JSBool strict, js::Value *vp)
 {
     if (attrs & JSPROP_SETTER)
-        return ExternalGetOrSet(cx, obj, id, CastAsObjectJsval(op), JSACC_WRITE, 1, vp, vp);
+        return InvokeGetterOrSetter(cx, obj, CastAsObjectJsval(op), 1, vp, vp);
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     if (attrs & JSPROP_SHORTID)
         id = INT_TO_JSID(shortid);
     return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp);
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -507,17 +507,17 @@ Class js_NoSuchMethodClass = {
     no_such_method_trace  /* trace */
 };
 
 /*
  * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
  * the base object, we search for the __noSuchMethod__ method in the base.
  * If it exists, we store the method and the property's id into an object of
  * NoSuchMethod class and store this object into the callee's stack slot.
- * Later, js_Invoke will recognise such an object and transfer control to
+ * Later, Invoke will recognise such an object and transfer control to
  * NoSuchMethod that invokes the method like:
  *
  *   this.__noSuchMethod__(id, args)
  *
  * where id is the name of the method that this invocation attempted to
  * call by name, and args is an Array containing this invocation's actual
  * parameters.
  */
@@ -616,17 +616,17 @@ RunScript(JSContext *cx, JSScript *scrip
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
 JS_REQUIRES_STACK bool
-Invoke(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
+InvokeKernel(JSContext *cx, const CallArgs &argsRef, MaybeConstruct construct)
 {
     /* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
 
     CallArgs args = argsRef;
     JS_ASSERT(args.argc() <= StackSpace::ARGS_LENGTH_MAX);
 
     if (args.calleev().isPrimitive()) {
         js_ReportIsNotFunction(cx, &args.calleev(), ToReportFlags(construct));
@@ -776,18 +776,18 @@ InvokeSessionGuard::start(JSContext *cx,
     if (ifg_.pushed())
         ifg_.pop();
     formals_ = actuals_ = args_.argv();
     nformals_ = (unsigned)-1;
     return true;
 }
 
 bool
-ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
-               uintN argc, Value *argv, Value *rval)
+Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv,
+       Value *rval)
 {
     LeaveTrace(cx);
 
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return false;
 
     args.calleev() = fval;
@@ -809,18 +809,17 @@ ExternalInvoke(JSContext *cx, const Valu
     if (!Invoke(cx, args))
         return false;
 
     *rval = args.rval();
     return true;
 }
 
 bool
-ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
-                          Value *rval)
+InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval)
 {
     LeaveTrace(cx);
 
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc, &args))
         return false;
 
     args.calleev() = fval;
@@ -830,28 +829,28 @@ ExternalInvokeConstructor(JSContext *cx,
     if (!InvokeConstructor(cx, args))
         return false;
 
     *rval = args.rval();
     return true;
 }
 
 bool
-ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
-                 JSAccessMode mode, uintN argc, Value *argv, Value *rval)
+InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv,
+                     Value *rval)
 {
     LeaveTrace(cx);
 
     /*
-     * ExternalInvoke could result in another try to get or set the same id
-     * again, see bug 355497.
+     * Invoke could result in another try to get or set the same id again, see
+     * bug 355497.
      */
     JS_CHECK_RECURSION(cx, return false);
 
-    return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
+    return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
 }
 
 #if JS_HAS_SHARP_VARS
 JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
 
 static JS_NEVER_INLINE bool
 InitSharpSlots(JSContext *cx, StackFrame *fp)
 {
@@ -873,18 +872,18 @@ InitSharpSlots(JSContext *cx, StackFrame
         sharps[0].setUndefined();
         sharps[1].setUndefined();
     }
     return true;
 }
 #endif
 
 bool
-Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
-        ExecuteType type, StackFrame *evalInFrame, Value *result)
+ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
+              ExecuteType type, StackFrame *evalInFrame, Value *result)
 {
     JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
@@ -914,17 +913,17 @@ Execute(JSContext *cx, JSScript *script,
         *result = fp->returnValue();
 
     Probes::stopExecution(cx, script);
 
     return !!ok;
 }
 
 bool
-ExternalExecute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval)
+Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval)
 {
     /* The scope chain could be anything, so innerize just in case. */
     JSObject *scopeChain = &scopeChainArg;
     OBJ_TO_INNER_OBJECT(cx, scopeChain);
     if (!scopeChain)
         return false;
 
     /* If we were handed a non-native object, complain bitterly. */
@@ -939,18 +938,18 @@ ExternalExecute(JSContext *cx, JSScript 
         scopeChain->makeVarObj();
 
     /* Use the scope chain as 'this', modulo outerization. */
     JSObject *thisObj = scopeChain->thisObject(cx);
     if (!thisObj)
         return false;
     Value thisv = ObjectValue(*thisObj);
 
-    return Execute(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
-                   NULL /* evalInFrame */, rval);
+    return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
+                         NULL /* evalInFrame */, rval);
 }
 
 bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs)
 {
     JSObject *obj2;
     JSProperty *prop;
     uintN oldAttrs;
@@ -1185,17 +1184,17 @@ TypeOfValue(JSContext *cx, const Value &
         return JSTYPE_VOID;
     if (v.isObject())
         return v.toObject().typeOf(cx);
     JS_ASSERT(v.isBoolean());
     return JSTYPE_BOOLEAN;
 }
 
 JS_REQUIRES_STACK bool
-InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
+InvokeConstructorKernel(JSContext *cx, const CallArgs &argsRef)
 {
     JS_ASSERT(!js_FunctionClass.construct);
     CallArgs args = argsRef;
 
     if (args.calleev().isObject()) {
         JSObject *callee = &args.callee();
         Class *clasp = callee->getClass();
         if (clasp == &js_FunctionClass) {
@@ -1207,17 +1206,17 @@ InvokeConstructor(JSContext *cx, const C
             bool ok = CallJSNativeConstructor(cx, fun->u.n.native, args);
             Probes::calloutEnd(cx, fun);
             return ok;
             }
 
             if (!fun->isInterpretedConstructor())
                 goto error;
 
-            if (!Invoke(cx, args, CONSTRUCT))
+            if (!InvokeKernel(cx, args, CONSTRUCT))
                 return false;
 
             JS_ASSERT(args.rval().isObject());
             return true;
         }
         if (clasp->construct) {
             args.thisv().setMagicWithObjectOrNullPayload(NULL);
             return CallJSNativeConstructor(cx, clasp->construct, args);
@@ -3981,17 +3980,17 @@ END_CASE(JSOP_ENUMELEM)
 
 BEGIN_CASE(JSOP_EVAL)
 {
     CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
     if (IsBuiltinEvalForScope(&regs.fp()->scopeChain(), args.calleev())) {
         if (!DirectEval(cx, args))
             goto error;
     } else {
-        if (!Invoke(cx, args))
+        if (!InvokeKernel(cx, args))
             goto error;
     }
     CHECK_INTERRUPT_HANDLER();
     regs.sp = args.spAfterCall();
 }
 END_CASE(JSOP_EVAL)
 
 BEGIN_CASE(JSOP_NEW)
@@ -4005,20 +4004,20 @@ BEGIN_CASE(JSOP_FUNAPPLY)
     MaybeConstruct construct = *regs.pc == JSOP_NEW ? CONSTRUCT : NO_CONSTRUCT;
 
     JSObject *callee;
     JSFunction *fun;
 
     /* Don't bother trying to fast-path calls to scripted non-constructors. */
     if (!IsFunctionObject(args.calleev(), &callee, &fun) || !fun->isInterpretedConstructor()) {
         if (construct) {
-            if (!InvokeConstructor(cx, args))
+            if (!InvokeConstructorKernel(cx, args))
                 goto error;
         } else {
-            if (!Invoke(cx, args))
+            if (!InvokeKernel(cx, args))
                 goto error;
         }
         regs.sp = args.spAfterCall();
         CHECK_INTERRUPT_HANDLER();
         TRACE_0(NativeCallComplete);
         len = JSOP_CALL_LENGTH;
         DO_NEXT_OP(len);
     }
@@ -6087,17 +6086,17 @@ END_CASE(JSOP_ARRAYPUSH)
 
   exit:
     interpReturnOK = ScriptEpilogueOrGeneratorYield(cx, regs.fp(), interpReturnOK);
     regs.fp()->setFinishedInInterpreter();
 
     /*
      * At this point we are inevitably leaving an interpreted function or a
      * top-level script, and returning to one of:
-     * (a) an "out of line" call made through js_Invoke;
+     * (a) an "out of line" call made through Invoke;
      * (b) a js_Execute activation;
      * (c) a generator (SendToGenerator, jsiter.c).
      *
      * We must not be in an inline frame. The check above ensures that for the
      * error case and for a normal return, the code jumps directly to parent's
      * frame pc.
      */
     JS_ASSERT(entryFrame == regs.fp());
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -140,43 +140,54 @@ BoxNonStrictThis(JSContext *cx, const Ca
  * call represented by |fp|. ComputeThis is necessary because fp->thisValue()
  * may be set to 'undefined' when 'this' should really be the global object (as
  * an optimization to avoid global-this computation).
  */
 inline bool
 ComputeThis(JSContext *cx, StackFrame *fp);
 
 /*
- * The js::InvokeArgumentsGuard passed to js_Invoke must come from an
- * immediately-enclosing successful call to js::StackSpace::pushInvokeArgs,
- * i.e., there must have been no un-popped pushes to cx->stack. Furthermore,
- * |args.getvp()[0]| should be the callee, |args.getvp()[1]| should be |this|,
- * and the range [args.getvp() + 2, args.getvp() + 2 + args.getArgc()) should
- * be initialized actual arguments.
+ * InvokeKernel assumes that the given args have been pushed on the top of the
+ * VM stack. Additionally, if 'args' is contained in a CallArgsList, that they
+ * have already been marked 'active'.
  */
 extern bool
-Invoke(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
+InvokeKernel(JSContext *cx, const CallArgs &args, MaybeConstruct construct = NO_CONSTRUCT);
 
 /*
- * For calls to natives, the InvokeArgsGuard object provides a record of the
- * call for the debugger's callstack. For this to work, the InvokeArgsGuard
- * record needs to know when the call is actually active (because the
- * InvokeArgsGuard can be pushed long before and popped long after the actual
- * call, during which time many stack-observing things can happen).
+ * Invoke assumes that 'args' has been pushed (via ContextStack::pushInvokeArgs)
+ * and is currently at the top of the VM stack.
  */
 inline bool
 Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT)
 {
     args.setActive();
-    bool ok = Invoke(cx, ImplicitCast<CallArgs>(args), construct);
+    bool ok = InvokeKernel(cx, args, construct);
     args.setInactive();
     return ok;
 }
 
 /*
+ * This Invoke overload places the least requirements on the caller: it may be
+ * called at any time and it takes care of copying the given callee, this, and
+ * arguments onto the stack.
+ */
+extern bool
+Invoke(JSContext *cx, const Value &thisv, const Value &fval, uintN argc, Value *argv,
+       Value *rval);
+
+/*
+ * This helper takes care of the infinite-recursion check necessary for
+ * getter/setter calls.
+ */
+extern bool
+InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, uintN argc, Value *argv,
+                     Value *rval);
+
+/*
  * Natives like sort/forEach/replace call Invoke repeatedly with the same
  * callee, this, and number of arguments. To optimize this, such natives can
  * start an "invoke session" to factor out much of the dynamic setup logic
  * required by a normal Invoke. Usage is:
  *
  *   InvokeSessionGuard session(cx);
  *   if (!session.start(cx, callee, thisp, argc, &session))
  *     ...
@@ -193,59 +204,57 @@ Invoke(JSContext *cx, InvokeArgsGuard &a
  *     ... = session.rval();
  *   }
  *
  *   // session ended by ~InvokeSessionGuard
  */
 class InvokeSessionGuard;
 
 /*
- * "External" calls may come from C or C++ code using a JSContext on which no
- * JS is running (!cx->fp), so they may need to push a dummy StackFrame.
+ * InvokeConstructor* implement a function call from a constructor context
+ * (e.g. 'new') handling the the creation of the new 'this' object.
  */
+extern JS_REQUIRES_STACK bool
+InvokeConstructorKernel(JSContext *cx, const CallArgs &args);
 
+/* See the InvokeArgsGuard overload of Invoke. */
+inline bool
+InvokeConstructor(JSContext *cx, InvokeArgsGuard &args)
+{
+    args.setActive();
+    bool ok = InvokeConstructorKernel(cx, ImplicitCast<CallArgs>(args));
+    args.setInactive();
+    return ok;
+}
+
+/* See the fval overload of Invoke. */
 extern bool
-ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
-               uintN argc, Value *argv, Value *rval);
-
-extern bool
-ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
-                 JSAccessMode mode, uintN argc, Value *argv, Value *rval);
+InvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv, Value *rval);
 
 /*
- * These two functions invoke a function called from a constructor context
- * (e.g. 'new'). InvokeConstructor handles the general case where a new object
- * needs to be created for/by the constructor. ConstructWithGivenThis directly
- * calls the constructor with the given 'this', hence the caller must
- * understand the semantics of the constructor call.
+ * InvokeConstructorWithGivenThis directly calls the constructor with the given
+ * 'this'; the caller must choose the semantically correct 'this'.
  */
-
-extern JS_REQUIRES_STACK bool
-InvokeConstructor(JSContext *cx, const CallArgs &args);
-
 extern JS_REQUIRES_STACK bool
 InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fval,
                                uintN argc, Value *argv, Value *rval);
 
-extern bool
-ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *argv,
-                          Value *rval);
-
-extern bool
-ExternalExecute(JSContext *cx, JSScript *script, JSObject &scopeChain, Value *rval);
-
 /*
  * Executes a script with the given scopeChain/this. The 'type' indicates
  * whether this is eval code or global code. To support debugging, the
  * evalFrame parameter can point to an arbitrary frame in the context's call
  * stack to simulate executing an eval in that frame.
  */
 extern bool
-Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
-        ExecuteType type, StackFrame *evalInFrame, Value *result);
+ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv,
+              ExecuteType type, StackFrame *evalInFrame, Value *result);
+
+/* Execute a script with the given scopeChain as global code. */
+extern bool
+Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, Value *rval);
 
 /* Flags to toggle js::Interpret() execution. */
 enum InterpMode
 {
     JSINTERP_NORMAL    = 0, /* interpreter is running normally */
     JSINTERP_RECORD    = 1, /* interpreter has been started to record/run traces */
     JSINTERP_SAFEPOINT = 2, /* interpreter should leave on a method JIT safe point */
     JSINTERP_PROFILE   = 3  /* interpreter should profile a loop */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -357,17 +357,17 @@ GetCustomIterator(JSContext *cx, JSObjec
     }
 
     if (!cx->runningWithTrustedPrincipals())
         ++sCustomIteratorCount;
 
     /* Otherwise call it and return that object. */
     LeaveTrace(cx);
     Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
-    if (!ExternalInvoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
+    if (!Invoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
         return false;
     if (vp->isPrimitive()) {
         /*
          * We are always coming from js_ValueToIterator, and we are no longer on
          * trace, so the object we are iterating over is on top of the stack (-1).
          */
         JSAutoByteString bytes;
         if (!js_AtomToPrintableString(cx, atom, &bytes))
@@ -949,17 +949,17 @@ js_IteratorMore(JSContext *cx, JSObject 
     /* We're reentering below and can call anything. */
     JS_CHECK_RECURSION(cx, return false);
 
     /* Fetch and cache the next value from the iterator. */
     if (!ni) {
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
         if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
             return false;
-        if (!ExternalInvoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
+        if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
             /* Check for StopIteration. */
             if (!cx->isExceptionPending() || !js_ValueIsStopIteration(cx->getPendingException()))
                 return false;
 
             cx->clearPendingException();
             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
             rval->setBoolean(false);
             return true;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1269,18 +1269,18 @@ EvalKernel(JSContext *cx, const CallArgs
                                                      chars, length, filename, lineno,
                                                      cx->findVersion(), linearStr, staticLevel);
         if (!compiled)
             return false;
 
         esg.setNewScript(compiled);
     }
 
-    return Execute(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType),
-                   NULL /* evalInFrame */, &call.rval());
+    return ExecuteKernel(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType),
+                         NULL /* evalInFrame */, &call.rval());
 }
 
 /*
  * We once supported a second argument to eval to use as the scope chain
  * when evaluating the code string.  Warn when such uses are seen so that
  * authors will know that support for eval(s, o) has been removed.
  */
 static inline bool
@@ -1399,18 +1399,18 @@ obj_watch_handler(JSContext *cx, JSObjec
     }
 
     /* Avoid recursion on (obj, id) already being watched on cx. */
     AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
     if (resolving.alreadyStarted())
         return true;
 
     Value argv[] = { IdToValue(id), Valueify(old), Valueify(*nvp) };
-    return ExternalInvoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable),
-                          JS_ARRAY_LENGTH(argv), argv, Valueify(nvp));
+    return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), JS_ARRAY_LENGTH(argv), argv,
+                  Valueify(nvp));
 }
 
 static JSBool
 obj_watch(JSContext *cx, uintN argc, Value *vp)
 {
     if (argc <= 1) {
         js_ReportMissingArg(cx, *vp, 1);
         return JS_FALSE;
@@ -5443,17 +5443,17 @@ JSObject::reportNotExtensible(JSContext 
                                     NULL, NULL, NULL);
 }
 
 bool
 JSObject::callMethod(JSContext *cx, jsid id, uintN argc, Value *argv, Value *vp)
 {
     Value fval;
     return js_GetMethod(cx, this, id, JSGET_NO_METHOD_BARRIER, &fval) &&
-           ExternalInvoke(cx, ObjectValue(*this), fval, argc, argv, vp);
+           Invoke(cx, ObjectValue(*this), fval, argc, argv, vp);
 }
 
 JSBool
 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
                      Value *vp, JSBool strict)
 {
     JSObject *pobj;
     JSProperty *prop;
@@ -5848,17 +5848,17 @@ static bool
 MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, vp))
         return false;
     if (!js_IsCallable(*vp)) {
         *vp = ObjectValue(*obj);
         return true;
     }
-    return ExternalInvoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp);
+    return Invoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp);
 }
 
 JSBool
 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
 {
     JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
     JS_ASSERT(!obj->isXML());
 
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -124,20 +124,18 @@ JSProxyHandler::get(JSContext *cx, JSObj
         vp->setUndefined();
         return true;
     }
     if (!desc.getter ||
         (!(desc.attrs & JSPROP_GETTER) && desc.getter == PropertyStub)) {
         *vp = desc.value;
         return true;
     }
-    if (desc.attrs & JSPROP_GETTER) {
-        return ExternalGetOrSet(cx, receiver, id, CastAsObjectJsval(desc.getter),
-                                JSACC_READ, 0, NULL, vp);
-    }
+    if (desc.attrs & JSPROP_GETTER)
+        return InvokeGetterOrSetter(cx, receiver, CastAsObjectJsval(desc.getter), 0, NULL, vp);
     if (!(desc.attrs & JSPROP_SHARED))
         *vp = desc.value;
     else
         vp->setUndefined();
     if (desc.attrs & JSPROP_SHORTID)
         id = INT_TO_JSID(desc.shortid);
     return CallJSPropertyOp(cx, desc.getter, receiver, id, vp);
 }
@@ -270,32 +268,31 @@ JSProxyHandler::defaultValue(JSContext *
     return DefaultValue(cx, proxy, hint, vp);
 }
 
 bool
 JSProxyHandler::call(JSContext *cx, JSObject *proxy, uintN argc, Value *vp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoValueRooter rval(cx);
-    JSBool ok = ExternalInvoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp),
-                               rval.addr());
+    JSBool ok = Invoke(cx, vp[1], GetCall(proxy), argc, JS_ARGV(cx, vp), rval.addr());
     if (ok)
         JS_SET_RVAL(cx, vp, rval.value());
     return ok;
 }
 
 bool
 JSProxyHandler::construct(JSContext *cx, JSObject *proxy,
                           uintN argc, Value *argv, Value *rval)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     Value fval = GetConstruct(proxy);
     if (fval.isUndefined())
-        return ExternalInvokeConstructor(cx, GetCall(proxy), argc, argv, rval);
-    return ExternalInvoke(cx, UndefinedValue(), fval, argc, argv, rval);
+        return InvokeConstructor(cx, GetCall(proxy), argc, argv, rval);
+    return Invoke(cx, UndefinedValue(), fval, argc, argv, rval);
 }
 
 bool
 JSProxyHandler::hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, ObjectValue(*proxy), NULL);
@@ -354,17 +351,17 @@ GetDerivedTrap(JSContext *cx, JSObject *
               atom == ATOM(iterate));
 
     return GetTrap(cx, handler, atom, fvalp);
 }
 
 static bool
 Trap(JSContext *cx, JSObject *handler, Value fval, uintN argc, Value* argv, Value *rval)
 {
-    return ExternalInvoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
+    return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
 }
 
 static bool
 Trap1(JSContext *cx, JSObject *handler, Value fval, jsid id, Value *rval)
 {
     JSString *str = js_ValueToString(cx, IdToValue(id));
     if (!str)
         return false;
@@ -1112,19 +1109,17 @@ proxy_Call(JSContext *cx, uintN argc, Va
     return JSProxy::call(cx, proxy, argc, vp);
 }
 
 JSBool
 proxy_Construct(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
     JS_ASSERT(proxy->isProxy());
-    Value rval;
-    bool ok = JSProxy::construct(cx, proxy, argc, JS_ARGV(cx, vp), &rval);
-    *vp = rval;
+    bool ok = JSProxy::construct(cx, proxy, argc, JS_ARGV(cx, vp), vp);
     return ok;
 }
 
 JS_FRIEND_API(Class) FunctionProxyClass = {
     "Proxy",
     Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(5),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
@@ -1310,19 +1305,17 @@ static const uint32 JSSLOT_CALLABLE_CONS
 
 static JSBool
 callable_Call(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *callable = &JS_CALLEE(cx, vp).toObject();
     JS_ASSERT(callable->getClass() == &CallableObjectClass);
     const Value &fval = callable->getSlot(JSSLOT_CALLABLE_CALL);
     const Value &thisval = vp[1];
-    Value rval;
-    bool ok = ExternalInvoke(cx, thisval, fval, argc, JS_ARGV(cx, vp), &rval);
-    *vp = rval;
+    bool ok = Invoke(cx, thisval, fval, argc, JS_ARGV(cx, vp), vp);
     return ok;
 }
 
 JSBool
 callable_Construct(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *thisobj = js_CreateThis(cx, &JS_CALLEE(cx, vp).toObject());
     if (!thisobj)
@@ -1350,30 +1343,28 @@ callable_Construct(JSContext *cx, uintN 
         }
 
         JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
         if (!newobj)
             return false;
 
         /* If the call returns an object, return that, otherwise the original newobj. */
         Value rval;
-        if (!ExternalInvoke(cx, ObjectValue(*newobj), callable->getSlot(JSSLOT_CALLABLE_CALL),
-                            argc, vp + 2, &rval)) {
+        if (!Invoke(cx, ObjectValue(*newobj), callable->getSlot(JSSLOT_CALLABLE_CALL),
+                    argc, vp + 2, &rval)) {
             return false;
         }
         if (rval.isPrimitive())
             vp->setObject(*newobj);
         else
             *vp = rval;
         return true;
     }
 
-    Value rval;
-    bool ok = ExternalInvoke(cx, ObjectValue(*thisobj), fval, argc, vp + 2, &rval);
-    *vp = rval;
+    bool ok = Invoke(cx, ObjectValue(*thisobj), fval, argc, vp + 2, vp);
     return ok;
 }
 
 Class CallableObjectClass = {
     "Function",
     JSCLASS_HAS_RESERVED_SLOTS(2),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -226,87 +226,87 @@ class NodeBuilder
 
   private:
     bool callback(Value fun, TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { NullValue() }; /* no zero-length arrays allowed! */
-        return ExternalInvoke(cx, userv, fun, 0, argv, dst);
+        return Invoke(cx, userv, fun, 0, argv, dst);
     }
 
     bool callback(Value fun, Value v1, TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { v1, loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { v1 };
-        return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+        return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
     }
 
     bool callback(Value fun, Value v1, Value v2, TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { v1, v2, loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { v1, v2 };
-        return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+        return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
     }
 
     bool callback(Value fun, Value v1, Value v2, Value v3, TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { v1, v2, v3, loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { v1, v2, v3 };
-        return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+        return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
     }
 
     bool callback(Value fun, Value v1, Value v2, Value v3, Value v4, TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { v1, v2, v3, v4, loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { v1, v2, v3, v4 };
-        return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+        return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
     }
 
     bool callback(Value fun, Value v1, Value v2, Value v3, Value v4, Value v5,
                   TokenPos *pos, Value *dst) {
         if (saveLoc) {
             Value loc;
             if (!newNodeLoc(pos, &loc))
                 return false;
             Value argv[] = { v1, v2, v3, v4, v5, loc };
-            return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+            return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
         }
 
         Value argv[] = { v1, v2, v3, v4, v5 };
-        return ExternalInvoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
+        return Invoke(cx, userv, fun, JS_ARRAY_LENGTH(argv), argv, dst);
     }
 
     Value opt(Value v) {
         JS_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE);
         return v.isMagic(JS_SERIALIZE_NO_NODE) ? UndefinedValue() : v;
     }
 
     bool atomValue(const char *s, Value *dst) {
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -262,17 +262,17 @@ inline bool
 Shape::get(JSContext* cx, JSObject *receiver, JSObject* obj, JSObject *pobj, js::Value* vp) const
 {
     JS_ASSERT(!JSID_IS_VOID(propid));
     JS_ASSERT(!hasDefaultGetter());
 
     if (hasGetterValue()) {
         JS_ASSERT(!isMethod());
         js::Value fval = getterValue();
-        return js::ExternalGetOrSet(cx, receiver, propid, fval, JSACC_READ, 0, 0, vp);
+        return js::InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp);
     }
 
     if (isMethod()) {
         vp->setObject(methodObject());
         return pobj->methodReadBarrier(cx, *this, vp);
     }
 
     /*
@@ -286,17 +286,17 @@ Shape::get(JSContext* cx, JSObject *rece
 
 inline bool
 Shape::set(JSContext* cx, JSObject* obj, bool strict, js::Value* vp) const
 {
     JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
     if (attrs & JSPROP_SETTER) {
         js::Value fval = setterValue();
-        return js::ExternalGetOrSet(cx, obj, propid, fval, JSACC_WRITE, 1, vp, vp);
+        return js::InvokeGetterOrSetter(cx, obj, fval, 1, vp, vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return js_ReportGetterOnlyAssignment(cx);
 
     /* See the comment in js::Shape::get as to why we check for With. */
     if (obj->getClass() == &js_WithClass)
         obj = js_UnwrapWithObject(cx, obj);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3499,17 +3499,17 @@ js_ValueToSource(JSContext *cx, const Va
     }
 
     Value rval = NullValue();
     Value fval;
     jsid id = ATOM_TO_JSID(cx->runtime->atomState.toSourceAtom);
     if (!js_GetMethod(cx, &v.toObject(), id, JSGET_NO_METHOD_BARRIER, &fval))
         return false;
     if (js_IsCallable(fval)) {
-        if (!ExternalInvoke(cx, v, fval, 0, NULL, &rval))
+        if (!Invoke(cx, v, fval, 0, NULL, &rval))
             return false;
     }
 
     return js_ValueToString(cx, rval);
 }
 
 namespace js {
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -187,24 +187,24 @@ InlineReturn(VMFrame &f)
               *f.regs.pc == JSOP_FUNCALL ||
               *f.regs.pc == JSOP_FUNAPPLY);
     f.regs.pc += JSOP_CALL_LENGTH;
 }
 
 void JS_FASTCALL
 stubs::SlowCall(VMFrame &f, uint32 argc)
 {
-    if (!Invoke(f.cx, CallArgsFromSp(argc, f.regs.sp)))
+    if (!InvokeKernel(f.cx, CallArgsFromSp(argc, f.regs.sp)))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::SlowNew(VMFrame &f, uint32 argc)
 {
-    if (!InvokeConstructor(f.cx, CallArgsFromSp(argc, f.regs.sp)))
+    if (!InvokeConstructorKernel(f.cx, CallArgsFromSp(argc, f.regs.sp)))
         THROW();
 }
 
 /*
  * HitStackQuota is called after the early prologue pushing the new frame would
  * overflow f.stackLimit.
  */
 void JS_FASTCALL
@@ -374,17 +374,17 @@ stubs::UncachedNewHelper(VMFrame &f, uin
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
 
     /* Try to do a fast inline call before the general Invoke path. */
     if (IsFunctionObject(args.calleev(), &ucr->fun) && ucr->fun->isInterpretedConstructor()) {
         ucr->callee = &args.callee();
         if (!UncachedInlineCall(f, CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
             THROW();
     } else {
-        if (!InvokeConstructor(cx, args))
+        if (!InvokeConstructorKernel(cx, args))
             THROW();
     }
 }
 
 void * JS_FASTCALL
 stubs::UncachedCall(VMFrame &f, uint32 argc)
 {
     UncachedCallResult ucr;
@@ -393,17 +393,17 @@ stubs::UncachedCall(VMFrame &f, uint32 a
 }
 
 void JS_FASTCALL
 stubs::Eval(VMFrame &f, uint32 argc)
 {
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
 
     if (!IsBuiltinEvalForScope(&f.fp()->scopeChain(), args.calleev())) {
-        if (!Invoke(f.cx, args))
+        if (!InvokeKernel(f.cx, args))
             THROW();
         return;
     }
 
     JS_ASSERT(f.fp() == f.cx->fp());
     if (!DirectEval(f.cx, args))
         THROW();
 
@@ -430,17 +430,17 @@ stubs::UncachedCallHelper(VMFrame &f, ui
 
         if (ucr->fun->isNative()) {
             if (!CallJSNative(cx, ucr->fun->u.n.native, args))
                 THROW();
             return;
         }
     }
 
-    if (!Invoke(f.cx, args))
+    if (!InvokeKernel(f.cx, args))
         THROW();
 
     return;
 }
 
 void JS_FASTCALL
 stubs::PutActivationObjects(VMFrame &f)
 {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -515,17 +515,17 @@ Debugger::handleUncaughtException(AutoCo
 {
     JSContext *cx = ac.context;
     if (cx->isExceptionPending()) {
         if (callHook && uncaughtExceptionHook) {
             Value fval = ObjectValue(*uncaughtExceptionHook);
             Value exc = cx->getPendingException();
             Value rv;
             cx->clearPendingException();
-            if (ExternalInvoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
+            if (Invoke(cx, ObjectValue(*object), fval, 1, &exc, &rv))
                 return vp ? parseResumptionValue(ac, true, rv, vp, false) : JSTRAP_CONTINUE;
         }
 
         if (cx->isExceptionPending()) {
             JS_ReportPendingException(cx);
             cx->clearPendingException();
         }
     }
@@ -621,17 +621,17 @@ CallMethodIfPresent(JSContext *cx, JSObj
                     Value *rval)
 {
     rval->setUndefined();
     JSAtom *atom = js_Atomize(cx, name, strlen(name));
     Value fval;
     return atom &&
            js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, &fval) &&
            (!js_IsCallable(fval) ||
-            ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
+            Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
 }
 
 JSTrapStatus
 Debugger::fireDebuggerStatement(JSContext *cx, Value *vp)
 {
     JSObject *hook = getHook(OnDebuggerStatement);
     JS_ASSERT(hook);
     JS_ASSERT(hook->isCallable());
@@ -642,17 +642,17 @@ Debugger::fireDebuggerStatement(JSContex
     if (!ac.enter())
         return JSTRAP_ERROR;
 
     Value argv[1];
     if (!getScriptFrame(cx, fp, argv))
         return handleUncaughtException(ac, vp, false);
 
     Value rv;
-    bool ok = ExternalInvoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
+    bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv);
     return parseResumptionValue(ac, ok, rv, vp);
 }
 
 JSTrapStatus
 Debugger::fireExceptionUnwind(JSContext *cx, Value *vp)
 {
     JSObject *hook = getHook(OnExceptionUnwind);
     JS_ASSERT(hook);
@@ -667,17 +667,17 @@ Debugger::fireExceptionUnwind(JSContext 
         return JSTRAP_ERROR;
 
     Value argv[2];
     argv[1] = exc;
     if (!getScriptFrame(cx, fp, &argv[0]) || !wrapDebuggeeValue(cx, &argv[1]))
         return handleUncaughtException(ac, vp, false);
 
     Value rv;
-    bool ok = ExternalInvoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv, &rv);
+    bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv, &rv);
     JSTrapStatus st = parseResumptionValue(ac, ok, rv, vp);
     if (st == JSTRAP_CONTINUE)
         cx->setPendingException(exc);
     return st;
 }
 
 void
 Debugger::fireEnterFrame(JSContext *cx)
@@ -692,17 +692,17 @@ Debugger::fireEnterFrame(JSContext *cx)
         return;
 
     Value argv[1];
     if (!getScriptFrame(cx, fp, &argv[0])) {
         handleUncaughtException(ac, NULL, false);
         return;
     }
     Value rv;
-    if (!ExternalInvoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
+    if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
         handleUncaughtException(ac, NULL, true);
 }
 
 void
 Debugger::fireNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind)
 {
     JSObject *hook = getHook(OnNewScript);
     JS_ASSERT(hook);
@@ -717,17 +717,17 @@ Debugger::fireNewScript(JSContext *cx, J
     if (!dsobj) {
         handleUncaughtException(ac, NULL, false);
         return;
     }
 
     Value argv[1];
     argv[0].setObject(*dsobj);
     Value rv;
-    if (!ExternalInvoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
+    if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, argv, &rv))
         handleUncaughtException(ac, NULL, true);
 }
 
 JSTrapStatus
 Debugger::dispatchHook(JSContext *cx, js::Value *vp, Hook which)
 {
     JS_ASSERT(which == OnDebuggerStatement || which == OnExceptionUnwind);
 
@@ -2671,17 +2671,17 @@ EvaluateInScope(JSContext *cx, JSObject 
     JSScript *script = Compiler::compileScript(cx, scobj, fp, fp->scopeChain().principals(cx),
                                                TCF_COMPILE_N_GO, chars, length,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
-    bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
+    bool ok = ExecuteKernel(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
     js_DestroyScript(cx, script, 6);
     return ok;
 }
 
 }
 
 enum EvalBindingsMode { WithoutBindings, WithBindings };
 
@@ -3378,17 +3378,17 @@ ApplyOrCall(JSContext *cx, uintN argc, V
             return false;
     }
 
     /*
      * Call the function. Use newCompletionValue to return to the debugger
      * compartment and populate args.rval().
      */
     Value rval;
-    bool ok = ExternalInvoke(cx, thisv, calleev, callArgc, callArgv, &rval);
+    bool ok = Invoke(cx, thisv, calleev, callArgc, callArgv, &rval);
     return dbg->newCompletionValue(ac, ok, rval, &args.rval());
 }
 
 static JSBool
 DebuggerObject_apply(JSContext *cx, uintN argc, Value *vp)
 {
     return ApplyOrCall(cx, argc, vp, ApplyMode);
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -249,16 +249,23 @@ CallArgsFromVp(uintN argc, Value *vp)
 JS_ALWAYS_INLINE CallArgs
 CallArgsFromSp(uintN argc, Value *sp)
 {
     return CallArgsFromArgv(argc, sp - argc);
 }
 
 /*****************************************************************************/
 
+/*
+ * For calls to natives, the InvokeArgsGuard object provides a record of the
+ * call for the debugger's callstack. For this to work, the InvokeArgsGuard
+ * record needs to know when the call is actually active (because the
+ * InvokeArgsGuard can be pushed long before and popped long after the actual
+ * call, during which time many stack-observing things can happen).
+ */
 class CallArgsList : public CallArgs
 {
     friend class StackSegment;
     CallArgsList *prev_;
     bool active_;
   public:
     friend CallArgsList CallArgsListFromVp(uintN, Value *, CallArgsList *);
     friend CallArgsList CallArgsListFromArgv(uintN, Value *, CallArgsList *);