зеркало из https://github.com/mozilla/gecko-dev.git
Bug 399544: using custom storage for function argument and variable names. r,a=brendan
This commit is contained in:
Родитель
36de4db5b5
Коммит
a8fddbd5d6
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
599
js/src/jsfun.c
599
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче