From 5e262850d965b7e4fa7611d67c72c16133410297 Mon Sep 17 00:00:00 2001 From: "igor@mir2.org" Date: Wed, 2 Apr 2008 00:46:12 -0700 Subject: [PATCH] [Bug 423874] Allocating functions together with JSObject. r=brendan a1.9=blocking1.9 --- js/src/js.c | 1 - js/src/jsapi.c | 60 ++++----- js/src/jsarray.c | 6 +- js/src/jscntxt.h | 3 - js/src/jsdate.c | 2 +- js/src/jsdbgapi.c | 9 +- js/src/jsemit.c | 4 +- js/src/jsexn.c | 14 +- js/src/jsfun.c | 187 ++++++++++++--------------- js/src/jsfun.h | 13 +- js/src/jsgc.c | 8 -- js/src/jsgc.h | 16 +-- js/src/jsinterp.c | 41 +++--- js/src/jsiter.c | 4 +- js/src/jsobj.c | 56 ++++---- js/src/jsobj.h | 8 +- js/src/jsopcode.c | 11 +- js/src/jsopcode.h | 6 +- js/src/jsparse.c | 10 +- js/src/jsprvtd.h | 1 - js/src/jsregexp.c | 8 +- js/src/jsscript.c | 5 +- js/src/jsscript.h | 11 ++ js/src/jsxml.c | 19 +-- js/src/xpconnect/src/nsXPConnect.cpp | 6 +- 25 files changed, 240 insertions(+), 269 deletions(-) diff --git a/js/src/js.c b/js/src/js.c index f642a788bb49..255035059b8c 100644 --- a/js/src/js.c +++ b/js/src/js.c @@ -937,7 +937,6 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp) { "object", JSTRACE_OBJECT }, { "double", JSTRACE_DOUBLE }, { "string", JSTRACE_STRING }, - { "function", JSTRACE_FUNCTION }, #if JS_HAS_XML_SUPPORT { "namespace", JSTRACE_NAMESPACE }, { "qname", JSTRACE_QNAME }, diff --git a/js/src/jsapi.c b/js/src/jsapi.c index f36744388828..df5112eeb3fa 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -369,7 +369,7 @@ JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) break; case 'f': fun = va_arg(ap, JSFunction *); - *sp = fun ? OBJECT_TO_JSVAL(fun->object) : JSVAL_NULL; + *sp = fun ? OBJECT_TO_JSVAL(FUN_OBJECT(fun)) : JSVAL_NULL; break; case 'v': *sp = va_arg(ap, jsval); @@ -2014,10 +2014,6 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, name = "double"; break; - case JSTRACE_FUNCTION: - name = "function"; - break; - #if JS_HAS_XML_SUPPORT case JSTRACE_NAMESPACE: name = "namespace"; @@ -2053,7 +2049,20 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, { JSObject *obj = (JSObject *)thing; JSClass *clasp = STOBJ_GET_CLASS(obj); - if (clasp->flags & JSCLASS_HAS_PRIVATE) { + if (clasp == &js_FunctionClass) { + JSFunction *fun = (JSFunction *) + JS_GetPrivate(trc->context, obj); + + if (!fun) { + JS_snprintf(buf, bufsize, ""); + } else if (FUN_OBJECT(fun) != obj) { + JS_snprintf(buf, bufsize, "%p", fun); + } else { + if (fun->atom && ATOM_IS_STRING(fun->atom)) + js_PutEscapedString(buf, bufsize, + ATOM_TO_STRING(fun->atom), 0); + } + } else if (clasp->flags & JSCLASS_HAS_PRIVATE) { jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE); void *privateThing = JSVAL_IS_VOID(privateValue) ? NULL @@ -2074,15 +2083,6 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing); break; - case JSTRACE_FUNCTION: - { - JSFunction *fun = (JSFunction *)thing; - - if (fun->atom && ATOM_IS_STRING(fun->atom)) - js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(fun->atom), 0); - break; - } - #if JS_HAS_XML_SUPPORT case JSTRACE_NAMESPACE: { @@ -2698,7 +2698,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, } /* Create a prototype object for this class. */ - proto = js_NewObject(cx, clasp, parent_proto, obj); + proto = js_NewObject(cx, clasp, parent_proto, obj, 0); if (!proto) return NULL; @@ -2749,7 +2749,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, * different object, as is done for operator new -- and as at least * XML support requires. */ - ctor = fun->object; + ctor = FUN_OBJECT(fun); if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { cval = OBJECT_TO_JSVAL(ctor); if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) @@ -2966,7 +2966,7 @@ JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ - return js_NewObject(cx, clasp, proto, parent); + return js_NewObject(cx, clasp, proto, parent, 0); } JS_PUBLIC_API(JSObject *) @@ -2976,7 +2976,7 @@ JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ - return js_NewObjectWithGivenProto(cx, clasp, proto, parent); + return js_NewObjectWithGivenProto(cx, clasp, proto, parent, 0); } JS_PUBLIC_API(JSBool) @@ -3120,7 +3120,7 @@ JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, CHECK_REQUEST(cx); if (!clasp) clasp = &js_ObjectClass; /* default class is Object */ - nobj = js_NewObject(cx, clasp, proto, obj); + nobj = js_NewObject(cx, clasp, proto, obj, 0); if (!nobj) return NULL; if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, @@ -3999,7 +3999,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj) JSIdArray *ida; CHECK_REQUEST(cx); - iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); + iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj, 0); if (!iterobj) return NULL; @@ -4218,13 +4218,13 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) /* Indicate we cannot clone this object. */ return funobj; } - return js_CloneFunctionObject(cx, funobj, parent); + return js_CloneFunctionObject(cx, GET_FUNCTION_PRIVATE(cx, funobj), parent); } JS_PUBLIC_API(JSObject *) JS_GetFunctionObject(JSFunction *fun) { - return fun->object; + return FUN_OBJECT(fun); } JS_PUBLIC_API(const char *) @@ -4419,7 +4419,7 @@ JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) * As jsapi.h notes, fs must point to storage that lives as long * as fun->object lives. */ - if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) + if (!JS_SetReservedSlot(cx, FUN_OBJECT(fun), 0, PRIVATE_TO_JSVAL(fs))) return JS_FALSE; } @@ -4635,10 +4635,10 @@ JS_NewScriptObject(JSContext *cx, JSScript *script) JSObject *obj; if (!script) - return js_NewObject(cx, &js_ScriptClass, NULL, NULL); + return js_NewObject(cx, &js_ScriptClass, NULL, NULL, 0); JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr); - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL, 0); if (obj) { JS_SetPrivate(cx, obj, script); script->object = obj; @@ -4743,7 +4743,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, goto out2; /* From this point the control must flow through the label out. */ - JS_PUSH_TEMP_ROOT_FUNCTION(cx, fun, &tvr); + JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); for (i = 0; i < nargs; i++) { argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); if (!argAtom) { @@ -4765,7 +4765,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, if (obj && funAtom && !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), - OBJECT_TO_JSVAL(fun->object), + OBJECT_TO_JSVAL(FUN_OBJECT(fun)), NULL, NULL, JSPROP_ENUMERATE, NULL)) { fun = NULL; } @@ -4782,7 +4782,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, #endif out: - cx->weakRoots.newborn[JSTRACE_FUNCTION] = fun; + cx->weakRoots.newborn[JSTRACE_OBJECT] = FUN_OBJECT(fun); JS_POP_TEMP_ROOT(cx, &tvr); out2: @@ -4974,7 +4974,7 @@ JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, JSBool ok; CHECK_REQUEST(cx); - ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(FUN_OBJECT(fun)), argc, argv, rval); LAST_FRAME_CHECKS(cx, ok); return ok; diff --git a/js/src/jsarray.c b/js/src/jsarray.c index bfd0f3f82f60..e2b3f144f674 100644 --- a/js/src/jsarray.c +++ b/js/src/jsarray.c @@ -2879,7 +2879,7 @@ Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* If called without new, replace obj with a new Array object. */ if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -2929,7 +2929,7 @@ js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) JSTempValueRooter tvr; JSObject *obj; - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); + obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL, 0); if (!obj) return NULL; @@ -2946,7 +2946,7 @@ js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) JSObject * js_NewSlowArrayObject(JSContext *cx) { - JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL); + JSObject *obj = js_NewObject(cx, &js_SlowArrayClass, NULL, NULL, 0); if (obj) obj->fslots[JSSLOT_ARRAY_LENGTH] = 0; return obj; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index a7f4be532c99..66d4c2b4d629 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -619,9 +619,6 @@ JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(void *)); #define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ JS_PUSH_TEMP_ROOT_COMMON(cx, str, tvr, JSTVU_SINGLE, string) -#define JS_PUSH_TEMP_ROOT_FUNCTION(cx,fun,tvr) \ - JS_PUSH_TEMP_ROOT_COMMON(cx, fun, tvr, JSTVU_SINGLE, function) - #define JS_PUSH_TEMP_ROOT_QNAME(cx,qn,tvr) \ JS_PUSH_TEMP_ROOT_COMMON(cx, qn, tvr, JSTVU_SINGLE, qname) diff --git a/js/src/jsdate.c b/js/src/jsdate.c index 75ba8cf8f6fb..fc884698e561 100644 --- a/js/src/jsdate.c +++ b/js/src/jsdate.c @@ -2150,7 +2150,7 @@ js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) JSObject *obj; jsdouble *date; - obj = js_NewObject(cx, &js_DateClass, NULL, NULL); + obj = js_NewObject(cx, &js_DateClass, NULL, NULL, 0); if (!obj) return NULL; diff --git a/js/src/jsdbgapi.c b/js/src/jsdbgapi.c index 98dcec97f0da..81763b714975 100644 --- a/js/src/jsdbgapi.c +++ b/js/src/jsdbgapi.c @@ -697,7 +697,7 @@ js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) atom); if (!wrapper) return NULL; - return (JSPropertyOp) wrapper->object; + return (JSPropertyOp) FUN_OBJECT(wrapper); } JS_PUBLIC_API(JSBool) @@ -995,7 +995,7 @@ JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) JSRuntime *rt = cx->runtime; if (rt->findObjectPrincipals) { - if (fp->fun->object != fp->callee) + if (FUN_OBJECT(fp->fun) != fp->callee) return rt->findObjectPrincipals(cx, fp->callee); /* FALL THROUGH */ } @@ -1554,8 +1554,7 @@ JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) size_t nbytes; nbytes = sizeof *fun; - if (fun->object) - nbytes += JS_GetObjectTotalSize(cx, fun->object); + nbytes += JS_GetObjectTotalSize(cx, FUN_OBJECT(fun)); if (FUN_INTERPRETED(fun)) nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); if (fun->atom) @@ -1668,7 +1667,7 @@ JS_NewSystemObject(JSContext *cx, JSClass *clasp, JSObject *proto, { JSObject *obj; - obj = js_NewObject(cx, clasp, proto, parent); + obj = js_NewObject(cx, clasp, proto, parent, 0); if (obj && system) STOBJ_SET_SYSTEM(obj); return obj; diff --git a/js/src/jsemit.c b/js/src/jsemit.c index d29bf8fabf80..09fcb0aee3a3 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -2053,7 +2053,7 @@ CheckSideEffects(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: * in jsinterp.c. */ - fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object); + fun = (JSFunction *) pn->pn_funpob->object; if (fun->atom) *answer = JS_TRUE; break; @@ -3937,7 +3937,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) } #endif - fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object); + fun = (JSFunction *) pn->pn_funpob->object; if (fun->u.i.script) { /* * This second pass is needed to emit JSOP_NOP with a source note diff --git a/js/src/jsexn.c b/js/src/jsexn.c index bb8684046ae8..1ffbcaeab777 100644 --- a/js/src/jsexn.c +++ b/js/src/jsexn.c @@ -750,7 +750,7 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) .classPrototypeAtom), rval)) return JS_FALSE; - obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); + obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -1044,7 +1044,6 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) for (i = 0; exceptions[i].name != 0; i++) { JSAtom *atom; JSFunction *fun; - JSObject *funobj; JSString *nameString; int protoIndex = exceptions[i].protoIndex; @@ -1053,7 +1052,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) (protoIndex != JSEXN_NONE) ? protos[protoIndex] : obj_proto, - obj); + obj, 0); if (!protos[i]) break; @@ -1069,11 +1068,8 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) /* Make this constructor make objects of class Exception. */ fun->u.n.clasp = &js_ErrorClass; - /* Extract the constructor object. */ - funobj = fun->object; - /* Make the prototype and constructor links. */ - if (!js_SetClassPrototype(cx, funobj, protos[i], + if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), protos[i], JSPROP_READONLY | JSPROP_PERMANENT)) { break; } @@ -1092,7 +1088,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) } /* Finally, stash the constructor for later uses. */ - if (!js_SetClassObject(cx, obj, exceptions[i].key, funobj)) + if (!js_SetClassObject(cx, obj, exceptions[i].key, FUN_OBJECT(fun))) break; } @@ -1223,7 +1219,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) goto out; tv[0] = OBJECT_TO_JSVAL(errProto); - errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); + errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0); if (!errObject) { ok = JS_FALSE; goto out; diff --git a/js/src/jsfun.c b/js/src/jsfun.c index 43e7f52eac52..79490ad23e2e 100644 --- a/js/src/jsfun.c +++ b/js/src/jsfun.c @@ -254,7 +254,7 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp) return argsobj; /* Link the new object to fp so it can get actual argument values. */ - argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); + argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0); if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -602,7 +602,7 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) } /* Create the call object and link it to its stack frame. */ - callobj = js_NewObject(cx, &js_CallClass, NULL, parent); + callobj = js_NewObject(cx, &js_CallClass, NULL, parent, 0); if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -1076,7 +1076,6 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, return JS_TRUE; fun = GET_FUNCTION_PRIVATE(cx, obj); - JS_ASSERT(fun->object); /* * No need to reflect fun.prototype in 'fun.prototype = ... '. @@ -1112,8 +1111,8 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * Make the prototype object to have the same parent as the function * object itself. */ - proto = js_NewObject(cx, &js_ObjectClass, NULL, - OBJ_GET_PARENT(cx, obj)); + proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj), + 0); if (!proto) return JS_FALSE; @@ -1198,15 +1197,15 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); if (!fun) return JS_FALSE; - STOBJ_SET_PARENT(fun->object, NULL); - STOBJ_SET_PROTO(fun->object, NULL); + STOBJ_SET_PARENT(FUN_OBJECT(fun), NULL); + STOBJ_SET_PROTO(FUN_OBJECT(fun), NULL); #ifdef __GNUC__ nvars = nargs = 0; /* quell GCC uninitialized warning */ #endif } /* From here on, control flow must flow through label out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, fun->object, &tvr); + JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); ok = JS_TRUE; if (!JS_XDRUint32(xdr, &nullAtom)) @@ -1226,7 +1225,8 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) } /* do arguments and local vars */ - if (fun->object && (n = nargs + nvars) != 0) { + n = nargs + nvars; + if (n != 0) { void *mark; uintN i; uintN bitmapLength; @@ -1326,7 +1326,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) goto bad; if (xdr->mode == JSXDR_DECODE) { - *objp = fun->object; + *objp = FUN_OBJECT(fun); #ifdef CHECK_SCRIPT_OWNER fun->u.i.script->owner = NULL; #endif @@ -1378,6 +1378,12 @@ fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); } +static void +TraceLocalNames(JSTracer *trc, JSFunction *fun); + +static void +DestroyLocalNames(JSContext *cx, JSFunction *fun); + static void fun_trace(JSTracer *trc, JSObject *obj) { @@ -1385,8 +1391,42 @@ fun_trace(JSTracer *trc, JSObject *obj) /* A newborn function object may have a not yet initialized private slot. */ fun = (JSFunction *) JS_GetPrivate(trc->context, obj); - if (fun) - JS_CALL_TRACER(trc, fun, JSTRACE_FUNCTION, "private"); + if (!fun) + return; + + if (FUN_OBJECT(fun) != obj) { + /* obj is cloned function object, trace the original. */ + JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private"); + return; + } + if (fun->atom) + JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom"); + if (FUN_INTERPRETED(fun)) { + if (fun->u.i.script) + js_TraceScript(trc, fun->u.i.script); + TraceLocalNames(trc, fun); + } +} + +static void +fun_finalize(JSContext *cx, JSObject *obj) +{ + JSFunction *fun; + + /* Ignore newborn and cloned function objects. */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + if (!fun || FUN_OBJECT(fun) != obj) + return; + + /* + * Null-check of u.i.script is required since the parser sets interpreted + * very early. + */ + if (FUN_INTERPRETED(fun)) { + if (fun->u.i.script) + js_DestroyScript(cx, fun->u.i.script); + DestroyLocalNames(cx, fun); + } } static uint32 @@ -1418,7 +1458,7 @@ JS_FRIEND_DATA(JSClass) js_FunctionClass = { JS_PropertyStub, JS_PropertyStub, fun_getProperty, JS_PropertyStub, fun_enumerate, (JSResolveOp)fun_resolve, - fun_convert, JS_FinalizeStub, + fun_convert, fun_finalize, NULL, NULL, NULL, NULL, fun_xdrObject, fun_hasInstance, @@ -1721,20 +1761,19 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) fp = cx->fp; if (!(fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); + obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); + } else { + /* + * The constructor is called before the private slot is initialized so + * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here. + */ + if (JS_GetPrivate(cx, obj)) + return JS_TRUE; } - /* - * The constructor is called before the private slot is initialized so we - * must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here. - */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (fun) - return JS_TRUE; - /* * NB: (new Function) is not lexically closed by its caller, it's just an * anonymous function in the top-level scope that its constructor inhabits. @@ -1988,30 +2027,19 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, uintN flags, JSObject *parent, JSAtom *atom) { JSFunction *fun; - JSTempValueRooter tvr; - /* If funobj is null, allocate an object for it. */ if (funobj) { + JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); OBJ_SET_PARENT(cx, funobj, parent); } else { - funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); + funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0); if (!funobj) return NULL; } - - /* Protect fun from any potential GC callback. */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); - - /* - * Allocate fun after allocating funobj so allocations in js_NewObject - * and hooks called from it do not wipe out fun from newborn[GCX_FUNCTION]. - */ - fun = (JSFunction *) js_NewGCThing(cx, GCX_FUNCTION, sizeof(JSFunction)); - if (!fun) - goto out; + JS_ASSERT(funobj->fslots[JSSLOT_PRIVATE] == JSVAL_VOID); + fun = (JSFunction *) funobj; /* Initialize all function members. */ - fun->object = NULL; fun->nargs = nargs; fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED); if (flags & JSFUN_INTERPRETED) { @@ -2031,75 +2059,26 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, } fun->atom = atom; - /* Link fun to funobj and vice versa. */ - if (!js_LinkFunctionObject(cx, fun, funobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - fun = NULL; - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); + /* Set private to self to indicate non-cloned fully initialized function. */ + FUN_OBJECT(fun)->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); return fun; } -static void -TraceLocalNames(JSTracer *trc, JSFunction *fun); - -void -js_TraceFunction(JSTracer *trc, JSFunction *fun) -{ - if (fun->object) - JS_CALL_OBJECT_TRACER(trc, fun->object, "object"); - if (fun->atom) - JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom"); - if (FUN_INTERPRETED(fun)) { - if (fun->u.i.script) - js_TraceScript(trc, fun->u.i.script); - TraceLocalNames(trc, fun); - } -} - -static void -DestroyLocalNames(JSContext *cx, JSFunction *fun); - -void -js_FinalizeFunction(JSContext *cx, JSFunction *fun) -{ - /* - * Null-check of i.script is required since the parser sets interpreted - * very early. - */ - if (FUN_INTERPRETED(fun)) { - if (fun->u.i.script) - js_DestroyScript(cx, fun->u.i.script); - DestroyLocalNames(cx, fun); - } -} - JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) +js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent) { - JSObject *newfunobj; - JSFunction *fun; + JSObject *clone; - JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - newfunobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); - if (!newfunobj) + /* + * The cloned function object does not need the extra fields beyond + * JSObject as it points to fun via the private slot. + */ + clone = js_NewObject(cx, &js_FunctionClass, NULL, parent, + sizeof(JSObject)); + if (!clone) return NULL; - fun = GET_FUNCTION_PRIVATE(cx, funobj); - if (!js_LinkFunctionObject(cx, fun, newfunobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - return newfunobj; -} - -JSBool -js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) -{ - if (!fun->object) - fun->object = funobj; - return JS_SetPrivate(cx, funobj, fun); + clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); + return clone; } JSFunction * @@ -2114,7 +2093,7 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, return NULL; gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL; if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(fun->object), + OBJECT_TO_JSVAL(FUN_OBJECT(fun)), gsop, gsop, attrs & ~JSFUN_FLAGS_MASK, NULL)) { return NULL; @@ -2153,7 +2132,6 @@ JSObject * js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) { JSFunction *fun; - JSObject *funobj; JSStackFrame *caller; JSPrincipals *principals; @@ -2163,8 +2141,7 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) fun = js_ValueToFunction(cx, vp, flags); if (!fun) return NULL; - funobj = fun->object; - *vp = OBJECT_TO_JSVAL(funobj); + *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun)); caller = JS_GetScriptedCaller(cx, cx->fp); if (caller) { @@ -2174,13 +2151,13 @@ js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) principals = NULL; } - if (!js_CheckPrincipalsAccess(cx, funobj, principals, + if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals, fun->atom ? fun->atom : cx->runtime->atomState.anonymousAtom)) { return NULL; } - return funobj; + return FUN_OBJECT(fun); } JSObject * diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 3f1f03b25382..5e72d8be1db9 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -44,6 +44,7 @@ */ #include "jsprvtd.h" #include "jspubtd.h" +#include "jsobj.h" JS_BEGIN_EXTERN_C @@ -63,7 +64,7 @@ typedef union JSLocalNames { } JSLocalNames; struct JSFunction { - JSObject *object; /* back-pointer to GC'ed object header */ + JSObject object; /* GC'ed object header */ uint16 nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ uint16 flags; /* bound method and other flags, see jsapi.h */ @@ -90,6 +91,7 @@ struct JSFunction { #define JSFUN_SCRIPT_OR_FAST_NATIVE (JSFUN_INTERPRETED | JSFUN_FAST_NATIVE) +#define FUN_OBJECT(fun) (&(fun)->object) #define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED) #define FUN_SLOW_NATIVE(fun) (!((fun)->flags & JSFUN_SCRIPT_OR_FAST_NATIVE)) #define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) @@ -107,19 +109,20 @@ extern JS_FRIEND_DATA(JSClass) js_CallClass; /* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ extern JS_FRIEND_DATA(JSClass) js_FunctionClass; +#define HAS_FUNCTION_CLASS(obj) (STOBJ_GET_CLASS(obj) == &js_FunctionClass) + /* * NB: jsapi.h and jsobj.h must be included before any call to this macro. */ #define VALUE_IS_FUNCTION(cx, v) \ - (!JSVAL_IS_PRIMITIVE(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) + (!JSVAL_IS_PRIMITIVE(v) && HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v))) /* * Macro to access the private slot of the function object after the slot is * initialized. */ #define GET_FUNCTION_PRIVATE(cx, funobj) \ - (JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass), \ + (JS_ASSERT(HAS_FUNCTION_CLASS(funobj)), \ (JSFunction *) OBJ_GET_PRIVATE(cx, funobj)) extern JSObject * @@ -142,7 +145,7 @@ extern void js_FinalizeFunction(JSContext *cx, JSFunction *fun); extern JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); +js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent); extern JSBool js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); diff --git a/js/src/jsgc.c b/js/src/jsgc.c index 28e3f8cca3cb..f0149547a5ff 100644 --- a/js/src/jsgc.c +++ b/js/src/jsgc.c @@ -2250,10 +2250,6 @@ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) JS_CALL_STRING_TRACER(trc, JSSTRDEP_BASE(str), "base"); break; - case JSTRACE_FUNCTION: - js_TraceFunction(trc, (JSFunction *)thing); - break; - #if JS_HAS_XML_SUPPORT case JSTRACE_NAMESPACE: js_TraceXMLNamespace(trc, (JSXMLNamespace *)thing); @@ -2728,7 +2724,6 @@ TraceWeakRoots(JSTracer *trc, JSWeakRoots *wr) "newborn object", "newborn double", "newborn string", - "newborn function", "newborn namespace", "newborn qname", "newborn xml" @@ -3327,9 +3322,6 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) case GCX_DOUBLE: /* Do nothing. */ break; - case GCX_FUNCTION: - js_FinalizeFunction(cx, (JSFunction *) thing); - break; #if JS_HAS_XML_SUPPORT case GCX_NAMESPACE: js_FinalizeXMLNamespace(cx, diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 99f51e40cf5b..46351d09dde3 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -52,15 +52,14 @@ JS_BEGIN_EXTERN_C JS_STATIC_ASSERT(JSTRACE_STRING == 2); -#define JSTRACE_FUNCTION 3 -#define JSTRACE_NAMESPACE 4 -#define JSTRACE_QNAME 5 -#define JSTRACE_XML 6 +#define JSTRACE_NAMESPACE 3 +#define JSTRACE_QNAME 4 +#define JSTRACE_XML 5 /* * One past the maximum trace kind. */ -#define JSTRACE_LIMIT 7 +#define JSTRACE_LIMIT 6 /* * We use the trace kinds as the types for all GC things except external @@ -69,7 +68,6 @@ JS_STATIC_ASSERT(JSTRACE_STRING == 2); #define GCX_OBJECT JSTRACE_OBJECT /* JSObject */ #define GCX_DOUBLE JSTRACE_DOUBLE /* jsdouble */ #define GCX_STRING JSTRACE_STRING /* JSString */ -#define GCX_FUNCTION JSTRACE_FUNCTION /* JSFunction */ #define GCX_NAMESPACE JSTRACE_NAMESPACE /* JSXMLNamespace */ #define GCX_QNAME JSTRACE_QNAME /* JSXMLQName */ #define GCX_XML JSTRACE_XML /* JSXML */ @@ -235,14 +233,14 @@ js_IsAboutToBeFinalized(JSContext *cx, void *thing); #if JS_HAS_XML_SUPPORT # define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_XML) #else -# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_FUNCTION) +# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_STRING) #endif /* - * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_FUNCTION is the last non-xml + * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_STRING is the last non-xml * trace kind when JS_HAS_XML_SUPPORT is false. */ -JS_STATIC_ASSERT(JSTRACE_FUNCTION + 1 == JSTRACE_NAMESPACE); +JS_STATIC_ASSERT(JSTRACE_STRING + 1 == JSTRACE_NAMESPACE); /* * Trace jsval when JSVAL_IS_OBJECT(v) can be an arbitrary GC thing casted as diff --git a/js/src/jsinterp.c b/js/src/jsinterp.c index 68548bac21a2..aa96fe881a54 100644 --- a/js/src/jsinterp.c +++ b/js/src/jsinterp.c @@ -931,7 +931,7 @@ js_OnUnknownMethod(JSContext *cx, jsval *vp) vp[0] = ID_TO_VALUE(id); } #endif - obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL); + obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0); if (!obj) { ok = JS_FALSE; goto out; @@ -1600,7 +1600,9 @@ js_ImportProperty(JSContext *cx, JSObject *obj, jsid id) goto out; if (VALUE_IS_FUNCTION(cx, value)) { funobj = JSVAL_TO_OBJECT(value); - closure = js_CloneFunctionObject(cx, funobj, obj); + closure = js_CloneFunctionObject(cx, + GET_FUNCTION_PRIVATE(cx, funobj), + obj); if (!closure) { ok = JS_FALSE; goto out; @@ -1839,7 +1841,7 @@ js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) clasp = fun2->u.n.clasp; } } - obj = js_NewObject(cx, clasp, proto, parent); + obj = js_NewObject(cx, clasp, proto, parent, 0); if (!obj) return JS_FALSE; @@ -2546,10 +2548,7 @@ js_Interpret(JSContext *cx) JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj) #define LOAD_FUNCTION(PCOFF) \ - JS_BEGIN_MACRO \ - LOAD_OBJECT(PCOFF); \ - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); \ - JS_END_MACRO + JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun) /* * Prepare to call a user-supplied branch handler, and abort the script @@ -5586,8 +5585,9 @@ interrupt: * have seen the right parent already and created a sufficiently * well-scoped function object. */ + obj = FUN_OBJECT(fun); if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneFunctionObject(cx, obj, obj2); + obj = js_CloneFunctionObject(cx, fun, obj2); if (!obj) goto error; } @@ -5605,7 +5605,6 @@ interrupt: * and setters do not need a slot, their value is stored elsewhere * in the property itself, not in obj slots. */ - fun = GET_FUNCTION_PRIVATE(cx, obj); flags = JSFUN_GSFLAG2ATTR(fun->flags); if (flags) { attrs |= flags | JSPROP_SHARED; @@ -5670,7 +5669,7 @@ interrupt: if (!parent) goto error; - obj = js_CloneFunctionObject(cx, obj, parent); + obj = js_CloneFunctionObject(cx, fun, parent); if (!obj) goto error; @@ -5685,8 +5684,9 @@ interrupt: parent = js_GetScopeChain(cx, fp); if (!parent) goto error; + obj = FUN_OBJECT(fun); if (OBJ_GET_PARENT(cx, obj) != parent) { - obj = js_CloneFunctionObject(cx, obj, parent); + obj = js_CloneFunctionObject(cx, fun, parent); if (!obj) goto error; } @@ -5694,11 +5694,11 @@ interrupt: END_CASE(JSOP_ANONFUNOBJ) BEGIN_CASE(JSOP_NAMEDFUNOBJ) - /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ LOAD_FUNCTION(0); - rval = OBJECT_TO_JSVAL(obj); /* + * ECMA ed. 3 FunctionExpression: function Identifier [etc.]. + * * 1. Create a new object as if by the expression new Object(). * 2. Add Result(1) to the front of the scope chain. * @@ -5709,7 +5709,7 @@ interrupt: obj2 = js_GetScopeChain(cx, fp); if (!obj2) goto error; - parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); + parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2, 0); if (!parent) goto error; @@ -5719,16 +5719,10 @@ interrupt: * that was parsed by the compiler into a Function object, and * saved in the script's atom map]. * - * Protect parent from GC after js_CloneFunctionObject calls into - * js_NewObject, which displaces the newborn object root in cx by - * allocating the clone, then runs a last-ditch GC while trying - * to allocate the clone's slots vector. Another, multi-threaded - * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS - * which may suspend the current request in ClaimScope, with the - * newborn displaced as in the first scenario. + * Protect parent from the GC. */ fp->scopeChain = parent; - obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); + obj = js_CloneFunctionObject(cx, fun, parent); if (!obj) goto error; @@ -5745,7 +5739,6 @@ interrupt: * name is [fun->atom, the identifier parsed by the compiler], * value is Result(3), and attributes are { DontDelete, ReadOnly }. */ - fun = GET_FUNCTION_PRIVATE(cx, obj); attrs = JSFUN_GSFLAG2ATTR(fun->flags); if (attrs) { attrs |= JSPROP_SHARED; @@ -5912,7 +5905,7 @@ interrupt: JS_ASSERT(i == JSProto_Array || i == JSProto_Object); obj = (i == JSProto_Array) ? js_NewArrayObject(cx, 0, NULL) - : js_NewObject(cx, &js_ObjectClass, NULL, NULL); + : js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); if (!obj) goto error; PUSH_OPND(OBJECT_TO_JSVAL(obj)); diff --git a/js/src/jsiter.c b/js/src/jsiter.c index 8511e5d8d37b..1ad3f9a7ade5 100644 --- a/js/src/jsiter.c +++ b/js/src/jsiter.c @@ -400,7 +400,7 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) * we use the parent slot to keep track of the iterable, we must * fix it up after. */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); + iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0); if (!iterobj) goto bad; @@ -725,7 +725,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp) jsval *newsp; /* After the following return, failing control flow must goto bad. */ - obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); + obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL, 0); if (!obj) return NULL; diff --git a/js/src/jsobj.c b/js/src/jsobj.c index b3bb5943fdc8..a710d4c7fa69 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -1759,7 +1759,7 @@ Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); if (cx->fp->flags & JSFRAME_CONSTRUCTING) return JS_TRUE; - obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); + obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); if (!obj) return JS_FALSE; } @@ -1900,7 +1900,7 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) { JSObject *obj; - obj = js_NewObject(cx, &js_WithClass, proto, parent); + obj = js_NewObject(cx, &js_WithClass, proto, parent, 0); if (!obj) return NULL; STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp)); @@ -1919,7 +1919,7 @@ js_NewBlockObject(JSContext *cx) * scopes. Make sure obj has its own scope too, since clearing proto does * not affect OBJ_SCOPE(obj). */ - obj = js_NewObject(cx, &js_BlockClass, NULL, NULL); + obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0); if (!obj) return NULL; JS_LOCK_OBJ(cx, obj); @@ -1937,7 +1937,7 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, { JSObject *clone; - clone = js_NewObject(cx, &js_BlockClass, proto, parent); + clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0); if (!clone) return NULL; STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp)); @@ -2423,7 +2423,8 @@ js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) } JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, + uintN objectSize) { jsid id; @@ -2440,12 +2441,12 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) } } - return js_NewObjectWithGivenProto(cx, clasp, proto, parent); + return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize); } JSObject * js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent) + JSObject *parent, uintN objectSize) { JSObject *obj; JSObjectOps *ops; @@ -2459,24 +2460,25 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, jsdtrace_object_create_start(cx->fp, clasp); #endif - /* Always call the class's getObjectOps hook if it has one. */ - ops = clasp->getObjectOps - ? clasp->getObjectOps(cx, clasp) - : &js_ObjectOps; + /* Currently only functions can have non-standard allocation size. */ + if (clasp == &js_FunctionClass) { + if (objectSize == 0) + objectSize = sizeof(JSFunction); + else + JS_ASSERT(objectSize == sizeof(JSObject)); + } else { + JS_ASSERT(objectSize == 0); + objectSize = sizeof(JSObject); + } /* - * Allocate a zeroed object from the GC heap. Do this *after* any other - * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps, - * to avoid displacing the newborn root for obj. + * Allocate an object from the GC heap and initialize all its fields before + * doing any operation that can potentially trigger GC. */ - obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); + obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); if (!obj) goto earlybad; - /* - * Initialize all JSObject fields before doing any operation that can - * potentially trigger GC. - */ obj->map = NULL; obj->dslots = NULL; @@ -2496,6 +2498,11 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; +#ifdef DEBUG + memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, + objectSize - sizeof(JSObject)); +#endif + /* * Root obj to prevent it from being collected out from under this call to * js_NewObject. There's a possibilty of GC under the objectHook call-out @@ -2503,11 +2510,16 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, */ JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); + /* Always call the class's getObjectOps hook if it has one. */ + ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + /* * Default parent to the parent of the prototype, which was set from * the parent of the prototype's constructor. */ - if (!parent && proto) + if (proto && !parent) STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto)); /* @@ -2771,7 +2783,7 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, proto = JSVAL_TO_OBJECT(rval); } - obj = js_NewObject(cx, clasp, proto, parent); + obj = js_NewObject(cx, clasp, proto, parent, 0); if (!obj) goto out; @@ -4656,7 +4668,7 @@ js_PrimitiveToObject(JSContext *cx, jsval *vp) JS_ASSERT(!JSVAL_IS_OBJECT(*vp)); JS_ASSERT(*vp != JSVAL_VOID); clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1]; - obj = js_NewObject(cx, clasp, NULL, NULL); + obj = js_NewObject(cx, clasp, NULL, NULL, 0); if (!obj) return JS_FALSE; STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, *vp); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 5b975c4b7d23..19001d18a9b4 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -425,14 +425,18 @@ extern JSBool js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); extern JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); +js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, + uintN objectSize); /* * See jsapi.h, JS_NewObjectWithGivenProto. + * + * objectSize is either the explicit size for the allocated object or 0 + * indicating to use the default size based on object's class. */ extern JSObject * js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent); + JSObject *parent, uintN objectSize); /* * Fast access to immutable standard objects (constructors and prototypes). diff --git a/js/src/jsopcode.c b/js/src/jsopcode.c index d791927bad6a..ad128f7159da 100644 --- a/js/src/jsopcode.c +++ b/js/src/jsopcode.c @@ -1700,7 +1700,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj) #define LOAD_FUNCTION(PCOFF) \ - GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, obj) + GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun) #define LOAD_REGEXP(PCOFF) \ GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj) @@ -2016,11 +2016,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) break; case SRC_FUNCDEF: - JS_GET_SCRIPT_OBJECT(jp->script, js_GetSrcNoteOffset(sn, 0), - obj); + JS_GET_SCRIPT_FUNCTION(jp->script, + js_GetSrcNoteOffset(sn, 0), + fun); do_function: js_puts(jp, "\n"); - fun = GET_FUNCTION_PRIVATE(cx, obj); jp2 = JS_NEW_PRINTER(cx, "nested_function", fun, jp->indent, jp->pretty); if (!jp2) @@ -3767,8 +3767,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) SprintStack ss2; LOAD_FUNCTION(0); - fun = GET_FUNCTION_PRIVATE(cx, obj); - LOCAL_ASSERT(FUN_INTERPRETED(fun)); inner = fun->u.i.script; /* @@ -3884,7 +3882,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * parenthesization without confusing getter/setter code * that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ. */ - fun = GET_FUNCTION_PRIVATE(cx, obj); if (!(fun->flags & JSFUN_EXPR_CLOSURE)) indent |= JS_IN_GROUP_CONTEXT; str = JS_DecompileFunction(cx, fun, indent); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 8cbbb9cccaf7..e348b159dd65 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -332,10 +332,10 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, JS_GET_SCRIPT_OBJECT((script), index_, obj); \ JS_END_MACRO -#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, obj) \ +#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \ JS_BEGIN_MACRO \ - GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj); \ - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); \ + uintN index_ = js_GetIndexFromBytecode(cx, (script), (pc), (pcoff)); \ + JS_GET_SCRIPT_FUNCTION((script), index_, fun); \ JS_END_MACRO #define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \ diff --git a/js/src/jsparse.c b/js/src/jsparse.c index da360852385c..f387bf561a3e 100644 --- a/js/src/jsparse.c +++ b/js/src/jsparse.c @@ -1063,12 +1063,12 @@ NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom, JSFunction *fun; JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0); - parent = (tc->flags & TCF_IN_FUNCTION) ? tc->fun->object : cx->fp->varobj; + parent = (tc->flags & TCF_IN_FUNCTION) ? FUN_OBJECT(tc->fun) : cx->fp->varobj; fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda, parent, atom); if (fun && !(tc->flags & TCF_COMPILE_N_GO)) { - STOBJ_SET_PARENT(fun->object, NULL); - STOBJ_SET_PROTO(fun->object, NULL); + STOBJ_SET_PARENT(FUN_OBJECT(fun), NULL); + STOBJ_SET_PROTO(FUN_OBJECT(fun), NULL); } return fun; } @@ -1191,7 +1191,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * Create wrapping box for fun->object early to protect against a * last-ditch GC. */ - funpob = js_NewParsedObjectBox(cx, tc->parseContext, fun->object); + funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun)); if (!funpob) return NULL; @@ -4293,7 +4293,7 @@ GeneratorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, lambda->pn_op = JSOP_ANONFUNOBJ; lambda->pn_pos.begin = body->pn_pos.begin; lambda->pn_funpob = js_NewParsedObjectBox(cx, tc->parseContext, - fun->object); + FUN_OBJECT(fun)); if (!lambda->pn_funpob) return NULL; lambda->pn_body = body; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index ab988e837095..127c5250ee4e 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -237,7 +237,6 @@ typedef union JSTempValueUnion { jsval value; JSObject *object; JSString *string; - JSFunction *function; JSXML *xml; JSXMLQName *qname; JSTempValueTrace trace; diff --git a/js/src/jsregexp.c b/js/src/jsregexp.c index 0e214a6aa7ff..64754feefd4c 100644 --- a/js/src/jsregexp.c +++ b/js/src/jsregexp.c @@ -3868,7 +3868,7 @@ regexp_xdrObject(JSXDRState *xdr, JSObject **objp) return JS_FALSE; } if (xdr->mode == JSXDR_DECODE) { - obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); + obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL, 0); if (!obj) return JS_FALSE; STOBJ_SET_PARENT(obj, NULL); @@ -4236,7 +4236,7 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* Otherwise, replace obj with a new RegExp object. */ - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); + obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL, 0); if (!obj) return JS_FALSE; @@ -4296,7 +4296,7 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, if (!re) return NULL; JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); + obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, re)) { js_DestroyRegExp(cx, re); obj = NULL; @@ -4314,7 +4314,7 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) JSRegExp *re; JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); - clone = js_NewObject(cx, &js_RegExpClass, NULL, parent); + clone = js_NewObject(cx, &js_RegExpClass, NULL, parent, 0); if (!clone) return NULL; re = (JSRegExp *) JS_GetPrivate(cx, obj); diff --git a/js/src/jsscript.c b/js/src/jsscript.c index 7854771bb8d6..e718f24d554f 100644 --- a/js/src/jsscript.c +++ b/js/src/jsscript.c @@ -1692,7 +1692,6 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) uintN js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) { - JSObject *obj; JSFunction *fun; uintN lineno; ptrdiff_t offset, target; @@ -1710,9 +1709,7 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) if (js_CodeSpec[*pc].format & JOF_INDEXBASE) pc += js_CodeSpec[*pc].length; if (*pc == JSOP_DEFFUN) { - GET_FUNCTION_FROM_BYTECODE(script, pc, 0, obj); - fun = GET_FUNCTION_PRIVATE(cx, obj); - JS_ASSERT(FUN_INTERPRETED(fun)); + GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun); return fun->u.i.script->lineno; } diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 8b7d9f004fdf..ee78c504dbae 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -140,6 +140,17 @@ struct JSScript { (obj) = objects_->vector[(index)]; \ JS_END_MACRO +#define JS_GET_SCRIPT_FUNCTION(script, index, fun) \ + JS_BEGIN_MACRO \ + JSObject *funobj_; \ + \ + JS_GET_SCRIPT_OBJECT(script, index, funobj_); \ + JS_ASSERT(HAS_FUNCTION_CLASS(funobj_)); \ + JS_ASSERT(funobj_ == (JSObject *) STOBJ_GET_PRIVATE(funobj_)); \ + (fun) = (JSFunction *) funobj_; \ + JS_ASSERT(FUN_INTERPRETED(fun)); \ + JS_END_MACRO + #define JS_GET_SCRIPT_REGEXP(script, index, obj) \ JS_BEGIN_MACRO \ JSObjectArray *regexps_ = JS_SCRIPT_REGEXPS(script); \ diff --git a/js/src/jsxml.c b/js/src/jsxml.c index 9749d57153d8..aff680dd3aad 100644 --- a/js/src/jsxml.c +++ b/js/src/jsxml.c @@ -331,7 +331,7 @@ js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) JS_ASSERT(JS_GetPrivate(cx, obj) == ns); return obj; } - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, ns)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -607,7 +607,7 @@ js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) JS_ASSERT(JS_GetPrivate(cx, obj) == qn); return obj; } - obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); + obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -632,7 +632,7 @@ js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) return NULL; } - obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); + obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -748,7 +748,7 @@ Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* Create and return a new QName object exactly as if constructed. */ - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); + obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -856,7 +856,7 @@ QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) */ obj = js_NewObject(cx, JS_ValueToFunction(cx, argv[-2])->u.n.clasp, - NULL, NULL); + NULL, NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); @@ -7584,7 +7584,7 @@ NewXMLObject(JSContext *cx, JSXML *xml) { JSObject *obj; - obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); + obj = js_NewObject(cx, &js_XMLClass, NULL, NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, xml)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL; @@ -7713,7 +7713,7 @@ js_InitXMLClass(JSContext *cx, JSObject *obj) fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); if (!fun) return NULL; - if (!js_SetClassPrototype(cx, fun->object, proto, + if (!js_SetClassPrototype(cx, FUN_OBJECT(fun), proto, JSPROP_READONLY | JSPROP_PERMANENT)) { return NULL; } @@ -7997,7 +7997,8 @@ js_GetAnyName(JSContext *cx, jsval *vp) break; } - obj = js_NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL, NULL); + obj = js_NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL, + NULL, 0); if (!obj || !JS_SetPrivate(cx, obj, qn)) { cx->weakRoots.newborn[GCX_OBJECT] = NULL; ok = JS_FALSE; @@ -8311,7 +8312,7 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized) return JS_FALSE; } - filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL); + filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL, 0); if (!filterobj) return JS_FALSE; diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 2f2577bf1d9b..26c346b2f97f 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -533,13 +533,9 @@ nsXPConnect::Collect() return gCollected; } -// JSTRACE_FUNCTION can hold on to a lot of objects, adding it to the cycle -// collector reduces the number of edges to those objects. // JSTRACE_XML can recursively hold on to more JSTRACE_XML objects, adding it to // the cycle collector avoids stack overflow. -#define ADD_TO_CC(_kind) \ - ((_kind) == JSTRACE_OBJECT || (_kind) == JSTRACE_FUNCTION || \ - (_kind) == JSTRACE_XML) +#define ADD_TO_CC(_kind) ((_kind) == JSTRACE_OBJECT || (_kind) == JSTRACE_XML) #ifdef DEBUG_CC struct NoteJSRootTracer : public JSTracer