Bug 614493 - Move top-level binding storage (and argument, variable, and upvar counts) out of JSFunction and into js::Bindings, itself stored in JSScript, anticipating the time when strict mode eval scripts will need it. r=brendan

This commit is contained in:
Jeff Walden 2010-11-16 15:34:24 -08:00
parent c12bc82879
commit 1fe4d7a5ad
24 changed files with 863 additions and 666 deletions

View File

@ -96,6 +96,7 @@
#include "jsscopeinlines.h"
#include "jscntxtinlines.h"
#include "jsregexpinlines.h"
#include "jsscriptinlines.h"
#include "jsstrinlines.h"
#include "assembler/wtf/Platform.h"
@ -4241,7 +4242,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
uint32 i = uva->length;
JS_ASSERT(i != 0);
for (Shape::Range r(fun->lastUpvar()); i-- != 0; r.popFront()) {
for (Shape::Range r(fun->script()->bindings.lastUpvar()); i-- != 0; r.popFront()) {
JSObject *obj = parent;
int skip = uva->vector[i].level();
while (--skip > 0) {
@ -4784,6 +4785,7 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
goto out2;
}
}
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
if (!fun)
goto out2;
@ -4792,19 +4794,22 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
MUST_FLOW_THROUGH("out");
Bindings bindings(cx);
for (i = 0; i < nargs; i++) {
argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0);
if (!argAtom) {
fun = NULL;
goto out2;
}
if (!fun->addLocal(cx, argAtom, JSLOCAL_ARG)) {
uint16 dummy;
if (!bindings.addArgument(cx, argAtom, &dummy)) {
fun = NULL;
goto out2;
}
}
if (!Compiler::compileFunctionBody(cx, fun, principals,
if (!Compiler::compileFunctionBody(cx, fun, principals, &bindings,
chars, length, filename, lineno)) {
fun = NULL;
goto out2;

View File

@ -2729,8 +2729,8 @@ class AutoLocalNameArray {
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: context(cx),
mark(JS_ARENA_MARK(&cx->tempPool)),
names(fun->getLocalNameArray(cx, &cx->tempPool)),
count(fun->countLocalNames())
names(fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool)),
count(fun->script()->bindings.countLocalNames())
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}

View File

@ -69,6 +69,7 @@
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsautooplen.h"
@ -1126,14 +1127,14 @@ JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
JS_PUBLIC_API(JSBool)
JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
{
return fun->hasLocalNames();
return fun->script()->bindings.hasLocalNames();
}
extern JS_PUBLIC_API(jsuword *)
JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
{
*markp = JS_ARENA_MARK(&cx->tempPool);
return fun->getLocalNameArray(cx, &cx->tempPool);
return fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
}
extern JS_PUBLIC_API(JSAtom *)

View File

@ -72,6 +72,7 @@
#include "jsatominlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
/* Allocation chunk counts, must be powers of two in general. */
#define BYTECODE_CHUNK 256 /* code allocation increment */
@ -96,6 +97,12 @@ EmitIndexOp(JSContext *cx, JSOp op, uintN index, JSCodeGenerator *cg);
static JSBool
EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op, JSObjectBox *box);
void
JSTreeContext::trace(JSTracer *trc)
{
bindings.trace(trc);
}
JSCodeGenerator::JSCodeGenerator(Parser *parser,
JSArenaPool *cpool, JSArenaPool *npool,
uintN lineno)
@ -1323,10 +1330,10 @@ JSTreeContext::ensureSharpSlots()
if (!sharpArrayAtom || !sharpDepthAtom)
return false;
sharpSlotBase = fun()->u.i.nvars;
if (!fun()->addLocal(cx, sharpArrayAtom, JSLOCAL_VAR))
sharpSlotBase = bindings.countVars();
if (!bindings.addVariable(cx, sharpArrayAtom))
return false;
if (!fun()->addLocal(cx, sharpDepthAtom, JSLOCAL_VAR))
if (!bindings.addVariable(cx, sharpDepthAtom))
return false;
} else {
/*
@ -1714,7 +1721,7 @@ LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
* nor can prop be deleted.
*/
if (cg->inFunction()) {
if (cg->fun()->lookupLocal(cx, atom, NULL) != JSLOCAL_NONE)
if (cg->bindings.hasBinding(atom))
break;
} else {
JS_ASSERT(cg->compileAndGo());
@ -1897,7 +1904,7 @@ AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot)
{
JS_ASSERT((jsuint) slot < cg->maxStackDepth);
if (cg->inFunction()) {
slot += cg->fun()->u.i.nvars;
slot += cg->bindings.countVars();
if ((uintN) slot >= SLOTNO_LIMIT) {
ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
slot = -1;
@ -2015,8 +2022,8 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
JSAtom *atom = pn->pn_atom;
uintN index;
JSLocalKind localKind = fun->lookupLocal(cx, atom, &index);
if (localKind == JSLOCAL_NONE)
BindingKind kind = fun->script()->bindings.lookup(atom, &index);
if (kind == NONE)
return true;
JS_ASSERT(cg->staticLevel > upvarLevel);
@ -2025,7 +2032,7 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
JSAtomListElement *ale = cg->upvarList.lookup(atom);
if (!ale) {
if (cg->inFunction() && !cg->fun()->addLocal(cx, atom, JSLOCAL_UPVAR))
if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
return false;
ale = cg->upvarList.add(cg->parser, atom);
@ -2046,7 +2053,7 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
cg->upvarMap.length = length;
}
if (localKind != JSLOCAL_ARG)
if (kind != ARGUMENT)
index += fun->nargs;
JS_ASSERT(index < JS_BIT(16));
@ -2417,7 +2424,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (ale) {
index = ALE_INDEX(ale);
} else {
if (!cg->fun()->addLocal(cx, atom, JSLOCAL_UPVAR))
if (!cg->bindings.addUpvar(cx, atom))
return JS_FALSE;
ale = cg->upvarList.add(cg->parser, atom);
@ -3869,7 +3876,7 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
if (cg->inFunction() &&
JOF_OPTYPE(pn->pn_op) == JOF_LOCAL &&
pn->pn_cookie.slot() < cg->fun()->u.i.nvars &&
pn->pn_cookie.slot() < cg->bindings.countVars() &&
cg->shouldNoteClosedName(pn))
{
if (!cg->closedVars.append(pn->pn_cookie.slot()))
@ -4740,9 +4747,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
cg2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
(cg->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
cg2->bindings.transfer(cx, &pn->pn_funbox->bindings);
#if JS_HAS_SHARP_VARS
if (cg2->flags & TCF_HAS_SHARPS) {
cg2->sharpSlotBase = fun->sharpSlotBase(cx);
cg2->sharpSlotBase = cg2->bindings.sharpSlotBase(cx);
if (cg2->sharpSlotBase < 0)
return JS_FALSE;
}
@ -4815,13 +4823,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
} else {
#ifdef DEBUG
JSLocalKind localKind =
BindingKind kind =
#endif
cg->fun()->lookupLocal(cx, fun->atom, &slot);
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
cg->bindings.lookup(fun->atom, &slot);
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
JS_ASSERT(index < JS_BIT(20));
pn->pn_index = index;
op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
if (pn->isClosed() &&
!cg->callsEval() &&
!cg->closedVars.append(pn->pn_cookie.slot())) {

View File

@ -332,16 +332,21 @@ struct JSTreeContext { /* tree context for semantic checks */
JSParseNode *innermostWith; /* innermost WITH parse node */
js::Bindings bindings; /* bindings in this code, including
arguments if we're compiling a function */
#ifdef JS_SCOPE_DEPTH_METER
uint16 scopeDepth; /* current lexical scope chain depth */
uint16 maxScopeDepth; /* maximum lexical scope chain depth */
#endif
void trace(JSTracer *trc);
JSTreeContext(js::Parser *prs)
: flags(0), bodyid(0), blockidGen(0),
topStmt(NULL), topScopeStmt(NULL), blockChainBox(NULL), blockNode(NULL),
parser(prs), scopeChain_(NULL), parent(prs->tc), staticLevel(0),
funbox(NULL), functionList(NULL), innermostWith(NULL), sharpSlotBase(-1)
: flags(0), bodyid(0), blockidGen(0), topStmt(NULL), topScopeStmt(NULL),
blockChainBox(NULL), blockNode(NULL), parser(prs), scopeChain_(NULL), parent(prs->tc),
staticLevel(0), funbox(NULL), functionList(NULL), innermostWith(NULL), bindings(prs->context),
sharpSlotBase(-1)
{
prs->tc = this;
JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0);

View File

@ -90,6 +90,7 @@
#include "jsfuninlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
using namespace js;
using namespace js::gc;
@ -371,15 +372,12 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
wfunobj->setPrivate(wfun);
wfun->nargs = fun->nargs;
wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
wfun->u.i.nvars = fun->u.i.nvars;
wfun->u.i.nupvars = fun->u.i.nupvars;
wfun->u.i.skipmin = fun->u.i.skipmin;
wfun->u.i.wrapper = true;
wfun->u.i.script = NULL;
wfun->u.i.names = fun->u.i.names;
wfun->atom = fun->atom;
JSScript *script = fun->u.i.script;
JSScript *script = fun->script();
jssrcnote *snbase = script->notes();
jssrcnote *sn = snbase;
while (!SN_IS_TERMINATOR(sn))
@ -392,7 +390,7 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
JSScript::isValidOffset(script->objectsOffset)
? script->objects()->length
: 0,
fun->u.i.nupvars,
script->bindings.countUpvars(),
JSScript::isValidOffset(script->regexpsOffset)
? script->regexps()->length
: 0,
@ -435,10 +433,10 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
if (script->nClosedArgs + script->nClosedVars != 0)
script->copyClosedSlotsTo(wscript);
if (wfun->u.i.nupvars != 0) {
JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length);
if (script->bindings.hasUpvars()) {
JS_ASSERT(script->bindings.countUpvars() == wscript->upvars()->length);
memcpy(wscript->upvars()->vector, script->upvars()->vector,
wfun->u.i.nupvars * sizeof(uint32));
script->bindings.countUpvars() * sizeof(uint32));
}
jsbytecode *pc = wscript->code;
@ -494,6 +492,8 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
wscript->owner = script->owner;
#endif
wscript->bindings.clone(cx, &script->bindings);
/* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
wfun->u.i.script = wscript;
@ -944,8 +944,10 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
static JSObject *
NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee)
{
size_t vars = fun->countArgsAndVars();
size_t slots = JSObject::CALL_RESERVED_SLOTS + vars;
Bindings &bindings = fun->script()->bindings;
size_t argsVars = bindings.countArgsAndVars();
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
JSObject *callobj = js_NewGCObject(cx, kind);
@ -954,10 +956,10 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca
/* Init immediately to avoid GC seeing a half-init'ed object. */
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
callobj->setMap(fun->u.i.names);
callobj->setMap(bindings.lastShape());
/* This must come after callobj->lastProp has been set. */
if (!callobj->ensureInstanceReservedSlots(cx, vars))
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
return NULL;
#ifdef DEBUG
@ -1076,15 +1078,19 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
uintN n = fun->countArgsAndVars();
Bindings &bindings = fun->script()->bindings;
uintN n = bindings.countArgsAndVars();
if (n != 0) {
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
uint32 nargs = fun->nargs;
uint32 nvars = fun->u.i.nvars;
uint32 nvars = bindings.countVars();
uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->u.i.script;
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
@ -1096,7 +1102,6 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
* For each arg & var that is closed over, copy it from the stack
* into the call object.
*/
JSScript *script = fun->u.i.script;
uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i);
@ -1165,16 +1170,18 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, Value *vp,
JSObject &callee = obj->getCallObjCallee();
#ifdef DEBUG
JSFunction *callee_fun = (JSFunction *) callee.getPrivate();
JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun));
JS_ASSERT(i < callee_fun->u.i.nupvars);
JSFunction *calleeFun = callee.getFunctionPrivate();
JS_ASSERT(calleeFun->isFlatClosure());
JS_ASSERT(calleeFun->script()->bindings.countUpvars() == calleeFun->script()->upvars()->length);
JS_ASSERT(i < calleeFun->script()->bindings.countUpvars());
#endif
array = callee.getFlatClosureUpvars();
} else {
JSFunction *fun = obj->getCallObjCalleeFunction();
JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->script()->bindings.countVars());
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
@ -1314,17 +1321,14 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
if (!JSID_IS_ATOM(id))
return JS_TRUE;
#ifdef DEBUG
JSFunction *fun = obj->getCallObjCalleeFunction();
JS_ASSERT(fun->lookupLocal(cx, JSID_TO_ATOM(id), NULL) == JSLOCAL_NONE);
#endif
JS_ASSERT(!obj->getCallObjCalleeFunction()->script()->bindings.hasBinding(JSID_TO_ATOM(id)));
/*
* Resolve arguments so that we never store a particular Call object's
* arguments object reference in a Call prototype's |arguments| slot.
*
* Include JSPROP_ENUMERATE for consistency with all other Call object
* properties; see JSFunction::addLocal and js::Interpret's JSOP_DEFFUN
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
* rebinding-Call-property logic.
*/
if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
@ -1356,7 +1360,7 @@ call_trace(JSTracer *trc, JSObject *obj)
* hiding hack.
*/
uintN first = JSObject::CALL_RESERVED_SLOTS;
uintN count = fp->fun()->countArgsAndVars();
uintN count = fp->fun()->script()->bindings.countArgsAndVars();
JS_ASSERT(obj->numSlots() >= first + count);
SetValueRangeToUndefined(obj->getSlots() + first, count);
@ -1792,9 +1796,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
uint32 firstword; /* flag telling whether fun->atom is non-null,
plus for fun->u.i.skipmin, fun->u.i.wrapper,
and 14 bits reserved for future use */
uintN nargs, nvars, nupvars, n;
uint32 localsword; /* word for argument and variable counts */
uint32 flagsword; /* word for fun->u.i.nupvars and fun->flags */
uint32 flagsword; /* word for argument count and fun->flags */
cx = xdr->cx;
if (xdr->mode == JSXDR_ENCODE) {
@ -1815,20 +1817,13 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
}
JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
nargs = fun->nargs;
nvars = fun->u.i.nvars;
nupvars = fun->u.i.nupvars;
localsword = (nargs << 16) | nvars;
flagsword = (nupvars << 16) | fun->flags;
flagsword = (fun->nargs << 16) | fun->flags;
} else {
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
if (!fun)
return false;
FUN_OBJECT(fun)->clearParent();
FUN_OBJECT(fun)->clearProto();
#ifdef __GNUC__
nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */
#endif
}
AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
@ -1837,122 +1832,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
return false;
if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
return false;
if (!JS_XDRUint32(xdr, &localsword) ||
!JS_XDRUint32(xdr, &flagsword)) {
if (!JS_XDRUint32(xdr, &flagsword))
return false;
}
if (xdr->mode == JSXDR_DECODE) {
nargs = localsword >> 16;
nvars = uint16(localsword);
fun->nargs = flagsword >> 16;
JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
nupvars = flagsword >> 16;
fun->flags = uint16(flagsword);
fun->u.i.skipmin = uint16(firstword >> 2);
fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
}
/* do arguments and local vars */
n = nargs + nvars + nupvars;
if (n != 0) {
void *mark;
uintN i;
uintN bitmapLength;
uint32 *bitmap;
jsuword *names;
JSAtom *name;
JSLocalKind localKind;
bool ok = true;
mark = JS_ARENA_MARK(&xdr->cx->tempPool);
/*
* From this point the control must flow via the label release_mark.
*
* To xdr the names we prefix the names with a bitmap descriptor and
* then xdr the names as strings. For argument names (indexes below
* nargs) the corresponding bit in the bitmap is unset when the name
* is null. Such null names are not encoded or decoded. For variable
* names (indexes starting from nargs) bitmap's bit is set when the
* name is declared as const, not as ordinary var.
* */
MUST_FLOW_THROUGH("release_mark");
bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
bitmapLength * sizeof *bitmap);
if (!bitmap) {
js_ReportOutOfScriptQuota(xdr->cx);
ok = false;
goto release_mark;
}
if (xdr->mode == JSXDR_ENCODE) {
names = fun->getLocalNameArray(xdr->cx, &xdr->cx->tempPool);
if (!names) {
ok = false;
goto release_mark;
}
PodZero(bitmap, bitmapLength);
for (i = 0; i != n; ++i) {
if (i < fun->nargs
? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
: JS_LOCAL_NAME_IS_CONST(names[i])) {
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
}
}
}
#ifdef __GNUC__
else {
names = NULL; /* quell GCC uninitialized warning */
}
#endif
for (i = 0; i != bitmapLength; ++i) {
ok = !!JS_XDRUint32(xdr, &bitmap[i]);
if (!ok)
goto release_mark;
}
for (i = 0; i != n; ++i) {
if (i < nargs &&
!(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
if (xdr->mode == JSXDR_DECODE) {
ok = !!fun->addLocal(xdr->cx, NULL, JSLOCAL_ARG);
if (!ok)
goto release_mark;
} else {
JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
}
continue;
}
if (xdr->mode == JSXDR_ENCODE)
name = JS_LOCAL_NAME_TO_ATOM(names[i]);
ok = !!js_XDRAtom(xdr, &name);
if (!ok)
goto release_mark;
if (xdr->mode == JSXDR_DECODE) {
localKind = (i < nargs)
? JSLOCAL_ARG
: (i < nargs + nvars)
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
? JSLOCAL_CONST
: JSLOCAL_VAR)
: JSLOCAL_UPVAR;
ok = !!fun->addLocal(xdr->cx, name, localKind);
if (!ok)
goto release_mark;
}
}
release_mark:
JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
if (!ok)
return false;
if (xdr->mode == JSXDR_DECODE)
fun->freezeLocalNames(cx);
}
if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
return false;
@ -1961,6 +1851,7 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
#ifdef CHECK_SCRIPT_OWNER
fun->script()->owner = NULL;
#endif
JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
js_CallNewScriptHook(cx, fun->script(), fun);
}
@ -2018,69 +1909,40 @@ fun_trace(JSTracer *trc, JSObject *obj)
MarkObject(trc, *fun, "private");
/* The function could be a flat closure with upvar copies in the clone. */
if (FUN_FLAT_CLOSURE(fun) && fun->u.i.nupvars)
MarkValueRange(trc, fun->u.i.nupvars, obj->getFlatClosureUpvars(), "upvars");
if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) {
MarkValueRange(trc, fun->script()->bindings.countUpvars(),
obj->getFlatClosureUpvars(), "upvars");
}
return;
}
if (fun->atom)
MarkString(trc, ATOM_TO_STRING(fun->atom), "atom");
if (FUN_INTERPRETED(fun)) {
if (fun->u.i.script)
js_TraceScript(trc, fun->u.i.script);
for (const Shape *shape = fun->u.i.names; shape; shape = shape->previous())
shape->trace(trc);
}
if (fun->isInterpreted() && fun->script())
js_TraceScript(trc, fun->script());
}
static void
fun_finalize(JSContext *cx, JSObject *obj)
{
/* Ignore newborn function objects. */
JSFunction *fun = (JSFunction *) obj->getPrivate();
JSFunction *fun = obj->getFunctionPrivate();
if (!fun)
return;
/* Cloned function objects may be flat closures with upvars to free. */
if (fun != obj) {
if (FUN_FLAT_CLOSURE(fun) && fun->u.i.nupvars != 0)
if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars())
cx->free((void *) obj->getFlatClosureUpvars());
return;
}
/*
* Null-check of u.i.script is required since the parser sets interpreted
* very early.
* Null-check fun->script() because the parser sets interpreted very early.
*/
if (FUN_INTERPRETED(fun) && fun->u.i.script)
js_DestroyScriptFromGC(cx, fun->u.i.script);
}
int
JSFunction::sharpSlotBase(JSContext *cx)
{
#if JS_HAS_SHARP_VARS
JSAtom *name = js_Atomize(cx, "#array", 6, 0);
if (name) {
uintN index = uintN(-1);
#ifdef DEBUG
JSLocalKind kind =
#endif
lookupLocal(cx, name, &index);
JS_ASSERT(kind == JSLOCAL_VAR);
return int(index);
}
#endif
return -1;
}
uint32
JSFunction::countUpvarSlots() const
{
JS_ASSERT(FUN_INTERPRETED(this));
return (u.i.nupvars == 0) ? 0 : u.i.script->upvars()->length;
if (fun->isInterpreted() && fun->script())
js_DestroyScriptFromGC(cx, fun->script());
}
/*
@ -2531,6 +2393,8 @@ Function(JSContext *cx, uintN argc, Value *vp)
return JS_FALSE;
}
Bindings bindings(cx);
Value *argv = vp + 2;
uintN n = argc ? argc - 1 : 0;
if (n > 0) {
@ -2635,7 +2499,7 @@ Function(JSContext *cx, uintN argc, Value *vp)
JSAtom *atom = ts.currentToken().t_atom;
/* Check for a duplicate parameter name. */
if (fun->lookupLocal(cx, atom, NULL) != JSLOCAL_NONE) {
if (bindings.hasBinding(atom)) {
JSAutoByteString name;
if (!js_AtomToPrintableString(cx, atom, &name)) {
state = BAD;
@ -2644,11 +2508,16 @@ Function(JSContext *cx, uintN argc, Value *vp)
if (!ReportCompileErrorNumber(cx, &ts, NULL,
JSREPORT_WARNING | JSREPORT_STRICT,
JSMSG_DUPLICATE_FORMAL, name.ptr())) {
state = BAD;
goto after_args;
}
}
if (!fun->addLocal(cx, atom, JSLOCAL_ARG))
uint16 dummy;
if (!bindings.addArgument(cx, atom, &dummy)) {
state = BAD;
goto after_args;
}
/*
* Get the next token. Stop on end of stream. Otherwise
@ -2681,10 +2550,10 @@ Function(JSContext *cx, uintN argc, Value *vp)
JSString *str;
if (argc) {
str = js_ValueToString(cx, argv[argc-1]);
str = js_ValueToString(cx, argv[argc - 1]);
if (!str)
return JS_FALSE;
argv[argc-1].setString(str);
argv[argc - 1].setString(str);
} else {
str = cx->runtime->emptyString;
}
@ -2693,8 +2562,9 @@ Function(JSContext *cx, uintN argc, Value *vp)
const jschar *chars = str->getChars(cx);
if (!chars)
return JS_FALSE;
return Compiler::compileFunctionBody(cx, fun, principals, chars, length,
filename, lineno);
return Compiler::compileFunctionBody(cx, fun, principals, &bindings,
chars, length, filename, lineno);
}
static JSBool
@ -2768,12 +2638,9 @@ js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
JS_ASSERT(!native);
JS_ASSERT(nargs == 0);
fun->u.i.nvars = 0;
fun->u.i.nupvars = 0;
fun->u.i.skipmin = 0;
fun->u.i.wrapper = false;
fun->u.i.script = NULL;
fun->u.i.names = cx->runtime->emptyCallShape;
} else {
fun->u.n.clasp = NULL;
if (flags & JSFUN_TRCINFO) {
@ -2860,16 +2727,17 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CloneFunctionObject, CONTEXT, FUNCTION,
JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
{
JS_ASSERT(FUN_FLAT_CLOSURE(fun));
JS_ASSERT((JSScript::isValidOffset(fun->u.i.script->upvarsOffset)
? fun->u.i.script->upvars()->length
: 0) == fun->u.i.nupvars);
JS_ASSERT(fun->isFlatClosure());
JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
fun->script()->bindings.hasUpvars());
JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
JSObject *closure = CloneFunctionObject(cx, fun, scopeChain);
if (!closure)
return closure;
uint32 nslots = fun->countUpvarSlots();
uint32 nslots = fun->script()->bindings.countUpvars();
if (nslots == 0)
return closure;
@ -2896,12 +2764,12 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
return NULL;
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
if (!closure || fun->u.i.nupvars == 0)
if (!closure || !fun->script()->bindings.hasUpvars())
return closure;
Value *upvars = closure->getFlatClosureUpvars();
uintN level = fun->u.i.script->staticLevel;
JSUpvarArray *uva = fun->u.i.script->upvars();
JSUpvarArray *uva = fun->script()->upvars();
for (uint32 i = 0, n = uva->length; i < n; i++)
upvars[i] = GetUpvar(cx, level, uva->vector[i]);
@ -3073,215 +2941,3 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
}
const Shape *
JSFunction::lastArg() const
{
const Shape *shape = lastVar();
if (u.i.nvars != 0) {
while (shape->previous() && shape->getter() != GetCallArg)
shape = shape->previous();
}
return shape;
}
const Shape *
JSFunction::lastVar() const
{
const Shape *shape = u.i.names;
if (u.i.nupvars != 0) {
while (shape->getter() == GetFlatUpvar)
shape = shape->previous();
}
return shape;
}
bool
JSFunction::addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind)
{
JS_ASSERT(FUN_INTERPRETED(this));
JS_ASSERT(!u.i.script);
/*
* We still follow 10.2.3 of ES3 and make argument and variable properties
* of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
* avoid objects as activations, something we should do too.
*/
uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
uint16 *indexp;
PropertyOp getter, setter;
uint32 slot = JSObject::CALL_RESERVED_SLOTS;
if (kind == JSLOCAL_ARG) {
JS_ASSERT(u.i.nupvars == 0);
indexp = &nargs;
getter = GetCallArg;
setter = SetCallArg;
slot += nargs;
} else if (kind == JSLOCAL_UPVAR) {
indexp = &u.i.nupvars;
getter = GetFlatUpvar;
setter = SetFlatUpvar;
slot = SHAPE_INVALID_SLOT;
} else {
JS_ASSERT(u.i.nupvars == 0);
indexp = &u.i.nvars;
getter = GetCallVar;
setter = SetCallVar;
if (kind == JSLOCAL_CONST)
attrs |= JSPROP_READONLY;
else
JS_ASSERT(kind == JSLOCAL_VAR);
slot += nargs + u.i.nvars;
}
if (*indexp == JS_BITMASK(16)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
(kind == JSLOCAL_ARG)
? JSMSG_TOO_MANY_FUN_ARGS
: JSMSG_TOO_MANY_LOCALS);
return false;
}
jsid id;
if (!atom) {
/* Destructuring formal parameter: use argument index as id. */
JS_ASSERT(kind == JSLOCAL_ARG);
id = INT_TO_JSID(nargs);
} else {
id = ATOM_TO_JSID(atom);
}
Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp);
Shape *shape = u.i.names->getChild(cx, child, &u.i.names);
if (!shape)
return false;
JS_ASSERT(u.i.names == shape);
++*indexp;
return true;
}
JSLocalKind
JSFunction::lookupLocal(JSContext *cx, JSAtom *atom, uintN *indexp)
{
JS_ASSERT(FUN_INTERPRETED(this));
Shape *shape = SHAPE_FETCH(Shape::search(&u.i.names, ATOM_TO_JSID(atom)));
if (shape) {
JSLocalKind localKind;
if (shape->getter() == GetCallArg)
localKind = JSLOCAL_ARG;
else if (shape->getter() == GetFlatUpvar)
localKind = JSLOCAL_UPVAR;
else if (!shape->writable())
localKind = JSLOCAL_CONST;
else
localKind = JSLOCAL_VAR;
if (indexp)
*indexp = shape->shortid;
return localKind;
}
return JSLOCAL_NONE;
}
jsuword *
JSFunction::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
{
JS_ASSERT(hasLocalNames());
uintN n = countLocalNames();
jsuword *names;
/*
* No need to check for overflow of the allocation size as we are making a
* copy of already allocated data. As such it must fit size_t.
*/
JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names);
if (!names) {
js_ReportOutOfScriptQuota(cx);
return NULL;
}
#ifdef DEBUG
for (uintN i = 0; i != n; i++)
names[i] = 0xdeadbeef;
#endif
for (Shape::Range r = u.i.names; !r.empty(); r.popFront()) {
const Shape &shape = r.front();
uintN index = uint16(shape.shortid);
jsuword constFlag = 0;
if (shape.getter() == GetCallArg) {
JS_ASSERT(index < nargs);
} else if (shape.getter() == GetFlatUpvar) {
JS_ASSERT(index < u.i.nupvars);
index += nargs + u.i.nvars;
} else {
JS_ASSERT(index < u.i.nvars);
index += nargs;
if (!shape.writable())
constFlag = 1;
}
JSAtom *atom;
if (JSID_IS_ATOM(shape.id)) {
atom = JSID_TO_ATOM(shape.id);
} else {
JS_ASSERT(JSID_IS_INT(shape.id));
JS_ASSERT(shape.getter() == GetCallArg);
atom = NULL;
}
names[index] = jsuword(atom);
}
#ifdef DEBUG
for (uintN i = 0; i != n; i++)
JS_ASSERT(names[i] != 0xdeadbeef);
#endif
return names;
}
void
JSFunction::freezeLocalNames(JSContext *cx)
{
JS_ASSERT(FUN_INTERPRETED(this));
Shape *shape = u.i.names;
if (shape->inDictionary()) {
do {
JS_ASSERT(!shape->frozen());
shape->setFrozen();
} while ((shape = shape->parent) != NULL);
}
}
/*
* This method is called only if we parsed a duplicate formal. Let's use the
* simplest possible algorithm, risking O(n^2) pain -- anyone dup'ing formals
* is asking for it!
*/
JSAtom *
JSFunction::findDuplicateFormal() const
{
JS_ASSERT(isInterpreted());
if (nargs <= 1)
return NULL;
for (Shape::Range r = lastArg(); !r.empty(); r.popFront()) {
const Shape &shape = r.front();
for (Shape::Range r2 = shape.previous(); !r2.empty(); r2.popFront()) {
if (r2.front().id == shape.id)
return JSID_TO_ATOM(shape.id);
}
}
return NULL;
}

