diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index 4f03644b1c5..bb43af18ad9 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -1501,9 +1501,6 @@ DumpScope(JSContext *cx, JSObject *obj, FILE *fp) } else { if (JSID_IS_ATOM(sprop->id)) { str = JSVAL_TO_STRING(v); - } else if (JSID_IS_HIDDEN(sprop->id)) { - str = JSVAL_TO_STRING(v); - fputs("hidden ", fp); } else { JS_ASSERT(JSID_IS_OBJECT(sprop->id)); str = js_ValueToString(cx, v); diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index 2bd215de8e8..5c93d81333b 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -2693,7 +2693,7 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, * we know to create an object of this class when we call the * constructor. */ - fun->clasp = clasp; + fun->u.n.clasp = clasp; /* * Optionally construct the prototype object, before the class has diff --git a/mozilla/js/src/jsdbgapi.c b/mozilla/js/src/jsdbgapi.c index 5e9f3c45b4a..0026bd070c3 100644 --- a/mozilla/js/src/jsdbgapi.c +++ b/mozilla/js/src/jsdbgapi.c @@ -1285,7 +1285,6 @@ JS_PUBLIC_API(JSBool) JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, JSPropertyDesc *pd) { - JSPropertyOp getter; JSScope *scope; JSScopeProperty *aprop; jsval lastException; @@ -1322,20 +1321,14 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, js_RemoveRoot(cx->runtime, &lastException); } - getter = sprop->getter; pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) - | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0); - if (JSID_IS_HIDDEN(sprop->id)) { - pd->flags |= (getter == JS_HIDDEN_ARG_GETTER) - ? JSPD_ARGUMENT - : JSPD_VARIABLE; - } + | ((sprop->getter == js_GetCallVariable) ? JSPD_VARIABLE : 0); /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && - getter == js_CallClass.getProperty) { + sprop->getter == js_CallClass.getProperty) { /* * Property of a heavyweight function's variable object having the * class-default getter. It's either an argument if permanent, or a diff --git a/mozilla/js/src/jsexn.c b/mozilla/js/src/jsexn.c index 5fc9f700e57..8191e6ebd01 100644 --- a/mozilla/js/src/jsexn.c +++ b/mozilla/js/src/jsexn.c @@ -1057,7 +1057,7 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) break; /* Make this constructor make objects of class Exception. */ - fun->clasp = &js_ErrorClass; + fun->u.n.clasp = &js_ErrorClass; /* Extract the constructor object. */ funobj = fun->object; diff --git a/mozilla/js/src/jsfun.c b/mozilla/js/src/jsfun.c index da4c1dcbc53..7b656d5ffe6 100644 --- a/mozilla/js/src/jsfun.c +++ b/mozilla/js/src/jsfun.c @@ -746,66 +746,64 @@ static JSBool call_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; - JSObject *funobj, *pobj; - JSScope *scope; - JSScopeProperty *sprop, *cprop; - jsval *vec; - jsid id; + JSFunction *fun; + uintN n, i, slot; + void *mark; + JSAtom **names, *name; + JSObject *pobj; JSProperty *prop; + jsval v; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!fp) return JS_TRUE; - - /* - * Do not enumerate a cloned function object at fp->callee, it may have - * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets - * the clone's prototype property). We must enumerate the function object - * that was decorated with parameter and local variable properties by the - * compiler when the compiler created fp->fun, namely fp->fun->object. - * - * Contrast with call_resolve, where we prefer fp->callee, because we'll - * use js_LookupProperty to find any overridden properties in that object, - * if it was a mutated clone; and if not, we will search its prototype, - * fp->fun->object, to find compiler-created params and locals. - */ - funobj = fp->fun->object; - if (!funobj) - return JS_TRUE; + JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun); /* * Reflect actual args from fp->argv for formal parameters, and local vars * and functions in fp->vars for declared variables and nested-at-top-level * local functions. */ - scope = OBJ_SCOPE(funobj); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (!JSID_IS_HIDDEN(sprop->id)) - continue; - vec = (sprop->getter == JS_HIDDEN_ARG_GETTER) ? fp->argv : fp->vars; + fun = fp->fun; + n = fun->nargs + fun->u.i.nvars; + if (n == 0) + return JS_TRUE; - /* Trigger reflection by looking up the unhidden atom for sprop->id. */ - id = JSID_UNHIDE_NAME(sprop->id); - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; + mark = JS_ARENA_MARK(&cx->tempPool); + names = js_GetLocalNames(cx, fun, &cx->tempPool, NULL); + if (!names) + goto out; + + for (i = 0; i != n; ++i) { + name = names[i]; + if (!name) + continue; /* - * If we found the property in a different object, don't try sticking - * it into wrong slots vector. This can occur because we have a mutable - * __proto__ slot, and cloned function objects rely on their __proto__ - * to delegate to the object that contains the var and arg properties. + * Trigger reflection by looking up the name of the argument or + * variable. */ - if (!prop || pobj != obj) { - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - continue; + if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop)) { + names = NULL; + goto out; } - cprop = (JSScopeProperty *)prop; - LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); - OBJ_DROP_PROPERTY(cx, obj, prop); + + /* + * At this point the call object always has a property corresponding + * to the local name because call_resolve creates the property using + * JSPROP_PERMANENT. + */ + JS_ASSERT(prop && pobj == obj); + slot = ((JSScopeProperty *) prop)->slot; + OBJ_DROP_PROPERTY(cx, pobj, prop); + + v = (i < fun->nargs) ? fp->argv[i] : fp->vars[i - fun->nargs]; + LOCKED_OBJ_SET_SLOT(obj, slot, v); } - return JS_TRUE; + out: + JS_ARENA_RELEASE(&cx->tempPool, mark); + return names != NULL; } static JSBool @@ -824,14 +822,11 @@ call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, if (!fp) return JS_TRUE; JS_ASSERT(fp->fun); + JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun); if (!JSVAL_IS_STRING(id)) return JS_TRUE; - if (!fp->callee) - return JS_TRUE; - JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun); - str = JSVAL_TO_STRING(id); atom = js_AtomizeString(cx, str, 0); if (!atom) @@ -1320,6 +1315,9 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) JS_ARENA_RELEASE(&xdr->cx->tempPool, mark); if (!ok) goto out; + + if (xdr->mode == JSXDR_DECODE) + js_FreezeLocalNames(cx, fun); } if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) @@ -2013,13 +2011,16 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, fun->u.i.nvars = 0; fun->u.i.spare = 0; fun->u.i.script = NULL; +#ifdef DEBUG + fun->u.i.names.taggedAtom = 0; +#endif } else { fun->u.n.native = native; fun->u.n.extra = 0; fun->u.n.minargs = 0; + fun->u.n.clasp = NULL; } fun->atom = atom; - fun->clasp = NULL; /* Link fun to funobj and vice versa. */ if (!js_LinkFunctionObject(cx, fun, funobj)) { @@ -2032,6 +2033,9 @@ out: return fun; } +static void +TraceLocalNames(JSTracer *trc, JSFunction *fun); + void js_TraceFunction(JSTracer *trc, JSFunction *fun) { @@ -2039,10 +2043,16 @@ js_TraceFunction(JSTracer *trc, JSFunction *fun) 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) && fun->u.i.script) - js_TraceScript(trc, fun->u.i.script); + 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) { @@ -2050,8 +2060,11 @@ js_FinalizeFunction(JSContext *cx, JSFunction *fun) * Null-check of i.script is required since the parser sets interpreted * very early. */ - if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_DestroyScript(cx, fun->u.i.script); + if (FUN_INTERPRETED(fun)) { + if (fun->u.i.script) + js_DestroyScript(cx, fun->u.i.script); + DestroyLocalNames(cx, fun); + } } JSObject * @@ -2209,61 +2222,201 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) name, source); } +/* + * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables, + * their name are stored as the JSLocalNames.array. + */ +#define MAX_ARRAY_LOCALS 8 + +JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS); +JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16)); + +/* + * We use the lowest bit of the string atom to distinguish const from var + * name when there is only single name or when names are stored as an array. + */ +JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0); + +/* + * When we use a hash table to store the local names, we use a singly linked + * list to record the indexes of duplicated parameter names to preserve the + * duplicates for the decompiler. + */ +typedef struct JSNameIndexPair JSNameIndexPair; + +struct JSNameIndexPair { + JSAtom *name; + uint16 index; + JSNameIndexPair *link; +}; + +struct JSLocalNameMap { + JSDHashTable names; + JSNameIndexPair *lastdup; +}; + +typedef struct JSLocalNameHashEntry { + JSDHashEntryHdr hdr; + JSAtom *name; + uint16 index; + uint8 localKind; +} JSLocalNameHashEntry; + +static void +FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map) +{ + JSNameIndexPair *dup, *next; + + for (dup = map->lastdup; dup; dup = next) { + next = dup->link; + JS_free(cx, dup); + } + JS_DHashTableFinish(&map->names); + JS_free(cx, map); +} + +static JSBool +HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name, + JSLocalKind localKind, uintN index) +{ + JSLocalNameHashEntry *entry; + JSNameIndexPair *dup; + + JS_ASSERT(index <= JS_BITMASK(16)); +#if JS_HAS_DESTRUCTURING + if (!name) { + /* A destructuring pattern does not need a hash entry. */ + JS_ASSERT(localKind == JSLOCAL_ARG); + return JS_TRUE; + } +#endif + JS_ASSERT(ATOM_IS_STRING(name)); + entry = (JSLocalNameHashEntry *) + JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD); + if (!entry) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + if (entry->name) { + JS_ASSERT(entry->name == name); + JS_ASSERT(entry->localKind == JSLOCAL_ARG); + dup = (JSNameIndexPair *) JS_malloc(cx, sizeof *dup); + if (!dup) + return JS_FALSE; + dup->name = entry->name; + dup->index = entry->index; + dup->link = map->lastdup; + map->lastdup = dup; + } + entry->name = name; + entry->index = (uint16) index; + entry->localKind = (uint8) localKind; + return JS_TRUE; +} + JSBool js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind) { + jsuword taggedAtom; uint16 *indexp; - JSPropertyOp getter; - uintN readonly; + uintN n, i; + jsuword *array; + JSLocalNameMap *map; JS_ASSERT(FUN_INTERPRETED(fun)); - JS_ASSERT(OBJ_IS_NATIVE(fun->object)); + JS_ASSERT(!fun->u.i.script); + JS_ASSERT(((jsuword) atom & 1) == 0); + taggedAtom = (jsuword) atom; if (kind == JSLOCAL_ARG) { -#if JS_HAS_DESTRUCTURING - /* - * Destructuring parameter does not have name so we just update the - * number of arguments for it without adding a property. - */ - if (!atom) { - ++fun->nargs; - return JS_TRUE; - } -#endif indexp = &fun->nargs; - getter = JS_HIDDEN_ARG_GETTER; - readonly = 0; } else { - JS_ASSERT(kind == JSLOCAL_VAR || kind == JSLOCAL_CONST); indexp = &fun->u.i.nvars; - getter = JS_HIDDEN_VAR_GETTER; - readonly = (kind == JSLOCAL_CONST) ? JSPROP_READONLY : 0; + if (kind == JSLOCAL_CONST) + taggedAtom |= 1; + else + JS_ASSERT(kind == JSLOCAL_VAR); + } + n = fun->nargs + fun->u.i.nvars; + if (n == 0) { + JS_ASSERT(fun->u.i.names.taggedAtom == 0); + fun->u.i.names.taggedAtom = taggedAtom; + } else if (n < MAX_ARRAY_LOCALS) { + if (n > 1) { + array = fun->u.i.names.array; + } else { + array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array); + if (!array) + return JS_FALSE; + array[0] = fun->u.i.names.taggedAtom; + fun->u.i.names.array = array; + } + if (kind == JSLOCAL_ARG) { + /* + * A destructuring argument pattern adds variables, not arguments, + * so for the following arguments nvars != 0. + */ +#if JS_HAS_DESTRUCTURING + if (fun->u.i.nvars != 0) { + memmove(array + fun->nargs + 1, array + fun->nargs, + fun->u.i.nvars * sizeof *array); + } +#else + JS_ASSERT(fun->u.i.nvars == 0); +#endif + array[fun->nargs] = taggedAtom; + } else { + array[n] = taggedAtom; + } + } else if (n == MAX_ARRAY_LOCALS) { + array = fun->u.i.names.array; + map = (JSLocalNameMap *) JS_malloc(cx, sizeof *map); + if (!map) + return JS_FALSE; + if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(), + NULL, sizeof(JSLocalNameHashEntry), + JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS + * 2))) { + JS_ReportOutOfMemory(cx); + JS_free(cx, map); + return JS_FALSE; + } + + map->lastdup = NULL; + for (i = 0; i != MAX_ARRAY_LOCALS; ++i) { + taggedAtom = array[i]; + if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1), + (i < fun->nargs) + ? JSLOCAL_ARG + : (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR, + (i < fun->nargs) ? i : i - fun->nargs)) { + FreeLocalNameHash(cx, map); + return JS_FALSE; + } + } + if (!HashLocalName(cx, map, atom, kind, *indexp)) { + FreeLocalNameHash(cx, map); + return JS_FALSE; + } + + /* + * At this point the entry is added and we cannot fail. It is time + * to replace fun->u.i.names with the built map. + */ + fun->u.i.names.map = map; + JS_free(cx, array); + } else { + if (*indexp == JS_BITMASK(16)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + (kind == JSLOCAL_ARG) + ? JSMSG_TOO_MANY_FUN_ARGS + : JSMSG_TOO_MANY_FUN_VARS); + return JS_FALSE; + } + if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp)) + return JS_FALSE; } - if (*indexp == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - (kind == JSLOCAL_ARG) - ? JSMSG_TOO_MANY_FUN_ARGS - :JSMSG_TOO_MANY_FUN_VARS); - return JS_FALSE; - } - - /* - * To support duplicate parameter names we force a duplicate node on the - * SCOPE_LAST_PROP(scope) list with the same id, distinguished by the - * SPROP_ALLOW_DUPLICATE flag, and not mapped by an entry in scope. The - * flag is cleared when js_AddNativeProperty finds that the property is - * unique. - */ - if (!js_AddNativeProperty(cx, fun->object, - JSID_HIDE_NAME(ATOM_TO_JSID(atom)), - getter, NULL, SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED | readonly, - SPROP_HAS_SHORTID | SPROP_ALLOW_DUPLICATE, - *indexp)) { - return JS_FALSE; - } - - /* Update the argument of variable counter. */ + /* Update the argument or variable counter. */ ++*indexp; return JS_TRUE; } @@ -2271,58 +2424,99 @@ js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind) JSLocalKind js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp) { - jsid id; - JSScope *scope; - JSScopeProperty *sprop; - JSLocalKind kind; + uintN n, i; + jsuword *array; + JSLocalNameHashEntry *entry; JS_ASSERT(FUN_INTERPRETED(fun)); - JS_ASSERT(OBJ_IS_NATIVE(fun->object)); - kind = JSLOCAL_NONE; - if (fun->nargs + fun->u.i.nvars != 0) { - id = JSID_HIDE_NAME(ATOM_TO_JSID(atom)); + n = fun->nargs + fun->u.i.nvars; + if (n == 0) + return JSLOCAL_NONE; + if (n <= MAX_ARRAY_LOCALS) { + array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; - /* - * Inline js_LookupPropertyWithFlags to avoid prototype chain search - * and the resolve hook invocation. The former is just an optimization - * while the latter is necessary to avoid early creation of lazy slots - * in fun->objects. See comments in fun_resolve for details. - */ - JS_LOCK_OBJ(cx, fun->object); - scope = OBJ_SCOPE(fun->object); - JS_ASSERT(scope->object == fun->object); - sprop = SCOPE_GET_PROPERTY(scope, id); - if (sprop) { - JS_ASSERT(sprop->setter == NULL); - if (sprop->getter == JS_HIDDEN_ARG_GETTER) { - kind = JSLOCAL_ARG; - } else { - JS_ASSERT(sprop->getter == JS_HIDDEN_VAR_GETTER); - kind = (sprop->attrs & JSPROP_READONLY) - ? JSLOCAL_CONST - : JSLOCAL_VAR; + /* Search from the tail to pick up the last duplicated name. */ + i = n; + do { + --i; + if (atom == (JSAtom *) (array[i] & ~1)) { + if (i < fun->nargs) { + if (indexp) + *indexp = i; + return JSLOCAL_ARG; + } + if (indexp) + *indexp = i - fun->nargs; + return (array[i] & 1) ? JSLOCAL_CONST : JSLOCAL_VAR; } + } while (i != 0); + } else { + entry = (JSLocalNameHashEntry *) + JS_DHashTableOperate(&fun->u.i.names.map->names, atom, + JS_DHASH_LOOKUP); + if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) { + JS_ASSERT(entry->localKind != JSLOCAL_NONE); if (indexp) - *indexp = (uint16) sprop->shortid; + *indexp = entry->index; + return (JSLocalKind) entry->localKind; } - JS_UNLOCK_OBJ(cx, fun->object); } - return kind; + return JSLOCAL_NONE; +} + +typedef struct JSGetLocalNamesArgs { + JSFunction *fun; + JSAtom **names; + uint32 *bitmap; +#ifdef DEBUG + uintN nCopiedArgs; + uintN nCopiedVars; +#endif +} JSGetLocalNamesArgs; + +#define SET_BIT32(bitmap, bit) \ + ((bitmap)[(bit) >> JS_BITS_PER_UINT32_LOG2] |= \ + JS_BIT((bit) & (JS_BITS_PER_UINT32 - 1))) + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSLocalNameHashEntry *entry; + JSGetLocalNamesArgs *args; + uint i; + + entry = (JSLocalNameHashEntry *) hdr; + args = (JSGetLocalNamesArgs *) arg; + JS_ASSERT(entry->name); + if (entry->localKind == JSLOCAL_ARG) { + JS_ASSERT(entry->index < args->fun->nargs); + JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs); + i = entry->index; + } else { + JS_ASSERT(entry->localKind == JSLOCAL_VAR || + entry->localKind == JSLOCAL_CONST); + JS_ASSERT(entry->index < args->fun->u.i.nvars); + JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars); + i = args->fun->nargs + entry->index; + } + args->names[i] = entry->name; + if (args->bitmap && entry->localKind != JSLOCAL_VAR) + SET_BIT32(args->bitmap, i); + return JS_DHASH_NEXT; } JSAtom ** js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool, uint32 **bitmap) { - uintN n, index; + uintN n, i; size_t allocsize; JSAtom **names; - JSScopeProperty *sprop; - JSBool setbit; - uint32 bit; -#ifdef DEBUG - uintN nvars = 0, nargs = 0; -#endif + jsuword *array; + JSLocalNameMap *map; + JSGetLocalNamesArgs args; + JSNameIndexPair *dup; JS_ASSERT(FUN_INTERPRETED(fun)); JS_ASSERT(OBJ_IS_NATIVE(fun->object)); @@ -2345,33 +2539,124 @@ js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool, *bitmap = (uint32 *) (names + n); memset(*bitmap, 0, JS_HOWMANY(n, JS_BITS_PER_UINT32) * sizeof(uint32)); } - for (sprop = SCOPE_LAST_PROP(OBJ_SCOPE(fun->object)); - sprop; sprop = sprop->parent) { - if (!JSID_IS_HIDDEN(sprop->id)) - continue; - index = (uint16) sprop->shortid; - if (sprop->getter == JS_HIDDEN_ARG_GETTER) { - JS_ASSERT(nargs++ < fun->nargs); - JS_ASSERT(index < fun->nargs); - setbit = JS_TRUE; - } else { - JS_ASSERT(sprop->getter == JS_HIDDEN_VAR_GETTER); - JS_ASSERT(nvars++ < fun->u.i.nvars); - JS_ASSERT(index < fun->u.i.nvars); - index += fun->nargs; - setbit = (sprop->attrs & JSPROP_READONLY); - } - names[index] = JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); - if (bitmap && setbit) { - bit = JS_BIT(index & (JS_BITS_PER_UINT32 - 1)); - JS_ASSERT(((*bitmap)[index / JS_BITS_PER_UINT32] & bit) == 0); - (*bitmap)[index / JS_BITS_PER_UINT32] |= bit; - } - } -#if !JS_HAS_DESTRUCTURING - JS_ASSERT(nargs == fun->nargs); + + if (n <= MAX_ARRAY_LOCALS) { + array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; + + i = n; + do { + --i; + names[i] = (JSAtom *) (array[i] & ~1); + if (bitmap && + (i < fun->nargs ? array[i] != 0 : array[i] & 1)) { + SET_BIT32(*bitmap, i); + } + } while (i != 0); + } else { + map = fun->u.i.names.map; + args.fun = fun; + args.names = names; + args.bitmap = bitmap ? *bitmap : NULL; +#ifdef DEBUG + args.nCopiedArgs = 0; + args.nCopiedVars = 0; #endif - JS_ASSERT(nvars == fun->u.i.nvars); + JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args); + for (dup = map->lastdup; dup; dup = dup->link) { + JS_ASSERT(dup->index < fun->nargs); + JS_ASSERT(args.nCopiedArgs++ < fun->nargs); + names[dup->index] = dup->name; + if (bitmap) + SET_BIT32(*bitmap, dup->index); + } +#if !JS_HAS_DESTRUCTURING + JS_ASSERT(args.nCopiedArgs == fun->nargs); +#endif + JS_ASSERT(args.nCopiedVars == fun->u.i.nvars); + } + return names; } +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) +{ + JSLocalNameHashEntry *entry; + JSTracer *trc; + + entry = (JSLocalNameHashEntry *) hdr; + JS_ASSERT(entry->name); + trc = (JSTracer *) arg; + JS_SET_TRACING_INDEX(trc, + entry->localKind == JSLOCAL_ARG ? "arg" : "var", + entry->index); + JS_CallTracer(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING); + return JS_DHASH_NEXT; +} + +static void +TraceLocalNames(JSTracer *trc, JSFunction *fun) +{ + uintN n, i; + JSAtom *atom; + jsuword *array; + + JS_ASSERT(FUN_INTERPRETED(fun)); + n = fun->nargs + fun->u.i.nvars; + if (n == 0) + return; + if (n <= MAX_ARRAY_LOCALS) { + array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array; + i = n; + do { + --i; + atom = (JSAtom *) (array[i] & ~1); + if (atom) { + JS_SET_TRACING_INDEX(trc, + i < fun->nargs ? "arg" : "var", + i < fun->nargs ? i : i - fun->nargs); + JS_CallTracer(trc, ATOM_TO_STRING(atom), JSTRACE_STRING); + } + } while (i != 0); + } else { + JS_DHashTableEnumerate(&fun->u.i.names.map->names, + trace_local_names_enumerator, trc); + + /* + * No need to trace the list of duplicates in map->lastdup as the + * names there are traced when enumerating the hash table. + */ + } +} + +void +DestroyLocalNames(JSContext *cx, JSFunction *fun) +{ + uintN n; + + n = fun->nargs + fun->u.i.nvars; + if (n <= 1) + return; + if (n <= MAX_ARRAY_LOCALS) + JS_free(cx, fun->u.i.names.array); + else + FreeLocalNameHash(cx, fun->u.i.names.map); +} + +void +js_FreezeLocalNames(JSContext *cx, JSFunction *fun) +{ + uintN n; + jsuword *array; + + JS_ASSERT(FUN_INTERPRETED(fun)); + JS_ASSERT(!fun->u.i.script); + n = fun->nargs + fun->u.i.nvars; + if (2 <= n && n < MAX_ARRAY_LOCALS) { + /* Shrink over-allocated array ignoring realloc failures. */ + array = JS_realloc(cx, fun->u.i.names.array, n * sizeof *array); + if (array) + fun->u.i.names.array = array; + } +} diff --git a/mozilla/js/src/jsfun.h b/mozilla/js/src/jsfun.h index 5727798ad86..cc3850a6622 100644 --- a/mozilla/js/src/jsfun.h +++ b/mozilla/js/src/jsfun.h @@ -47,26 +47,42 @@ JS_BEGIN_EXTERN_C +typedef struct JSLocalNameMap JSLocalNameMap; + +/* + * Depending on the number of arguments and variables in the function their + * names and attributes are stored either as a single atom or as an array of + * tagged atoms (when there are few locals) or as a hash-based map (when there + * are many locals). In the first 2 cases the lowest bit of the atom is used + * as a tag to distinguish const from var. See jsfun.c for details. + */ +typedef union JSLocalNames { + jsuword taggedAtom; + jsuword *array; + JSLocalNameMap *map; +} JSLocalNames; + struct JSFunction { - JSObject *object; /* back-pointer to GC'ed object header */ - uint16 nargs; /* maximum number of specified arguments, + JSObject *object; /* back-pointer to 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 */ + uint16 flags; /* bound method and other flags, see jsapi.h */ union { struct { - uint16 extra; /* number of arg slots for local GC roots */ - uint16 minargs; /* minimum number of specified arguments, used + uint16 extra; /* number of arg slots for local GC roots */ + uint16 minargs;/* minimum number of specified arguments, used only when calling fast native */ - JSNative native; /* native method pointer or null */ + JSNative native; /* native method pointer or null */ + JSClass *clasp; /* if non-null, constructor for this class */ } n; struct { - uint16 nvars; /* number of local variables */ - uint16 spare; /* reserved for future use */ - JSScript *script; /* interpreted bytecode descriptor or null */ + uint16 nvars; /* number of local variables */ + uint16 spare; /* reserved for future use */ + JSScript *script;/* interpreted bytecode descriptor or null */ + JSLocalNames names; /* argument and variable names */ } i; } u; - JSAtom *atom; /* name for diagnostics and decompiling */ - JSClass *clasp; /* if non-null, constructor for this class */ + JSAtom *atom; /* name for diagnostics and decompiling */ }; #define JSFUN_EXPR_CLOSURE 0x4000 /* expression closure: function(x)x*x */ @@ -219,12 +235,8 @@ extern JSAtom ** js_GetLocalNames(JSContext *cx, JSFunction *fun, JSArenaPool *pool, uint32 **bitmap); -/* - * Pseudo-getter function pointers to distinguish the kind of the hidden - * property. - */ -#define JS_HIDDEN_ARG_GETTER ((JSPropertyOp) sizeof(jsuword)) -#define JS_HIDDEN_VAR_GETTER ((JSPropertyOp) (2 * sizeof(jsuword))) +extern void +js_FreezeLocalNames(JSContext *cx, JSFunction *fun); JS_END_EXTERN_C diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 46db19fc44a..fd5ca45eea5 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -1867,10 +1867,10 @@ js_StrictlyEqual(jsval lval, jsval rval) JSBool js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) { - JSFunction *fun; + JSFunction *fun, *fun2; JSObject *obj, *obj2, *proto, *parent; jsval lval, rval; - JSClass *clasp, *funclasp; + JSClass *clasp; fun = NULL; obj2 = NULL; @@ -1908,9 +1908,9 @@ js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) parent = OBJ_GET_PARENT(cx, obj2); if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { - funclasp = GET_FUNCTION_PRIVATE(cx, obj2)->clasp; - if (funclasp) - clasp = funclasp; + fun2 = GET_FUNCTION_PRIVATE(cx, obj2); + if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp) + clasp = fun2->u.n.clasp; } } obj = js_NewObject(cx, clasp, proto, parent); diff --git a/mozilla/js/src/jsprvtd.h b/mozilla/js/src/jsprvtd.h index 94eda25b5d9..4923d2d2660 100644 --- a/mozilla/js/src/jsprvtd.h +++ b/mozilla/js/src/jsprvtd.h @@ -74,25 +74,11 @@ #define OBJECT_TO_JSID(obj) ((jsid)OBJECT_TO_JSVAL(obj)) #define OBJECT_JSVAL_TO_JSID(v) ((jsid)v) -/* - * To put a property into the hidden subspace we re-tag JSString * behind - * property's atom as JSVAL_BOOLEAN to get a different id. js_TraceId must - * properly trace such pseudo-booleans to ensure GC safety. - */ -#define JSID_IS_HIDDEN(id) (JSVAL_TAG((jsval)(id)) == JSVAL_BOOLEAN) - -#define JSID_HIDE_NAME(id) \ - (JS_ASSERT(JSID_IS_ATOM(id)), \ - (jsid)((jsval)(id) ^ (JSVAL_STRING ^ JSVAL_BOOLEAN))) - -#define JSID_UNHIDE_NAME(id) \ - (JS_ASSERT(JSID_IS_HIDDEN(id)), \ - (jsid)((jsval)(id) ^ (JSVAL_BOOLEAN ^ JSVAL_STRING))) - /* * Convenience constants. */ -#define JS_BITS_PER_UINT32 (sizeof(uint32) * JS_BITS_PER_BYTE) +#define JS_BITS_PER_UINT32_LOG2 5 +#define JS_BITS_PER_UINT32 32 /* Scalar typedefs. */ typedef uint8 jsbytecode; diff --git a/mozilla/js/src/jsscope.c b/mozilla/js/src/jsscope.c index b0f9bcbdced..ea62fdad0ca 100644 --- a/mozilla/js/src/jsscope.c +++ b/mozilla/js/src/jsscope.c @@ -384,11 +384,9 @@ ChangeScope(JSContext *cx, JSScope *scope, int change) } /* - * Take care to exclude the mark and duplicate bits, in case we're called from - * the GC, or we are searching for a property that has not yet been flagged as - * a duplicate when making a duplicate formal parameter. + * Take care to exclude the mark bits in case we're called from the GC. */ -#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_ALLOW_DUPLICATE) +#define SPROP_FLAGS_NOT_MATCHED SPROP_MARK JS_STATIC_DLL_CALLBACK(JSDHashNumber) js_HashScopeProperty(JSDHashTable *table, const void *key) @@ -955,7 +953,7 @@ CheckAncestorLine(JSScope *scope, JSBool sparse) for (sprop = ancestorLine; sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) { - JS_ASSERT(sparse || (sprop->flags & SPROP_ALLOW_DUPLICATE)); + JS_ASSERT(sparse); continue; } ancestorCount++; @@ -1063,51 +1061,39 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, } /* - * Duplicate formal parameters require us to leave the old property - * on the ancestor line, so the decompiler can find it, even though - * its entry in scope->table is overwritten to point at a new property - * descending from the old one. The SPROP_ALLOW_DUPLICATE flag helps - * us cope with the consequent disparity between ancestor line height - * and scope->entryCount. + * If we are clearing sprop to force an existing property to be + * overwritten (apart from a duplicate formal parameter), we must + * unlink it from the ancestor line at scope->lastProp, lazily if + * sprop is not lastProp. And we must remove the entry at *spp, + * precisely so the lazy "middle delete" fixup code further below + * won't find sprop in scope->table, in spite of sprop being on + * the ancestor line. + * + * When we finally succeed in finding or creating a new sprop + * and storing its pointer at *spp, we'll use the |overwriting| + * local saved when we first looked up id to decide whether we're + * indeed creating a new entry, or merely overwriting an existing + * property. */ - if (flags & SPROP_ALLOW_DUPLICATE) { - sprop->flags |= SPROP_ALLOW_DUPLICATE; - } else { + if (sprop == SCOPE_LAST_PROP(scope)) { + do { + SCOPE_REMOVE_LAST_PROP(scope); + if (!SCOPE_HAD_MIDDLE_DELETE(scope)) + break; + sprop = SCOPE_LAST_PROP(scope); + } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); + } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { /* - * If we are clearing sprop to force an existing property to be - * overwritten (apart from a duplicate formal parameter), we must - * unlink it from the ancestor line at scope->lastProp, lazily if - * sprop is not lastProp. And we must remove the entry at *spp, - * precisely so the lazy "middle delete" fixup code further below - * won't find sprop in scope->table, in spite of sprop being on - * the ancestor line. - * - * When we finally succeed in finding or creating a new sprop - * and storing its pointer at *spp, we'll use the |overwriting| - * local saved when we first looked up id to decide whether we're - * indeed creating a new entry, or merely overwriting an existing - * property. + * If we have no hash table yet, we need one now. The middle + * delete code is simple-minded that way! */ - if (sprop == SCOPE_LAST_PROP(scope)) { - do { - SCOPE_REMOVE_LAST_PROP(scope); - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - sprop = SCOPE_LAST_PROP(scope); - } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); - } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { - /* - * If we have no hash table yet, we need one now. The middle - * delete code is simple-minded that way! - */ - if (!scope->table) { - if (!CreateScopeTable(cx, scope, JS_TRUE)) - return NULL; - spp = js_SearchScope(scope, id, JS_TRUE); - sprop = overwriting = SPROP_FETCH(spp); - } - SCOPE_SET_MIDDLE_DELETE(scope); + if (!scope->table) { + if (!CreateScopeTable(cx, scope, JS_TRUE)) + return NULL; + spp = js_SearchScope(scope, id, JS_TRUE); + sprop = overwriting = SPROP_FETCH(spp); } + SCOPE_SET_MIDDLE_DELETE(scope); } /* @@ -1130,8 +1116,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, * scope->lastProp, we may need to fork the property tree and squeeze * all deleted properties out of scope's ancestor line. Otherwise we * risk adding a node with the same id as a "middle" node, violating - * the rule that properties along an ancestor line have distinct ids - * (unless flagged SPROP_ALLOW_DUPLICATE). + * the rule that properties along an ancestor line have distinct ids. */ if (SCOPE_HAD_MIDDLE_DELETE(scope)) { JS_ASSERT(scope->table); @@ -1274,7 +1259,7 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, child.setter = setter; child.slot = slot; child.attrs = attrs; - child.flags = flags & ~SPROP_ALLOW_DUPLICATE; + child.flags = flags; child.shortid = shortid; sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); if (!sprop) @@ -1527,24 +1512,18 @@ PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) JSScopeProperty *sprop; jsid id; size_t n; - const char *name, *prefix; + const char *name; JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter); sprop = (JSScopeProperty *)trc->debugPrintArg; id = sprop->id; name = trc->debugPrintIndex ? js_setter_str : js_getter_str; - if (JSID_IS_ATOM(id) || JSID_IS_HIDDEN(id)) { - if (JSID_IS_HIDDEN(id)) { - id = JSID_UNHIDE_NAME(id); - prefix = "hidden "; - } else { - prefix = ""; - } + if (JSID_IS_ATOM(id)) { n = js_PutEscapedString(buf, bufsize - 1, ATOM_TO_STRING(JSID_TO_ATOM(id)), 0); if (n < bufsize - 1) - JS_snprintf(buf + n, bufsize - n, " %s%s", prefix, name); + JS_snprintf(buf + n, bufsize - n, " %s", name); } else if (JSID_IS_INT(sprop->id)) { JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); } else { @@ -1656,9 +1635,6 @@ DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp) } else { if (JSID_IS_ATOM(sprop->id)) { str = JSVAL_TO_STRING(v); - } else if (JSID_IS_HIDDEN(sprop->id)) { - str = JSVAL_TO_STRING(v); - fputs("hidden ", fp); } else { JSASSERT(JSID_IS_OBJECT(sprop->id)); str = js_ValueToString(cx, v); diff --git a/mozilla/js/src/jsscope.h b/mozilla/js/src/jsscope.h index e997277cf67..55c2a216242 100644 --- a/mozilla/js/src/jsscope.h +++ b/mozilla/js/src/jsscope.h @@ -288,9 +288,8 @@ struct JSScopeProperty { /* Bits stored in sprop->flags. */ #define SPROP_MARK 0x01 -#define SPROP_ALLOW_DUPLICATE 0x02 -#define SPROP_IS_ALIAS 0x04 -#define SPROP_HAS_SHORTID 0x08 +#define SPROP_IS_ALIAS 0x02 +#define SPROP_HAS_SHORTID 0x04 /* * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather @@ -346,8 +345,7 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, extern void js_DestroyScope(JSContext *cx, JSScope *scope); -#define ID_TO_VALUE(id) \ - (JSID_IS_HIDDEN(id) ? (jsval)JSID_UNHIDE_NAME(id) : (jsval)(id)) +#define ID_TO_VALUE(id) ((jsval)(id)) extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index f6449281bac..55b5172d45f 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -1443,6 +1443,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) */ if (fun) { JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); + js_FreezeLocalNames(cx, fun); fun->u.i.script = script; if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) fun->flags |= JSFUN_HEAVYWEIGHT; diff --git a/mozilla/js/src/jsxml.c b/mozilla/js/src/jsxml.c index e6c042b264e..349b77c19ef 100644 --- a/mozilla/js/src/jsxml.c +++ b/mozilla/js/src/jsxml.c @@ -857,7 +857,7 @@ QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * (see below after this function). */ obj = js_NewObject(cx, - JS_ValueToFunction(cx, argv[-2])->clasp, + JS_ValueToFunction(cx, argv[-2])->u.n.clasp, NULL, NULL); if (!obj) return JS_FALSE;