From 32af93813b52c72563af69d775741e20c9c33972 Mon Sep 17 00:00:00 2001 From: "brendan@mozilla.org" Date: Tue, 19 Jun 2007 23:37:15 -0700 Subject: [PATCH] Fix violation of function prototyping due to cloned function object implementation (300079, r=mrbkap; expecting r=igor after the fact, want to get this in for widespread testing tomorrow). --- js/src/jsapi.c | 10 +---- js/src/jsatom.h | 4 ++ js/src/jsfun.c | 116 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 92 insertions(+), 38 deletions(-) diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 637f935a8b4..d9a5917cf03 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -1267,11 +1267,7 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) js_InitDateClass(cx, obj); } -#define ATOM_OFFSET(name) offsetof(JSAtomState,name##Atom) -#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) -#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) -#define CLASP(name) (JSClass *)&js_##name##Class - +#define CLASP(name) ((JSClass *)&js_##name##Class) #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL #define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) @@ -1640,11 +1636,7 @@ JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, return js_SetIdArrayLength(cx, ida, i); } -#undef ATOM_OFFSET -#undef CLASS_ATOM_OFFSET -#undef OFFSET_TO_ATOM #undef CLASP - #undef EAGER_ATOM #undef EAGER_CLASS_ATOM #undef EAGER_ATOM_CLASP diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 7b917ec6d33..3b216be3128 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -248,6 +248,10 @@ struct JSAtomState { #endif }; +#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) +#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) +#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) + #define CLASS_ATOM(cx,name) \ ((cx)->runtime->atomState.classAtoms[JSProto_##name]) diff --git a/js/src/jsfun.c b/js/src/jsfun.c index 4488c294109..e01a31aa996 100644 --- a/js/src/jsfun.c +++ b/js/src/jsfun.c @@ -962,19 +962,32 @@ JSClass js_CallClass = { * * The extensions below other than length, i.e., the ones not in ECMA-262, * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility - * with ECMA we must allow a delegating object to override them. + * with ECMA we must allow a delegating object to override them. Therefore to + * avoid entraining garbage in Function.prototype slots, they must be resolved + * in non-prototype function objects, wherefore the lazy_function_props table + * and fun_resolve's use of it. */ #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) static JSPropertySpec function_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, - {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, - {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, - {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, {0,0,0,0,0} }; +typedef struct LazyFunctionProp { + uint16 atomOffset; + int8 tinyid; + uint8 attrs; +} LazyFunctionProp; + +/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */ +static LazyFunctionProp lazy_function_props[] = { + {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT}, + {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT}, + {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT}, + {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT}, +}; + static JSBool fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { @@ -1094,46 +1107,71 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { JSFunction *fun; - JSString *str; - JSAtom *prototypeAtom; - - /* - * No need to reflect fun.prototype in 'fun.prototype = ...' or in an - * unqualified reference to prototype, which the emitter looks up as a - * hidden atom when attempting to bind to a formal parameter or local - * variable slot. - */ - if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_HIDDEN)) - return JS_TRUE; + JSAtom *atom; + uintN i; if (!JSVAL_IS_STRING(id)) return JS_TRUE; - /* No valid function object should lack private data, but check anyway. */ + /* No valid function object should lack private data. */ fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); - if (!fun || !fun->object) + JS_ASSERT(fun && fun->object); + + /* + * Check for a hidden formal parameter or local variable binding in the + * clone-parent of obj, which would be a different, non-null fun->object. + */ + if (flags & JSRESOLVE_HIDDEN) { + if (fun->object != obj) { + JSObject *pobj; + JSProperty *prop; + + atom = js_AtomizeString(cx, JSVAL_TO_STRING(id), 0); + if (!atom) + return JS_FALSE; + if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) { + JS_ASSERT(pobj == fun->object); + *objp = pobj; + OBJ_DROP_PROPERTY(cx, pobj, prop); + } + } + return JS_TRUE; + } + + /* + * No need to reflect fun.prototype in 'fun.prototype = ...'. This test + * must come after the JSRESOLVE_HIDDEN test, since call_resolve may look + * for a hidden function object property from an assignment bytecode. + */ + if (flags & JSRESOLVE_ASSIGNING) return JS_TRUE; /* * Ok, check whether id is 'prototype' and bootstrap the function object's * prototype property. */ - str = JSVAL_TO_STRING(id); - prototypeAtom = cx->runtime->atomState.classPrototypeAtom; - if (str == ATOM_TO_STRING(prototypeAtom)) { + atom = cx->runtime->atomState.classPrototypeAtom; + if (id == ATOM_KEY(atom)) { JSObject *proto, *parentProto; jsval pval; proto = parentProto = NULL; - if (fun->object != obj && fun->object) { + if (fun->object != obj && + (!cx->runtime->findObjectPrincipals || + cx->runtime->findObjectPrincipals(cx, obj) == + cx->runtime->findObjectPrincipals(cx, fun->object))) { /* - * Clone of a function: make its prototype property value have the - * same class as the clone-parent's prototype. + * Clone of a function where the clone and the object owning fun + * appear to be in the same trust domain: make the cloned function + * object's 'prototype' property value have the same class as the + * clone-parent's 'prototype' value. */ - if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), - &pval)) { + if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(atom), &pval)) return JS_FALSE; - } if (!JSVAL_IS_PRIMITIVE(pval)) { /* * We are about to allocate a new object, so hack the newborn @@ -1175,6 +1213,24 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, return JS_FALSE; } *objp = obj; + return JS_TRUE; + } + + for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) { + LazyFunctionProp *lfp = &lazy_function_props[i]; + + atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); + if (id == ATOM_KEY(atom)) { + if (!js_DefineNativeProperty(cx, obj, + ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, lfp->attrs, + SPROP_HAS_SHORTID, lfp->tinyid, + NULL)) { + return JS_FALSE; + } + *objp = obj; + return JS_TRUE; + } } return JS_TRUE; @@ -1457,8 +1513,10 @@ fun_trace(JSTracer *trc, JSObject *obj) fun = (JSFunction *) JS_GetPrivate(trc->context, obj); if (fun) { JS_CALL_TRACER(trc, fun, JSTRACE_FUNCTION, "private"); + if (fun->object != obj) + JS_CALL_TRACER(trc, fun->object, JSTRACE_OBJECT, "object"); if (fun->atom) - JS_CALL_TRACER(trc, fun->atom, JSTRACE_ATOM, "name"); + JS_CALL_TRACER(trc, fun->atom, JSTRACE_ATOM, "atom"); if (FUN_INTERPRETED(fun) && fun->u.i.script) js_TraceScript(trc, fun->u.i.script); } @@ -2170,7 +2228,7 @@ js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) JSFunction *fun; JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); + newfunobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); if (!newfunobj) return NULL; fun = (JSFunction *) JS_GetPrivate(cx, funobj);