View File

@ -46,6 +46,7 @@
#include "jspubtd.h"
#include "jsobj.h"
#include "jsatom.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsopcode.h"
@ -113,29 +114,6 @@
JS_ASSERT((fun)->flags & JSFUN_TRCINFO), \
fun->u.n.trcinfo)
/*
* Formal parameters, local variables, and upvars are stored in a shape tree
* path with its latest node at fun->u.i.names. The addLocal, lookupLocal, and
* getLocalNameArray methods abstract away this detail.
*
* The lastArg, lastVar, and lastUpvar JSFunction methods provide more direct
* access to the shape path. These methods may be used to make a Shape::Range
* for iterating over the relevant shapes from youngest to oldest (i.e., last
* or right-most to first or left-most in source order).
*
* Sometimes iteration order must be from oldest to youngest, however. For such
* cases, use getLocalNameArray. The RAII helper class js::AutoLocalNameArray,
* defined in jscntxt.h, should be used where possible instead of direct calls
* to getLocalNameArray.
*/
enum JSLocalKind {
JSLOCAL_NONE,
JSLOCAL_ARG,
JSLOCAL_VAR,
JSLOCAL_CONST,
JSLOCAL_UPVAR
};
struct JSFunction : public JSObject_Slots2
{
/* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */
@ -152,9 +130,6 @@ struct JSFunction : public JSObject_Slots2
} n;
struct Scripted {
JSScript *script; /* interpreted bytecode descriptor or null */
uint16 nvars; /* number of local variables */
uint16 nupvars; /* number of upvars (computable from script
but here for faster access) */
uint16 skipmin; /* net skip amount up (toward zero) from
script->staticLevel to nearest upvar,
including upvars in nested functions */
@ -183,84 +158,19 @@ struct JSFunction : public JSObject_Slots2
/* Returns the strictness of this function, which must be interpreted. */
inline bool inStrictMode() const;
uintN countVars() const {
JS_ASSERT(FUN_INTERPRETED(this));
return u.i.nvars;
void setArgCount(uint16 nargs) {
JS_ASSERT(this->nargs == 0);
this->nargs = nargs;
}
/* uint16 representation bounds number of call object dynamic slots. */
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
uintN countArgsAndVars() const {
JS_ASSERT(FUN_INTERPRETED(this));
return nargs + u.i.nvars;
}
uintN countLocalNames() const {
JS_ASSERT(FUN_INTERPRETED(this));
return countArgsAndVars() + u.i.nupvars;
}
bool hasLocalNames() const {
JS_ASSERT(FUN_INTERPRETED(this));
return countLocalNames() != 0;
}
int sharpSlotBase(JSContext *cx);
uint32 countUpvarSlots() const;
const js::Shape *lastArg() const;
const js::Shape *lastVar() const;
const js::Shape *lastUpvar() const { return u.i.names; }
/*
* The parser builds shape paths for functions, usable by Call objects at
* runtime, by calling addLocal. All locals of ARG kind must be addLocal'ed
* before any VAR kind, and VAR before UPVAR.
*/
bool addLocal(JSContext *cx, JSAtom *atom, JSLocalKind kind);
/*
* Look up an argument or variable name returning its kind when found or
* JSLOCAL_NONE when no such name exists. When indexp is not null and the
* name exists, *indexp will receive the index of the corresponding
* argument or variable.
*/
JSLocalKind lookupLocal(JSContext *cx, JSAtom *atom, uintN *indexp);
/*
* Function and macros to work with local names as an array of words.
* getLocalNameArray returns the array, or null if we are out of memory.
* This function must be called only when fun->hasLocalNames().
*
* The supplied pool is used to allocate the returned array, so the caller
* is obligated to mark and release to free it.
*
* The elements of the array with index less than fun->nargs correspond to
* the names of function formal parameters. An index >= fun->nargs
* addresses a var binding. Use JS_LOCAL_NAME_TO_ATOM to convert array's
* element to an atom pointer. This pointer can be null when the element is
* for a formal parameter corresponding to a destructuring pattern.
*
* If nameWord does not name a formal parameter, use JS_LOCAL_NAME_IS_CONST
* to check if nameWord corresponds to the const declaration.
*/
jsuword *getLocalNameArray(JSContext *cx, struct JSArenaPool *pool);
void freezeLocalNames(JSContext *cx);
/*
* If fun's formal parameters include any duplicate names, return one
* of them (chosen arbitrarily). If they are all unique, return NULL.
*/
JSAtom *findDuplicateFormal() const;
#define JS_LOCAL_NAME_TO_ATOM(nameWord) ((JSAtom *) ((nameWord) & ~(jsuword) 1))
#define JS_LOCAL_NAME_IS_CONST(nameWord) ((((nameWord) & (jsuword) 1)) != 0)
bool mightEscape() const {
return FUN_INTERPRETED(this) && (FUN_FLAT_CLOSURE(this) || u.i.nupvars == 0);
return isInterpreted() && (isFlatClosure() || !script()->bindings.hasUpvars());
}
bool joinable() const {
@ -606,12 +516,18 @@ GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
} // namespace js
extern JSBool

View File

@ -974,7 +974,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
if (prev && prev->script()->hasSharps) {
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
? prev->fun()->sharpSlotBase(cx)
? prev->fun()->script()->bindings.sharpSlotBase(cx)
: prev->numFixed() - SHARP_NSLOTS;
if (base < 0)
return false;
@ -5301,7 +5301,7 @@ BEGIN_CASE(JSOP_CALLUPVAR_DBG)
if (!names)
goto error;
uintN index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
uintN index = fun->script()->bindings.countArgsAndVars() + GET_UINT16(regs.pc);
atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
id = ATOM_TO_JSID(atom);
@ -5332,7 +5332,7 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
uintN index = GET_UINT16(regs.pc);
JSObject *obj = &argv[-2].toObject();
JS_ASSERT(index < obj->getFunctionPrivate()->u.i.nupvars);
JS_ASSERT(index < obj->getFunctionPrivate()->script()->bindings.countUpvars());
PUSH_COPY(obj->getFlatClosureUpvar(index));
if (op == JSOP_CALLFCSLOT)
PUSH_UNDEFINED();

View File

@ -465,14 +465,14 @@ struct JSObject : js::gc::Cell {
bool hasOwnShape() const { return !!(flags & OWN_SHAPE); }
void setMap(JSObjectMap *amap) {
void setMap(const JSObjectMap *amap) {
JS_ASSERT(!hasOwnShape());
map = amap;
map = const_cast<JSObjectMap *>(amap);
objShape = map->shape;
}
void setSharedNonNativeMap() {
setMap(const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative));
setMap(&JSObjectMap::sharedNonNative);
}
void deletingShapeChange(JSContext *cx, const js::Shape &shape);

View File

@ -493,6 +493,7 @@ JSObject::getFlatClosureUpvars() const
inline js::Value
JSObject::getFlatClosureUpvar(uint32 i) const
{
JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
return getFlatClosureUpvars()[i];
}

View File

@ -832,8 +832,8 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
jp->pcstack = NULL;
jp->fun = fun;
jp->localNames = NULL;
if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) {
jp->localNames = fun->getLocalNameArray(cx, &jp->pool);
if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) {
jp->localNames = fun->script()->bindings.getLocalNameArray(cx, &jp->pool);
if (!jp->localNames) {
js_DestroyPrinter(jp);
return NULL;
@ -1340,7 +1340,7 @@ GetArgOrVarAtom(JSPrinter *jp, uintN slot)
JSAtom *name;
LOCAL_ASSERT_RV(jp->fun, NULL);
LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL);
LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL);
name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
#if !JS_HAS_DESTRUCTURING
LOCAL_ASSERT_RV(name, NULL);
@ -2895,12 +2895,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
jp->fun = jp->script->getFunction(0);
}
if (!jp->localNames)
jp->localNames = jp->fun->getLocalNameArray(cx, &jp->pool);
if (!jp->localNames) {
JS_ASSERT(fun == jp->fun);
jp->localNames =
jp->fun->script()->bindings.getLocalNameArray(cx, &jp->pool);
}
uintN index = GET_UINT16(pc);
if (index < jp->fun->u.i.nupvars) {
index += jp->fun->countArgsAndVars();
if (index < jp->fun->script()->bindings.countUpvars()) {
index += jp->fun->script()->bindings.countArgsAndVars();
} else {
JSUpvarArray *uva;
#ifdef DEBUG
@ -2919,8 +2922,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
fp = fp->prev();
JS_ASSERT(fp->script() == jp->script);
JS_ASSERT(fp->prev()->fun() == jp->fun);
JS_ASSERT(FUN_INTERPRETED(jp->fun));
JS_ASSERT(jp->script != jp->fun->u.i.script);
JS_ASSERT(jp->fun->isInterpreted());
JS_ASSERT(jp->script != jp->fun->script());
JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
}
#endif
@ -4097,14 +4100,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* release to mark before returning.
*/
mark = JS_ARENA_MARK(&cx->tempPool);
if (!fun->hasLocalNames()) {
innerLocalNames = NULL;
} else {
innerLocalNames = fun->getLocalNameArray(cx, &cx->tempPool);
if (fun->script()->bindings.hasLocalNames()) {
innerLocalNames =
fun->script()->bindings.getLocalNameArray(cx, &cx->tempPool);
if (!innerLocalNames)
return NULL;
} else {
innerLocalNames = NULL;
}
inner = fun->u.i.script;
inner = fun->script();
if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
JS_ARENA_RELEASE(&cx->tempPool, mark);
return NULL;

View File

@ -92,6 +92,7 @@
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsregexpinlines.h"
#include "jsscriptinlines.h"
// Grr, windows.h or something under it #defines CONST...
#ifdef CONST
@ -243,6 +244,7 @@ Parser::newObjectBox(JSObject *obj)
traceListHead = objbox;
objbox->emitLink = NULL;
objbox->object = obj;
objbox->isFunctionBox = false;
return objbox;
}
@ -268,6 +270,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
traceListHead = funbox;
funbox->emitLink = NULL;
funbox->object = obj;
funbox->isFunctionBox = true;
funbox->node = fn;
funbox->siblings = tc->functionList;
tc->functionList = funbox;
@ -275,6 +278,7 @@ Parser::newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc)
funbox->kids = NULL;
funbox->parent = tc->funbox;
funbox->methods = NULL;
new (&funbox->bindings) Bindings(context);
funbox->queued = false;
funbox->inLoop = false;
for (JSStmtInfo *stmt = tc->topStmt; stmt; stmt = stmt->down) {
@ -315,8 +319,13 @@ Parser::trace(JSTracer *trc)
JSObjectBox *objbox = traceListHead;
while (objbox) {
MarkObject(trc, *objbox->object, "parser.object");
if (objbox->isFunctionBox)
static_cast<JSFunctionBox *>(objbox)->bindings.trace(trc);
objbox = objbox->traceLink;
}
for (JSTreeContext *tc = this->tc; tc; tc = tc->parent)
tc->trace(trc);
}
static void
@ -1314,13 +1323,10 @@ static bool
CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
JSParseNode *pn)
{
JSAtom *atom;
if (!tc->needStrictChecks())
return true;
atom = fun->findDuplicateFormal();
if (atom) {
if (JSAtom *atom = tc->bindings.findDuplicateArgument()) {
/*
* We have found a duplicate parameter name. If we can find the
* JSDefinition for the argument, that will have a more accurate source
@ -1339,7 +1345,9 @@ CheckStrictFormals(JSContext *cx, JSTreeContext *tc, JSFunction *fun,
if (tc->flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_PARAM_EVAL)) {
JSAtomState *atoms = &cx->runtime->atomState;
atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS) ? atoms->argumentsAtom : atoms->evalAtom;
JSAtom *atom = (tc->flags & TCF_FUN_PARAM_ARGUMENTS)
? atoms->argumentsAtom
: atoms->evalAtom;
/* The definition's source position will be more precise. */
JSDefinition *dn = ALE_DEFN(tc->decls.lookup(atom));
@ -1643,7 +1651,7 @@ DefineArg(JSParseNode *pn, JSAtom *atom, uintN i, JSTreeContext *tc)
*/
bool
Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
const jschar *chars, size_t length,
Bindings *bindings, const jschar *chars, size_t length,
const char *filename, uintN lineno)
{
Compiler compiler(cx, principals);
@ -1667,6 +1675,8 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin
funcg.flags |= TCF_IN_FUNCTION;
funcg.setFunction(fun);
funcg.bindings.transfer(cx, bindings);
fun->setArgCount(funcg.bindings.countArgs());
if (!GenerateBlockId(&funcg, funcg.bodyid))
return NULL;
@ -1683,7 +1693,7 @@ Compiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *prin
* NB: do not use AutoLocalNameArray because it will release space
* allocated from cx->tempPool by DefineArg.
*/
jsuword *names = fun->getLocalNameArray(cx, &cx->tempPool);
jsuword *names = funcg.bindings.getLocalNameArray(cx, &cx->tempPool);
if (!names) {
fn = NULL;
} else {
@ -1762,11 +1772,10 @@ struct BindData {
bool fresh;
};
static JSBool
BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
JSLocalKind localKind, bool isArg)
static bool
BindLocalVariable(JSContext *cx, JSTreeContext *tc, JSAtom *atom, BindingKind kind, bool isArg)
{
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
/*
* Don't bind a variable with the hidden name 'arguments', per ECMA-262.
@ -1778,15 +1787,14 @@ BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
* arguments property.
*/
if (atom == cx->runtime->atomState.argumentsAtom && !isArg)
return JS_TRUE;
return true;
return fun->addLocal(cx, atom, localKind);
return tc->bindings.add(cx, atom, kind);
}
#if JS_HAS_DESTRUCTURING
static JSBool
BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
JSTreeContext *tc)
BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
{
/* Flag tc so we don't have to lookup arguments on every use. */
if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
@ -1796,6 +1804,11 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
JS_ASSERT(tc->inFunction());
/*
* NB: Check tc->decls rather than tc->bindings, because destructuring
* bindings aren't added to tc->bindings until after all arguments have
* been parsed.
*/
if (tc->decls.lookup(atom)) {
ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
JSMSG_DESTRUCT_DUP_ARG);
@ -2725,6 +2738,8 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSAtom *funAtom = NULL,
}
}
funbox->bindings.transfer(funtc->parser->context, &funtc->bindings);
return true;
}
@ -2733,13 +2748,12 @@ DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
/*
* FIXME? this Parser method was factored from Parser::functionDef with minimal
* change, hence the funtc ref param, funbox, and fun. It probably should match
* functionBody, etc., and use tc, tc->funbox, and tc->fun() instead of taking
* explicit parameters.
* change, hence the funtc ref param and funbox. It probably should match
* functionBody, etc., and use tc and tc->funbox instead of taking explicit
* parameters.
*/
bool
Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
JSParseNode **listp)
Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **listp)
{
if (tokenStream.getToken() != TOK_LP) {
reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_PAREN_BEFORE_FORMAL);
@ -2782,8 +2796,8 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
* Adjust fun->nargs to count the single anonymous positional
* parameter that is to be destructured.
*/
uintN slot = fun->nargs;
if (!fun->addLocal(context, NULL, JSLOCAL_ARG))
uint16 slot;
if (!funtc.bindings.addDestructuring(context, &slot))
return false;
/*
@ -2822,14 +2836,20 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
#ifdef JS_HAS_DESTRUCTURING
/*
* ECMA-262 requires us to support duplicate parameter names, but if the
* parameter list includes destructuring, we consider the code to have
* "opted in" to higher standards, and forbid duplicates. We may see a
* destructuring parameter later, so always note duplicates now.
* ECMA-262 requires us to support duplicate parameter names,
* but if the parameter list includes destructuring, we
* consider the code to have "opted in" to higher standards and
* forbid duplicates. We may see a destructuring parameter
* later, so always note duplicates now.
*
* Duplicates are warned about (strict option) or cause errors (strict
* mode code), but we do those tests in one place below, after having
* parsed the body in case it begins with a "use strict"; directive.
* Duplicates are warned about (strict option) or cause errors
* (strict mode code), but we do those tests in one place
* below, after having parsed the body in case it begins with a
* "use strict"; directive.
*
* NB: Check funtc.decls rather than funtc.bindings, because
* destructuring bindings aren't added to funtc.bindings
* until after all arguments have been parsed.
*/
if (funtc.decls.lookup(atom)) {
duplicatedArg = atom;
@ -2838,10 +2858,10 @@ Parser::functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunctio
}
#endif
if (!DefineArg(funbox->node, atom, fun->nargs, &funtc))
uint16 slot;
if (!funtc.bindings.addArgument(context, atom, &slot))
return false;
if (!fun->addLocal(context, atom, JSLOCAL_ARG))
if (!DefineArg(funbox->node, atom, slot, &funtc))
return false;
break;
}
@ -2971,15 +2991,15 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
* already exists.
*/
uintN index;
switch (tc->fun()->lookupLocal(context, funAtom, &index)) {
case JSLOCAL_NONE:
case JSLOCAL_ARG:
index = tc->fun()->u.i.nvars;
if (!tc->fun()->addLocal(context, funAtom, JSLOCAL_VAR))
switch (tc->bindings.lookup(funAtom, &index)) {
case NONE:
case ARGUMENT:
index = tc->bindings.countVars();
if (!tc->bindings.addVariable(context, funAtom))
return NULL;
/* FALL THROUGH */
case JSLOCAL_VAR:
case VARIABLE:
pn->pn_cookie.set(tc->staticLevel, index);
pn->pn_dflags |= PND_BOUND;
break;
@ -3002,17 +3022,19 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
/* Now parse formal argument list and compute fun->nargs. */
JSParseNode *prelude = NULL;
if (!functionArguments(funtc, funbox, fun, &prelude))
if (!functionArguments(funtc, funbox, &prelude))
return NULL;
fun->setArgCount(funtc.bindings.countArgs());
#if JS_HAS_DESTRUCTURING
/*
* If there were destructuring formal parameters, bind the destructured-to
* local variables now that we've parsed all the regular and destructuring
* formal parameters. Because JSFunction::addLocal must be called first for
* all ARGs, then all VARs, finally all UPVARs, we can't bind vars induced
* by formal parameter destructuring until after Parser::functionArguments
* has returned.
* formal parameters. Because js::Bindings::add must be called first for
* all ARGUMENTs, then all VARIABLEs and CONSTANTs, and finally all UPVARs,
* we can't bind vars induced by formal parameter destructuring until after
* Parser::functionArguments has returned.
*/
if (prelude) {
JSAtomListIterator iter(&funtc.decls);
@ -3024,8 +3046,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
if (apn->pn_op != JSOP_SETLOCAL)
continue;
uintN index = fun->u.i.nvars;
if (!BindLocalVariable(context, fun, apn->pn_atom, JSLOCAL_VAR, true))
uint16 index = funtc.bindings.countVars();
if (!BindLocalVariable(context, &funtc, apn->pn_atom, VARIABLE, true))
return NULL;
apn->pn_cookie.set(funtc.staticLevel, index);
}
@ -3802,8 +3824,8 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
return JS_TRUE;
}
JSLocalKind localKind = tc->fun()->lookupLocal(cx, atom, NULL);
if (localKind == JSLOCAL_NONE) {
BindingKind kind = tc->bindings.lookup(atom, NULL);
if (kind == NONE) {
/*
* Property not found in current variable scope: we have not seen this
* variable before. Define a new local variable by adding a property to
@ -3812,10 +3834,10 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
* runtime, by script prolog JSOP_DEFVAR opcodes generated for global
* and heavyweight-function-local vars.
*/
localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
kind = (data->op == JSOP_DEFCONST) ? CONSTANT : VARIABLE;
uintN index = tc->fun()->u.i.nvars;
if (!BindLocalVariable(cx, tc->fun(), atom, localKind, false))
uintN index = tc->bindings.countVars();
if (!BindLocalVariable(cx, tc, atom, kind, false))
return JS_FALSE;
pn->pn_op = JSOP_GETLOCAL;
pn->pn_cookie.set(tc->staticLevel, index);
@ -3823,12 +3845,12 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
return JS_TRUE;
}
if (localKind == JSLOCAL_ARG) {
if (kind == ARGUMENT) {
/* We checked errors and strict warnings earlier -- see above. */
JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
} else {
/* Not an argument, must be a redeclared local var. */
JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
}
return JS_TRUE;
}

View File

@ -932,8 +932,9 @@ struct JSObjectBox {
JSObjectBox *traceLink;
JSObjectBox *emitLink;
JSObject *object;
uintN index;
JSObjectBox *parent;
uintN index;
bool isFunctionBox;
};
#define JSFB_LEVEL_BITS 14
@ -949,6 +950,7 @@ struct JSFunctionBox : public JSObjectBox
pn_link, since lambdas are
neither definitions nor uses
of a binding */
js::Bindings bindings; /* bindings for this function */
uint32 queued:1,
inLoop:1, /* in a loop in parent function */
level:JSFB_LEVEL_BITS;
@ -1138,8 +1140,7 @@ private:
bool recognizeDirectivePrologue(JSParseNode *pn, bool *isDirectivePrologueMember);
enum FunctionType { GETTER, SETTER, GENERAL };
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSFunction *fun,
JSParseNode **list);
bool functionArguments(JSTreeContext &funtc, JSFunctionBox *funbox, JSParseNode **list);
JSParseNode *functionBody();
JSParseNode *functionDef(JSAtom *name, FunctionType type, uintN lambda);
@ -1199,7 +1200,7 @@ struct Compiler
static bool
compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
const jschar *chars, size_t length,
js::Bindings *bindings, const jschar *chars, size_t length,
const char *filename, uintN lineno);
static JSScript *

View File

@ -65,6 +65,8 @@
#include "jsarray.h"
#include "jsnum.h"
#include "jsscriptinlines.h"
using namespace js;
namespace js {

View File

@ -72,6 +72,8 @@
#include "jsstaticcheck.h"
#include "jsvector.h"
#include "jsscriptinlines.h"
#if JS_HAS_XML_SUPPORT
#include "jsxml.h"
#endif

View File

@ -295,6 +295,7 @@ struct Shape : public JSObjectMap
friend struct ::JSObject;
friend struct ::JSFunction;
friend class js::PropertyTree;
friend class js::Bindings;
friend bool HasUnreachableGCThings(TreeFragment *f);
protected:
@ -494,7 +495,7 @@ struct Shape : public JSObjectMap
/* Property stored in per-object dictionary, not shared property tree. */
IN_DICTIONARY = 0x08,
/* Prevent unwanted mutation of shared JSFunction::u.i.names nodes. */
/* Prevent unwanted mutation of shared Bindings::lastBinding nodes. */
FROZEN = 0x10
};
@ -879,7 +880,7 @@ Shape::isSharedPermanent() const
return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
}
}
} // namespace js
#ifdef _MSC_VER
#pragma warning(pop)

View File

@ -73,6 +73,252 @@
using namespace js;
using namespace js::gc;
namespace js {
BindingKind
Bindings::lookup(JSAtom *name, uintN *indexp) const
{
JS_ASSERT(lastBinding);
Shape *shape =
SHAPE_FETCH(Shape::search(const_cast<Shape **>(&lastBinding), ATOM_TO_JSID(name)));
if (!shape)
return NONE;
if (indexp)
*indexp = shape->shortid;
if (shape->getter() == GetCallArg)
return ARGUMENT;
if (shape->getter() == GetFlatUpvar)
return UPVAR;
return shape->writable() ? VARIABLE : CONSTANT;
}
bool
Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
{
JS_ASSERT(lastBinding);
/*
* We still follow 10.2.3 of ES3 and make argument and variable properties
* of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
* avoid objects as activations, something we should do too.
*/
uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
uint16 *indexp;
PropertyOp getter, setter;
uint32 slot = JSObject::CALL_RESERVED_SLOTS;
if (kind == ARGUMENT) {
JS_ASSERT(nvars == 0);
JS_ASSERT(nupvars == 0);
indexp = &nargs;
getter = GetCallArg;
setter = SetCallArg;
slot += nargs;
} else if (kind == UPVAR) {
indexp = &nupvars;
getter = GetFlatUpvar;
setter = SetFlatUpvar;
slot = SHAPE_INVALID_SLOT;
} else {
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
JS_ASSERT(nupvars == 0);
indexp = &nvars;
getter = GetCallVar;
setter = SetCallVar;
if (kind == CONSTANT)
attrs |= JSPROP_READONLY;
slot += nargs + nvars;
}
if (*indexp == JS_BITMASK(16)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
(kind == ARGUMENT)
? JSMSG_TOO_MANY_FUN_ARGS
: JSMSG_TOO_MANY_LOCALS);
return false;
}
jsid id;
if (!name) {
JS_ASSERT(kind == ARGUMENT); /* destructuring */
id = INT_TO_JSID(nargs);
} else {
id = ATOM_TO_JSID(name);
}
Shape child(id, getter, setter, slot, attrs, Shape::HAS_SHORTID, *indexp);
Shape *shape = lastBinding->getChild(cx, child, &lastBinding);
if (!shape)
return false;
JS_ASSERT(lastBinding == shape);
++*indexp;
return true;
}
/*
* This algorithm is O(n^2)! But this method is only called if the function is
* strict mode code or if JSOPTION_STRICT is set, so for now we'll tolerate the
* quadratic blowup.
*/
JSAtom *
Bindings::findDuplicateArgument() const
{
JS_ASSERT(lastBinding);
if (nargs <= 1)
return NULL;
for (Shape::Range r = lastArgument(); !r.empty(); r.popFront()) {
const Shape &shape = r.front();
for (Shape::Range r2 = shape.previous(); !r2.empty(); r2.popFront()) {
if (r2.front().id == shape.id)
return JSID_TO_ATOM(shape.id);
}
}
return NULL;
}
jsuword *
Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
{
JS_ASSERT(lastBinding);
JS_ASSERT(hasLocalNames());
uintN n = countLocalNames();
jsuword *names;
JS_ASSERT(SIZE_MAX / size_t(n) > sizeof *names);
JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names);
if (!names) {
js_ReportOutOfScriptQuota(cx);
return NULL;
}
#ifdef DEBUG
for (uintN i = 0; i != n; i++)
names[i] = 0xdeadbeef;
#endif
for (Shape::Range r = lastBinding; !r.empty(); r.popFront()) {
const Shape &shape = r.front();
uintN index = uint16(shape.shortid);
jsuword constFlag = 0;
if (shape.getter() == GetCallArg) {
JS_ASSERT(index < nargs);
} else if (shape.getter() == GetFlatUpvar) {
JS_ASSERT(index < nupvars);
index += nargs + nvars;
} else {
JS_ASSERT(index < nvars);
index += nargs;
if (!shape.writable())
constFlag = 1;
}
JSAtom *atom;
if (JSID_IS_ATOM(shape.id)) {
atom = JSID_TO_ATOM(shape.id);
} else {
JS_ASSERT(JSID_IS_INT(shape.id));
JS_ASSERT(shape.getter() == GetCallArg);
atom = NULL;
}
names[index] = jsuword(atom);
}
#ifdef DEBUG
for (uintN i = 0; i != n; i++)
JS_ASSERT(names[i] != 0xdeadbeef);
#endif
return names;
}
const Shape *
Bindings::lastArgument() const
{
JS_ASSERT(lastBinding);
const js::Shape *shape = lastVariable();
if (nvars > 0) {
while (shape->previous() && shape->getter() != GetCallArg)
shape = shape->previous();
}
return shape;
}
const Shape *
Bindings::lastVariable() const
{
JS_ASSERT(lastBinding);
const js::Shape *shape = lastUpvar();
if (nupvars > 0) {
while (shape->getter() == GetFlatUpvar)
shape = shape->previous();
}
return shape;
}
const Shape *
Bindings::lastUpvar() const
{
JS_ASSERT(lastBinding);
return lastBinding;
}
int
Bindings::sharpSlotBase(JSContext *cx)
{
JS_ASSERT(lastBinding);
#if JS_HAS_SHARP_VARS
if (JSAtom *name = js_Atomize(cx, "#array", 6, 0)) {
uintN index = uintN(-1);
#ifdef DEBUG
BindingKind kind =
#endif
lookup(name, &index);
JS_ASSERT(kind == VARIABLE);
return int(index);
}
#endif
return -1;
}
void
Bindings::makeImmutable()
{
JS_ASSERT(lastBinding);
Shape *shape = lastBinding;
if (shape->inDictionary()) {
do {
JS_ASSERT(!shape->frozen());
shape->setFrozen();
} while ((shape = shape->parent) != NULL);
}
}
void
Bindings::trace(JSTracer *trc)
{
for (const Shape *shape = lastBinding; shape; shape = shape->previous())
shape->trace(trc);
}
} // namespace js
#if JS_HAS_XDR
enum ScriptBits {
@ -87,30 +333,30 @@ enum ScriptBits {
JSBool
js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
{
JSContext *cx;
JSScript *script, *oldscript;
JSScript *oldscript;
JSBool ok;
jsbytecode *code;
uint32 length, lineno, nslots, magic;
uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i;
uint32 length, lineno, nslots;
uint32 natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
uint32 prologLength, version, encodedClosedCount;
uint16 nClosedArgs = 0, nClosedVars = 0;
JSPrincipals *principals;
uint32 encodeable;
JSBool filenameWasSaved;
jssrcnote *notes, *sn;
jssrcnote *sn;
JSSecurityCallbacks *callbacks;
uint32 scriptBits = 0;
cx = xdr->cx;
script = *scriptp;
nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = nconsts = 0;
JSContext *cx = xdr->cx;
JSScript *script = *scriptp;
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
filenameWasSaved = JS_FALSE;
notes = NULL;
jssrcnote *notes = NULL;
/* Should not XDR scripts optimized for a single global object. */
JS_ASSERT_IF(script, !JSScript::isValidOffset(script->globalsOffset));
uint32 magic;
if (xdr->mode == JSXDR_ENCODE)
magic = JSXDR_MAGIC_SCRIPT_CURRENT;
if (!JS_XDRUint32(xdr, &magic))
@ -128,6 +374,116 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
if (hasMagic)
*hasMagic = JS_TRUE;
/* XDR arguments, local vars, and upvars. */
uint16 nargs, nvars, nupvars;
uint32 argsVars, paddingUpvars;
if (xdr->mode == JSXDR_ENCODE) {
nargs = script->bindings.countArgs();
nvars = script->bindings.countVars();
nupvars = script->bindings.countUpvars();
argsVars = (nargs << 16) | nvars;
paddingUpvars = nupvars;
}
if (!JS_XDRUint32(xdr, &argsVars) || !JS_XDRUint32(xdr, &paddingUpvars))
return false;
if (xdr->mode == JSXDR_DECODE) {
nargs = argsVars >> 16;
nvars = argsVars & 0xFFFF;
JS_ASSERT((paddingUpvars >> 16) == 0);
nupvars = paddingUpvars & 0xFFFF;
}
Bindings bindings(cx);
uint32 nameCount = nargs + nvars + nupvars;
if (nameCount > 0) {
struct AutoMark {
JSArenaPool * const pool;
void * const mark;
AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { }
~AutoMark() {
JS_ARENA_RELEASE(pool, mark);
}
} automark(&cx->tempPool);
/*
* To xdr the names we prefix the names with a bitmap descriptor and
* then xdr the names as strings. For argument names (indexes below
* nargs) the corresponding bit in the bitmap is unset when the name
* is null. Such null names are not encoded or decoded. For variable
* names (indexes starting from nargs) bitmap's bit is set when the
* name is declared as const, not as ordinary var.
* */
uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
uint32 *bitmap;
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool,
bitmapLength * sizeof *bitmap);
if (!bitmap) {
js_ReportOutOfScriptQuota(cx);
return false;
}
jsuword *names;
if (xdr->mode == JSXDR_ENCODE) {
names = script->bindings.getLocalNameArray(cx, &cx->tempPool);
if (!names)
return false;
PodZero(bitmap, bitmapLength);
for (uintN i = 0; i < nameCount; i++) {
if (i < nargs
? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
: JS_LOCAL_NAME_IS_CONST(names[i]))
{
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
}
}
}
#ifdef __GNUC__
else {
names = NULL; /* quell GCC uninitialized warning */
}
#endif
for (uintN i = 0; i < bitmapLength; ++i) {
if (!JS_XDRUint32(xdr, &bitmap[i]))
return false;
}
for (uintN i = 0; i < nameCount; i++) {
if (i < nargs &&
!(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))))
{
if (xdr->mode == JSXDR_DECODE) {
uint16 dummy;
if (!bindings.addDestructuring(cx, &dummy))
return false;
} else {
JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
}
continue;
}
JSAtom *name;
if (xdr->mode == JSXDR_ENCODE)
name = JS_LOCAL_NAME_TO_ATOM(names[i]);
if (!js_XDRAtom(xdr, &name))
return false;
if (xdr->mode == JSXDR_DECODE) {
BindingKind kind = (i < nargs)
? ARGUMENT
: (i < nargs + nvars)
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
? CONSTANT
: VARIABLE)
: UPVAR;
if (!bindings.add(cx, name, kind))
return false;
}
}
if (xdr->mode == JSXDR_DECODE)
bindings.makeImmutable();
}
if (xdr->mode == JSXDR_ENCODE)
length = script->length;
if (!JS_XDRUint32(xdr, &length))
@ -152,7 +508,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
if (JSScript::isValidOffset(script->objectsOffset))
nobjects = script->objects()->length;
if (JSScript::isValidOffset(script->upvarsOffset))
nupvars = script->upvars()->length;
JS_ASSERT(script->bindings.countUpvars() == script->upvars()->length);
if (JSScript::isValidOffset(script->regexpsOffset))
nregexps = script->regexps()->length;
if (JSScript::isValidOffset(script->trynotesOffset))
@ -186,8 +542,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
return JS_FALSE;
/*
* To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
* and trynote counts early.
* To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
* counts early.
*/
if (!JS_XDRUint32(xdr, &natoms))
return JS_FALSE;
@ -197,8 +553,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
return JS_FALSE;
if (!JS_XDRUint32(xdr, &nobjects))
return JS_FALSE;
if (!JS_XDRUint32(xdr, &nupvars))
return JS_FALSE;
if (!JS_XDRUint32(xdr, &nregexps))
return JS_FALSE;
if (!JS_XDRUint32(xdr, &nconsts))
@ -220,6 +574,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
if (!script)
return JS_FALSE;
script->bindings.transfer(cx, &bindings);
script->main += prologLength;
script->setVersion(JSVersion(version & 0xffff));
script->nfixed = uint16(version >> 16);
@ -894,10 +1250,11 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
PodZero(script);
script->length = length;
script->setVersion(cx->findVersion());
new (&script->bindings) Bindings(cx);
uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
cursor = (uint8 *)script + sizeof(JSScript);
cursor = scriptEnd;
if (nobjects != 0) {
script->objectsOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSObjectArray);
@ -928,7 +1285,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
} else {
script->globalsOffset = JSScript::INVALID_OFFSET;
}
JS_ASSERT((cursor - (uint8 *)script) < 0xFF);
JS_ASSERT(cursor - scriptEnd < 0xFF);
if (nconsts != 0) {
script->constOffset = (uint8)(cursor - scriptEnd);
cursor += sizeof(JSConstArray);
@ -1067,7 +1424,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
nfixed = cg->inFunction()
? cg->fun()->u.i.nvars
? cg->bindings.countVars()
: cg->sharpSlots();
JS_ASSERT(nfixed < SLOTNO_LIMIT);
script->nfixed = (uint16) nfixed;
@ -1136,6 +1493,9 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
script->nClosedVars * sizeof(uint32));
}
cg->bindings.makeImmutable();
script->bindings.transfer(cx, &cg->bindings);
/*
* We initialize fun->u.script to be the script constructed above
* so that the debugger has a valid FUN_SCRIPT(fun).
@ -1143,13 +1503,14 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
fun = NULL;
if (cg->inFunction()) {
fun = cg->fun();
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
JS_ASSERT(fun->isInterpreted());
JS_ASSERT(!fun->script());
#ifdef DEBUG
if (JSScript::isValidOffset(script->upvarsOffset))
JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
else
fun->u.i.nupvars = 0;
fun->freezeLocalNames(cx);
JS_ASSERT(script->bindings.countUpvars() == 0);
#endif
fun->u.i.script = script;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
@ -1343,6 +1704,8 @@ js_TraceScript(JSTracer *trc, JSScript *script)
if (IS_GC_MARKING_TRACER(trc) && script->filename)
js_MarkScriptFilename(script->filename);
script->bindings.trace(trc);
}
JSBool

View File

@ -145,6 +145,8 @@ typedef struct JSConstArray {
uint32 length;
} JSConstArray;
struct JSArenaPool;
namespace js {
struct GlobalSlotArray {
@ -156,6 +158,162 @@ struct GlobalSlotArray {
uint32 length;
};
class Shape;
enum BindingKind { NONE, ARGUMENT, VARIABLE, CONSTANT, UPVAR };
/*
* Formal parameters, local variables, and upvars are stored in a shape tree
* path encapsulated within this class. This class represents bindings for
* both function and top-level scripts (the latter is needed to track names in
* strict mode eval code, to give such code its own lexical environment).
*/
class Bindings {
js::Shape *lastBinding;
uint16 nargs;
uint16 nvars;
uint16 nupvars;
public:
inline Bindings(JSContext *cx);
/*
* Transfers ownership of bindings data from bindings into this fresh
* Bindings instance. Once such a transfer occurs, the old bindings must
* not be used again.
*/
inline void transfer(JSContext *cx, Bindings *bindings);
/*
* Clones bindings data from bindings, which must be immutable, into this
* fresh Bindings instance. A Bindings instance may be cloned multiple
* times.
*/
inline void clone(JSContext *cx, Bindings *bindings);
uint16 countArgs() const { return nargs; }
uint16 countVars() const { return nvars; }
uint16 countUpvars() const { return nupvars; }
uintN countArgsAndVars() const { return nargs + nvars; }
uintN countLocalNames() const { return nargs + nvars + nupvars; }
bool hasUpvars() const { return nupvars > 0; }
bool hasLocalNames() const { return countLocalNames() > 0; }
/* Returns the shape lineage generated for these bindings. */
inline const js::Shape *lastShape() const;
/*
* Add a local binding for the given name, of the given type, for the code
* being compiled. If fun is non-null, this binding set is being created
* for that function, so adjust corresponding metadata in that function
* while adding. Otherwise this set must correspond to a top-level script.
*
* A binding may be added twice with different kinds; the last one for a
* given name prevails. (We preserve both bindings for the decompiler,
* which must deal with such cases.) Pass null for name when indicating a
* destructuring argument. Return true on success.
*
*
* The parser builds shape paths for functions, usable by Call objects at
* runtime, by calling addLocal. All ARGUMENT bindings must be added before
* before any VARIABLE or CONSTANT bindings, which themselves must be added
* before all UPVAR bindings.
*/
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
/* Convenience specializations. */
bool addVariable(JSContext *cx, JSAtom *name) {
return add(cx, name, VARIABLE);
}
bool addConstant(JSContext *cx, JSAtom *name) {
return add(cx, name, CONSTANT);
}
bool addUpvar(JSContext *cx, JSAtom *name) {
return add(cx, name, UPVAR);
}
bool addArgument(JSContext *cx, JSAtom *name, uint16 *slotp) {
JS_ASSERT(name != NULL); /* not destructuring */
*slotp = nargs;
return add(cx, name, ARGUMENT);
}
bool addDestructuring(JSContext *cx, uint16 *slotp) {
*slotp = nargs;
return add(cx, NULL, ARGUMENT);
}
/*
* Look up an argument or variable name, returning its kind when found or
* NONE when no such name exists. When indexp is not null and the name
* exists, *indexp will receive the index of the corresponding argument or
* variable.
*/
BindingKind lookup(JSAtom *name, uintN *indexp) const;
/* Convenience method to check for any binding for a name. */
bool hasBinding(JSAtom *name) const {
return lookup(name, NULL) != NONE;
}
/*
* If this binding set for the given function includes duplicated argument
* names, return an arbitrary duplicate name. Otherwise, return NULL.
*/
JSAtom *findDuplicateArgument() const;
/*
* Function and macros to work with local names as an array of words.
* getLocalNameArray returns the array, or null if we are out of memory.
* This function must be called only when hasLocalNames().
*
* The supplied pool is used to allocate the returned array, so the caller
* is obligated to mark and release to free it.
*
* The elements of the array with index less than nargs correspond to the
* the names of arguments. An index >= nargs addresses a var binding. Use
* JS_LOCAL_NAME_TO_ATOM to convert array's element to an atom pointer.
* This pointer can be null when the element is for an argument
* corresponding to a destructuring pattern.
*
* If nameWord does not name an argument, use JS_LOCAL_NAME_IS_CONST to
* check if nameWord corresponds to the const declaration.
*/
jsuword *
getLocalNameArray(JSContext *cx, JSArenaPool *pool);
/*
* Returns the slot where the sharp array is stored, or a value < 0 if no
* sharps are present or in case of failure.
*/
int sharpSlotBase(JSContext *cx);
/*
* Protect stored bindings from mutation. Subsequent attempts to add
* bindings will copy the existing bindings before adding to them, allowing
* the original bindings to be safely shared.
*/
void makeImmutable();
/*
* These methods provide direct access to the shape path normally
* encapsulated by js::Bindings. These methods may be used to make a
* Shape::Range for iterating over the relevant shapes from youngest to
* oldest (i.e., last or right-most to first or left-most in source order).
*
* Sometimes iteration order must be from oldest to youngest, however. For
* such cases, use js::Bindings::getLocalNameArray. The RAII class
* js::AutoLocalNameArray, defined in jscntxt.h, should be used where
* possible instead of direct calls to getLocalNameArray.
*/
const js::Shape *lastArgument() const;
const js::Shape *lastVariable() const;
const js::Shape *lastUpvar() const;
void trace(JSTracer *trc);
};
} /* namespace js */
#define JS_OBJECT_ARRAY_SIZE(length) \
@ -255,6 +413,8 @@ struct JSScript {
uint16 staticLevel;/* static level for display maintenance */
uint16 nClosedArgs; /* number of args which are closed over. */
uint16 nClosedVars; /* number of vars which are closed over. */
js::Bindings bindings; /* names of top-level variables in this script
(and arguments if this is a function script) */
JSPrincipals *principals;/* principals for this script */
union {
/*

View File

@ -41,10 +41,59 @@
#ifndef jsscriptinlines_h___
#define jsscriptinlines_h___
#include "jscntxt.h"
#include "jsfun.h"
#include "jsopcode.h"
#include "jsregexp.h"
#include "jsscript.h"
#include "jsscope.h"
namespace js {
inline
Bindings::Bindings(JSContext *cx)
: lastBinding(cx->runtime->emptyCallShape), nargs(0), nvars(0), nupvars(0)
{
}
inline void
Bindings::transfer(JSContext *cx, Bindings *bindings)
{
JS_ASSERT(lastBinding == cx->runtime->emptyCallShape);
*this = *bindings;
#ifdef DEBUG
bindings->lastBinding = NULL;
#endif
/* Preserve back-pointer invariants across the lastBinding transfer. */
if (lastBinding->inDictionary())
lastBinding->listp = &this->lastBinding;
}
inline void
Bindings::clone(JSContext *cx, Bindings *bindings)
{
JS_ASSERT(lastBinding == cx->runtime->emptyCallShape);
/*
* Non-dictionary bindings are fine to share, as are dictionary bindings if
* they're copy-on-modification.
*/
JS_ASSERT(!bindings->lastBinding->inDictionary() || bindings->lastBinding->frozen());
*this = *bindings;
}
const Shape *
Bindings::lastShape() const
{
JS_ASSERT(lastBinding);
JS_ASSERT_IF(lastBinding->inDictionary(), lastBinding->frozen());
return lastBinding;
}
} // namespace js
inline JSFunction *
JSScript::getFunction(size_t index)

