Split out construction code from the interpreter proper and share it between the js_Interpret and a new Narcissus-only function on Function.prototype (__applyConstructor__). This allows Narcissus to call a native constructor with an array of arguments, like Function.prototype.apply. bug 331429, r=brendan
This commit is contained in:
Родитель
16a6738a82
Коммит
b079fe431d
|
@ -1631,7 +1631,7 @@ fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||
OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass))
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_APPLY_ARGS);
|
||||
JSMSG_BAD_APPLY_ARGS, "apply");
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (!js_GetLengthProperty(cx, aobj, &length))
|
||||
|
@ -1674,6 +1674,59 @@ out:
|
|||
}
|
||||
#endif /* JS_HAS_APPLY_FUNCTION */
|
||||
|
||||
#ifdef NARCISSUS
|
||||
static JSBool
|
||||
fun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
JSObject *aobj;
|
||||
uintN length, i;
|
||||
void *mark;
|
||||
jsval *sp, *newsp, *oldsp;
|
||||
JSBool ok;
|
||||
JSStackFrame *fp;
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(argv[0]) ||
|
||||
(aobj = JSVAL_TO_OBJECT(argv[0]),
|
||||
OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass &&
|
||||
OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_APPLY_ARGS, "__applyConstruct__");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (!js_GetLengthProperty(cx, aobj, &length))
|
||||
return JS_FALSE;
|
||||
|
||||
if (length >= ARGC_LIMIT)
|
||||
length = ARGC_LIMIT - 1;
|
||||
newsp = sp = js_AllocStack(cx, 2 + length, &mark);
|
||||
if (!sp)
|
||||
return JS_FALSE;
|
||||
|
||||
oldsp = fp->sp;
|
||||
*sp++ = OBJECT_TO_JSVAL(obj);
|
||||
*sp++ = JSVAL_NULL; /* This is filled automagically. */
|
||||
for (i = 0; i < length; i++) {
|
||||
ok = JS_GetElement(cx, aobj, (jsint)i, sp);
|
||||
if (!ok)
|
||||
goto out;
|
||||
sp++;
|
||||
}
|
||||
|
||||
fp = cx->fp;
|
||||
oldsp = fp->sp;
|
||||
fp->sp = sp;
|
||||
ok = js_InvokeConstructor(cx, newsp, length);
|
||||
|
||||
*rval = fp->sp[-1];
|
||||
fp->sp = oldsp;
|
||||
out:
|
||||
js_FreeStack(cx, mark);
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
|
||||
static JSFunctionSpec function_methods[] = {
|
||||
#if JS_HAS_TOSOURCE
|
||||
{js_toSource_str, fun_toSource, 0,0,0},
|
||||
|
@ -1684,6 +1737,9 @@ static JSFunctionSpec function_methods[] = {
|
|||
#endif
|
||||
#if JS_HAS_CALL_FUNCTION
|
||||
{call_str, fun_call, 1,0,0},
|
||||
#endif
|
||||
#ifdef NARCISSUS
|
||||
{"__applyConstructor__", fun_applyConstructor, 1,0,0},
|
||||
#endif
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
|
|
|
@ -1733,6 +1733,83 @@ js_StrictlyEqual(jsval lval, jsval rval)
|
|||
return lval == rval;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc)
|
||||
{
|
||||
JSFunction *fun;
|
||||
JSObject *obj, *obj2, *proto, *parent;
|
||||
jsval lval, rval;
|
||||
JSClass *clasp, *funclasp;
|
||||
|
||||
fun = NULL;
|
||||
obj2 = NULL;
|
||||
lval = *vp;
|
||||
if (!JSVAL_IS_OBJECT(lval) ||
|
||||
(obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
|
||||
/* XXX clean up to avoid special cases above ObjectOps layer */
|
||||
OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
|
||||
!obj2->map->ops->construct)
|
||||
{
|
||||
fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
clasp = &js_ObjectClass;
|
||||
if (!obj2) {
|
||||
proto = parent = NULL;
|
||||
fun = NULL;
|
||||
} else {
|
||||
/*
|
||||
* Get the constructor prototype object for this function.
|
||||
* Use the nominal 'this' parameter slot, vp[1], as a local
|
||||
* root to protect this prototype, in case it has no other
|
||||
* strong refs.
|
||||
*/
|
||||
if (!OBJ_GET_PROPERTY(cx, obj2,
|
||||
ATOM_TO_JSID(cx->runtime->atomState
|
||||
.classPrototypeAtom),
|
||||
&vp[1])) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
rval = vp[1];
|
||||
proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
|
||||
parent = OBJ_GET_PARENT(cx, obj2);
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
|
||||
funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp;
|
||||
if (funclasp)
|
||||
clasp = funclasp;
|
||||
}
|
||||
}
|
||||
obj = js_NewObject(cx, clasp, proto, parent);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Now we have an object with a constructor method; call it. */
|
||||
vp[1] = OBJECT_TO_JSVAL(obj);
|
||||
if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) {
|
||||
cx->newborn[GCX_OBJECT] = NULL;
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Check the return value and if it's primitive, force it to be obj. */
|
||||
rval = *vp;
|
||||
if (JSVAL_IS_PRIMITIVE(rval)) {
|
||||
if (!fun && JS_VERSION_IS_ECMA(cx)) {
|
||||
/* native [[Construct]] returning primitive is error */
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_NEW_RESULT,
|
||||
js_ValueToPrintableString(cx, rval));
|
||||
return JS_FALSE;
|
||||
}
|
||||
*vp = OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
|
||||
JS_RUNTIME_METER(cx->runtime, constructs);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
InternStringElementId(JSContext *cx, jsval idval, jsid *idp)
|
||||
{
|
||||
|
@ -1814,7 +1891,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
|
|||
JSStackFrame *fp;
|
||||
JSScript *script;
|
||||
uintN inlineCallCount;
|
||||
JSObject *obj, *obj2, *proto, *parent;
|
||||
JSObject *obj, *obj2, *parent;
|
||||
JSVersion currentVersion, originalVersion;
|
||||
JSBranchCallback onbranch;
|
||||
JSBool ok, cond;
|
||||
|
@ -1836,7 +1913,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
|
|||
JSString *str, *str2;
|
||||
jsint i, j;
|
||||
jsdouble d, d2;
|
||||
JSClass *clasp, *funclasp;
|
||||
JSClass *clasp;
|
||||
JSFunction *fun;
|
||||
JSType type;
|
||||
#if !defined JS_THREADED_INTERP && defined DEBUG
|
||||
|
@ -3288,82 +3365,13 @@ interrupt:
|
|||
vp = sp - (2 + argc);
|
||||
JS_ASSERT(vp >= fp->spbase);
|
||||
|
||||
fun = NULL;
|
||||
obj2 = NULL;
|
||||
lval = *vp;
|
||||
if (!JSVAL_IS_OBJECT(lval) ||
|
||||
(obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
|
||||
/* XXX clean up to avoid special cases above ObjectOps layer */
|
||||
OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
|
||||
!obj2->map->ops->construct)
|
||||
{
|
||||
fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
|
||||
if (!fun) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
clasp = &js_ObjectClass;
|
||||
if (!obj2) {
|
||||
proto = parent = NULL;
|
||||
fun = NULL;
|
||||
} else {
|
||||
/*
|
||||
* Get the constructor prototype object for this function.
|
||||
* Use the nominal 'this' parameter slot, vp[1], as a local
|
||||
* root to protect this prototype, in case it has no other
|
||||
* strong refs.
|
||||
*/
|
||||
ok = OBJ_GET_PROPERTY(cx, obj2,
|
||||
ATOM_TO_JSID(rt->atomState
|
||||
.classPrototypeAtom),
|
||||
&vp[1]);
|
||||
if (!ok)
|
||||
goto out;
|
||||
rval = vp[1];
|
||||
proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
|
||||
parent = OBJ_GET_PARENT(cx, obj2);
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
|
||||
funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp;
|
||||
if (funclasp)
|
||||
clasp = funclasp;
|
||||
}
|
||||
}
|
||||
obj = js_NewObject(cx, clasp, proto, parent);
|
||||
if (!obj) {
|
||||
ok = JS_FALSE;
|
||||
ok = js_InvokeConstructor(cx, vp, argc);
|
||||
if (!ok)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Now we have an object with a constructor method; call it. */
|
||||
vp[1] = OBJECT_TO_JSVAL(obj);
|
||||
ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT);
|
||||
RESTORE_SP(fp);
|
||||
LOAD_BRANCH_CALLBACK(cx);
|
||||
LOAD_INTERRUPT_HANDLER(rt);
|
||||
if (!ok) {
|
||||
cx->newborn[GCX_OBJECT] = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check the return value and update obj from it. */
|
||||
rval = *vp;
|
||||
if (JSVAL_IS_PRIMITIVE(rval)) {
|
||||
if (!fun && JS_VERSION_IS_ECMA(cx)) {
|
||||
/* native [[Construct]] returning primitive is error */
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_NEW_RESULT,
|
||||
js_ValueToPrintableString(cx, rval));
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
*vp = OBJECT_TO_JSVAL(obj);
|
||||
} else {
|
||||
obj = JSVAL_TO_OBJECT(rval);
|
||||
}
|
||||
JS_RUNTIME_METER(rt, constructs);
|
||||
obj = JSVAL_TO_OBJECT(*vp);
|
||||
len = js_CodeSpec[op].length;
|
||||
DO_NEXT_OP(len);
|
||||
|
||||
|
|
|
@ -313,6 +313,9 @@ js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
|
|||
extern JSBool
|
||||
js_StrictlyEqual(jsval lval, jsval rval);
|
||||
|
||||
extern JSBool
|
||||
js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc);
|
||||
|
||||
extern JSBool
|
||||
js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче