Compute 'this' in call ObjectOp so it can be overriden (572495, r=jorendorff).

This commit is contained in:
Andreas Gal 2010-06-16 16:36:40 -07:00
parent 2d3b5bbe7a
commit 02be8d79fb
4 changed files with 125 additions and 119 deletions

View File

@ -281,9 +281,13 @@ js_ComputeGlobalThis(JSContext *cx, jsval *argv)
return CallThisObjectHook(cx, thisp, argv); return CallThisObjectHook(cx, thisp, argv);
} }
static JSObject * JSObject *
ComputeThis(JSContext *cx, jsval *argv) js_ComputeThis(JSContext *cx, jsval *argv)
{ {
JS_ASSERT(argv[-1] != JSVAL_HOLE); // check for SynthesizeFrame poisoning
if (JSVAL_IS_NULL(argv[-1]))
return js_ComputeGlobalThis(cx, argv);
JSObject *thisp; JSObject *thisp;
JS_ASSERT(!JSVAL_IS_NULL(argv[-1])); JS_ASSERT(!JSVAL_IS_NULL(argv[-1]));
@ -301,15 +305,6 @@ ComputeThis(JSContext *cx, jsval *argv)
return CallThisObjectHook(cx, thisp, argv); return CallThisObjectHook(cx, thisp, argv);
} }
JSObject *
js_ComputeThis(JSContext *cx, jsval *argv)
{
JS_ASSERT(argv[-1] != JSVAL_HOLE); // check for SynthesizeFrame poisoning
if (JSVAL_IS_NULL(argv[-1]))
return js_ComputeGlobalThis(cx, argv);
return ComputeThis(cx, argv);
}
#if JS_HAS_NO_SUCH_METHOD #if JS_HAS_NO_SUCH_METHOD
const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE; const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE;
@ -433,112 +428,13 @@ class AutoPreserveEnumerators {
} }
}; };
/* static JS_REQUIRES_STACK bool
* Find a function reference and its 'this' object implicit first parameter Invoke(JSContext *cx, JSFunction *fun, JSScript *script, JSNative native,
* under argc arguments on cx's stack, and call the function. Push missing const InvokeArgsGuard &args, uintN flags)
* required arguments, allocate declared local variables, and pop everything
* when done. Then push the return value.
*/
JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
{ {
jsval *vp = args.getvp();
uintN argc = args.getArgc(); uintN argc = args.getArgc();
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); jsval *vp = args.getvp();
jsval v = vp[0];
if (JSVAL_IS_PRIMITIVE(v)) {
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
return false;
}
JSObject *funobj = JSVAL_TO_OBJECT(v);
JSObject *parent = funobj->getParent();
JSClass *clasp = funobj->getClass();
/* Filled by the following if/else statement. */
JSNative native;
JSFunction *fun;
JSScript *script;
if (clasp != &js_FunctionClass) {
#if JS_HAS_NO_SUCH_METHOD
if (clasp == &js_NoSuchMethodClass)
return NoSuchMethod(cx, argc, vp, flags);
#endif
/* Function is inlined, all other classes use object ops. */
const JSObjectOps *ops = funobj->map->ops;
fun = NULL;
script = NULL;
/* Try a call or construct native object op. */
if (flags & JSINVOKE_CONSTRUCT) {
if (!JSVAL_IS_OBJECT(vp[1])) {
if (!js_PrimitiveToObject(cx, &vp[1]))
return false;
}
native = ops->construct;
} else {
native = ops->call;
}
if (!native) {
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
return false;
}
} else {
/* Get private data and set derived locals from it. */
fun = GET_FUNCTION_PRIVATE(cx, funobj);
if (FUN_INTERPRETED(fun)) {
native = NULL;
script = fun->u.i.script;
JS_ASSERT(script);
if (script->isEmpty()) {
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
*vp = vp[1];
} else {
*vp = JSVAL_VOID;
}
return true;
}
} else {
native = fun->u.n.native;
script = NULL;
}
if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
/* Handle bound method special case. */
vp[1] = OBJECT_TO_JSVAL(parent);
} else if (!JSVAL_IS_OBJECT(vp[1])) {
JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
if (PRIMITIVE_THIS_TEST(fun, vp[1]))
goto start_call;
}
}
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
} else {
/*
* We must call js_ComputeThis in case we are not called from the
* interpreter, where a prior bytecode has computed an appropriate
* |this| already.
*
* But we need to compute |this| eagerly only for so-called "slow"
* (i.e., not fast) native functions. Fast natives must use either
* JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
* the appropriate this-computing bytecode, e.g., JSOP_THIS.
*/
if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
if (!js_ComputeThis(cx, vp + 2))
return false;
flags |= JSFRAME_COMPUTED_THIS;
}
}
start_call:
if (native && fun && fun->isFastNative()) { if (native && fun && fun->isFastNative()) {
#ifdef DEBUG_NOT_THROWING #ifdef DEBUG_NOT_THROWING
JSBool alreadyThrowing = cx->throwing; JSBool alreadyThrowing = cx->throwing;
@ -620,6 +516,7 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
cx->stack().pushInvokeFrame(cx, args, frame, regs); cx->stack().pushInvokeFrame(cx, args, frame, regs);
/* Now that the frame has been pushed, fix up the scope chain. */ /* Now that the frame has been pushed, fix up the scope chain. */
JSObject *parent = JSVAL_TO_OBJECT(vp[0])->getParent();
if (native) { if (native) {
/* Slow natives expect the caller's scopeChain as their scopeChain. */ /* Slow natives expect the caller's scopeChain as their scopeChain. */
if (JSStackFrame *down = fp->down) if (JSStackFrame *down = fp->down)
@ -677,6 +574,110 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
return ok; return ok;
} }
/*
* Find a function reference and its 'this' object 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 JS_FRIEND_API(JSBool)
js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
{
jsval *vp = args.getvp();
uintN argc = args.getArgc();
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
jsval v = vp[0];
if (JSVAL_IS_PRIMITIVE(v)) {
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
return false;
}
JSObject *funobj = JSVAL_TO_OBJECT(v);
JSClass *clasp = funobj->getClass();
if (clasp == &js_FunctionClass) {
/* Get private data and set derived locals from it. */
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
JSNative native;
JSScript *script;
if (FUN_INTERPRETED(fun)) {
native = NULL;
script = fun->u.i.script;
JS_ASSERT(script);
if (script->isEmpty()) {
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
*vp = vp[1];
} else {
*vp = JSVAL_VOID;
}
return true;
}
} else {
native = fun->u.n.native;
script = NULL;
}
if (JSFUN_BOUND_METHOD_TEST(fun->flags)) {
/* Handle bound method special case. */
vp[1] = OBJECT_TO_JSVAL(funobj->getParent());
} else if (!JSVAL_IS_OBJECT(vp[1])) {
JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT));
if (PRIMITIVE_THIS_TEST(fun, vp[1]))
return Invoke(cx, fun, script, native, args, flags);
}
if (flags & JSINVOKE_CONSTRUCT) {
JS_ASSERT(!JSVAL_IS_PRIMITIVE(args.getvp()[1]));
} else {
/*
* We must call js_ComputeThis in case we are not called from the
* interpreter, where a prior bytecode has computed an appropriate
* |this| already.
*
* But we need to compute |this| eagerly only for so-called "slow"
* (i.e., not fast) native functions. Fast natives must use either
* JS_THIS or JS_THIS_OBJECT, and scripted functions will go through
* the appropriate this-computing bytecode, e.g., JSOP_THIS.
*/
if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) {
jsval *vp = args.getvp();
if (!js_ComputeThis(cx, vp + 2))
return false;
flags |= JSFRAME_COMPUTED_THIS;
}
}
return Invoke(cx, fun, script, native, args, flags);
}
#if JS_HAS_NO_SUCH_METHOD
if (clasp == &js_NoSuchMethodClass)
return NoSuchMethod(cx, argc, vp, flags);
#endif
/* Function is inlined, all other classes use object ops. */
const JSObjectOps *ops = funobj->map->ops;
/* Try a call or construct native object op. */
JSNative native;
if (flags & JSINVOKE_CONSTRUCT) {
if (!JSVAL_IS_OBJECT(vp[1])) {
if (!js_PrimitiveToObject(cx, &vp[1]))
return false;
}
native = ops->construct;
} else {
native = ops->call;
}
if (!native) {
js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
return false;
}
return Invoke(cx, NULL, NULL, native, args, flags);
}
JSBool JSBool
js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
uintN argc, jsval *argv, jsval *rval) uintN argc, jsval *argv, jsval *rval)

View File

@ -5578,9 +5578,10 @@ GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
JSBool JSBool
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{ {
JSClass *clasp; if (!cx->fp->getThisObject(cx))
return false;
clasp = JSVAL_TO_OBJECT(argv[-2])->getClass(); JSClass *clasp = JSVAL_TO_OBJECT(argv[-2])->getClass();
if (!clasp->call) { if (!clasp->call) {
#ifdef NARCISSUS #ifdef NARCISSUS
JSObject *callee, *args; JSObject *callee, *args;

View File

@ -933,7 +933,8 @@ proxy_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSObject *proxy = JSVAL_TO_OBJECT(argv[-2]); JSObject *proxy = JSVAL_TO_OBJECT(argv[-2]);
JS_ASSERT(proxy->isProxy()); JS_ASSERT(proxy->isProxy());
AutoPendingProxyOperation pending(cx, proxy); AutoPendingProxyOperation pending(cx, proxy);
return js_InternalCall(cx, obj, proxy->fslots[JSSLOT_PROXY_CALL], argc, argv, rval); return !!cx->fp->getThisObject(cx) &&
js_InternalCall(cx, obj, proxy->fslots[JSSLOT_PROXY_CALL], argc, argv, rval);
} }
JSBool JSBool

View File

@ -5340,8 +5340,11 @@ regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
static JSBool static JSBool
regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{ {
return regexp_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, if (!cx->fp->getThisObject(cx))
JS_FALSE, rval); return false;
return !!cx->fp->getThisObject(cx) &&
regexp_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, JS_FALSE, rval);
} }
#if JS_HAS_XDR #if JS_HAS_XDR