View File

@ -3377,7 +3377,7 @@ struct VarClosureTraits
{
// See also UpvarVarTraits.
static inline Value get_slot(JSStackFrame* fp, unsigned slot) {
JS_ASSERT(slot < fp->fun()->u.i.nvars);
JS_ASSERT(slot < fp->fun()->script()->bindings.countVars());
return fp->slots()[slot];
}
@ -3391,7 +3391,7 @@ struct VarClosureTraits
}
static inline uint16 slot_count(JSObject* obj) {
return obj->getCallObjCalleeFunction()->u.i.nvars;
return obj->getCallObjCalleeFunction()->script()->bindings.countVars();
}
private:
@ -3472,11 +3472,13 @@ TraceRecorder::importImpl(Address addr, const void* p, JSValueType t,
JSAutoByteString funNameBytes;
if (*prefix == 'a' || *prefix == 'v') {
mark = JS_ARENA_MARK(&cx->tempPool);
if (fp->fun()->hasLocalNames())
localNames = fp->fun()->getLocalNameArray(cx, &cx->tempPool);
funName = fp->fun()->atom
? js_AtomToPrintableString(cx, fp->fun()->atom, &funNameBytes)
: "<anonymous>";
JSFunction *fun = fp->fun();
Bindings &bindings = fun->script()->bindings;
if (bindings.hasLocalNames())
localNames = bindings.getLocalNameArray(cx, &cx->tempPool);
funName = fun->atom
? js_AtomToPrintableString(cx, fun->atom, &funNameBytes)
: "<anonymous>";
}
if (!strcmp(prefix, "argv")) {
if (index < fp->numFormalArgs()) {
@ -10224,7 +10226,7 @@ TraceRecorder::putActivationObjects()
}
if (have_call) {
int nslots = fp->fun()->countVars();
int nslots = fp->fun()->script()->bindings.countVars();
LIns* slots_ins;
if (nslots) {
slots_ins = w.allocp(sizeof(Value) * nslots);
@ -13328,8 +13330,8 @@ TraceRecorder::guardCallee(Value& callee)
* we wish to optimize for the particular deactivated stack frame (null
* private data) case as noted above.
*/
if (FUN_INTERPRETED(callee_fun) &&
(!FUN_NULL_CLOSURE(callee_fun) || callee_fun->u.i.nupvars != 0)) {
if (callee_fun->isInterpreted() &&
(!FUN_NULL_CLOSURE(callee_fun) || callee_fun->script()->bindings.hasUpvars())) {
JSObject* parent = callee_obj.getParent();
if (parent != globalObj) {
@ -15270,14 +15272,15 @@ TraceRecorder::record_JSOP_LAMBDA_FC()
w.name(w.eqp(closure_ins, w.immpNull()), "guard(js_AllocFlatClosure)"),
OOM_EXIT);
if (fun->u.i.nupvars) {
JSUpvarArray *uva = fun->u.i.script->upvars();
JSScript *script = fun->script();
if (script->bindings.hasUpvars()) {
JSUpvarArray *uva = script->upvars();
LIns* upvars_ins = w.getObjPrivatizedSlot(closure_ins,
JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
for (uint32 i = 0, n = uva->length; i < n; i++) {
Value v;
LIns* v_ins = upvar(fun->u.i.script, uva, i, v);
LIns* v_ins = upvar(script, uva, i, v);
if (!v_ins)
return ARECORD_STOP;

View File

@ -194,7 +194,8 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
#define JSXDR_MAGIC_SCRIPT_8 0xdead0008
#define JSXDR_MAGIC_SCRIPT_9 0xdead0009
#define JSXDR_MAGIC_SCRIPT_10 0xdead000a
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_10
#define JSXDR_MAGIC_SCRIPT_11 0xdead000b
#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_11
/*
* Bytecode version number. Increment the subtrahend whenever JS bytecode
@ -205,7 +206,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 79)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80)
/*
* Library-private functions.

View File

@ -1742,16 +1742,13 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_DEFFUN)
{
uint32 index = fullAtomIndex(PC);
JSFunction *inner = script->getFunction(index);
JSFunction *innerFun = script->getFunction(index);
if (fun) {
JSLocalKind localKind = fun->lookupLocal(cx, inner->atom, NULL);
if (localKind != JSLOCAL_NONE)
frame.syncAndForgetEverything();
}
if (fun && script->bindings.hasBinding(innerFun->atom))
frame.syncAndForgetEverything();
prepareStubCall(Uses(0));
masm.move(ImmPtr(inner), Registers::ArgReg1);
masm.move(ImmPtr(innerFun), Registers::ArgReg1);
INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun));
}
END_CASE(JSOP_DEFFUN)
@ -1773,11 +1770,8 @@ mjit::Compiler::generateMethod()
uint32 index = fullAtomIndex(PC);
JSAtom *atom = script->getAtom(index);
if (fun) {
JSLocalKind localKind = fun->lookupLocal(cx, atom, NULL);
if (localKind != JSLOCAL_NONE)
frame.syncAndForgetEverything();
}
if (fun && script->bindings.hasBinding(atom))
frame.syncAndForgetEverything();
prepareStubCall(Uses(1));
masm.move(ImmPtr(atom), Registers::ArgReg1);

View File

@ -44,6 +44,8 @@
#include "jsnum.h"
#include "jsscope.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/Compiler.h"
#include "methodjit/StubCalls.h"

View File

@ -2033,16 +2033,17 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
else if (FUN_FLAT_CLOSURE(fun))
fputs(" FLAT_CLOSURE", stdout);
if (fun->u.i.nupvars) {
JSScript *script = fun->script();
if (script->bindings.hasUpvars()) {
fputs("\nupvars: {\n", stdout);
void *mark = JS_ARENA_MARK(&cx->tempPool);
jsuword *localNames = fun->getLocalNameArray(cx, &cx->tempPool);
jsuword *localNames = script->bindings.getLocalNameArray(cx, &cx->tempPool);
if (!localNames)
return false;
JSUpvarArray *uva = fun->u.i.script->upvars();
uintN upvar_base = fun->countArgsAndVars();
JSUpvarArray *uva = script->upvars();
uintN upvar_base = script->bindings.countArgsAndVars();
for (uint32 i = 0, n = uva->length; i < n; i++) {
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[upvar_base + i]);