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:
mrbkap%gmail.com 2006-04-17 23:36:31 +00:00
Родитель 16a6738a82
Коммит b079fe431d
3 изменённых файлов: 142 добавлений и 75 удалений

Просмотреть файл

@ -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);