mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 775323 - build Bindings after, not during, parsing (r=ejpbruel)
--HG-- extra : rebase_source : f78cf919d2e205b688260f462ea109f2816b5ace
This commit is contained in:
parent
abaee54e50
commit
d80d33c1f5
@ -176,7 +176,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
||||
if (!FoldConstants(cx, pn, &parser))
|
||||
return NULL;
|
||||
|
||||
if (!AnalyzeFunctions(&parser, callerFrame))
|
||||
if (!AnalyzeFunctions(&parser))
|
||||
return NULL;
|
||||
tc.functionList = NULL;
|
||||
|
||||
@ -215,8 +215,6 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// We're not in a function context, so we don't expect any bindings.
|
||||
JS_ASSERT(!sc.bindings.hasBinding(cx, arguments));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -238,7 +236,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
||||
// handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
|
||||
bool
|
||||
frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
|
||||
Bindings *bindings, const jschar *chars, size_t length)
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length)
|
||||
{
|
||||
if (!CheckLength(cx, length))
|
||||
return NULL;
|
||||
@ -262,25 +260,13 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
|
||||
JS_ASSERT(fun);
|
||||
SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL,
|
||||
StrictModeFromContext(cx));
|
||||
funsc.bindings.transfer(bindings);
|
||||
fun->setArgCount(funsc.bindings.numArgs());
|
||||
fun->setArgCount(formals.length());
|
||||
|
||||
unsigned staticLevel = 0;
|
||||
TreeContext funtc(&parser, &funsc, staticLevel, /* bodyid = */ 0);
|
||||
if (!funtc.init())
|
||||
return false;
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
|
||||
staticLevel, ss, 0, length));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
StackFrame *nullCallerFrame = NULL;
|
||||
BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, nullCallerFrame,
|
||||
/* hasGlobalScope = */ false, options.lineno);
|
||||
if (!funbce.init())
|
||||
return false;
|
||||
|
||||
/* FIXME: make Function format the source for a function definition. */
|
||||
ParseNode *fn = FunctionNode::create(PNK_NAME, &parser);
|
||||
if (!fn)
|
||||
@ -296,22 +282,9 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
|
||||
argsbody->makeEmpty();
|
||||
fn->pn_body = argsbody;
|
||||
|
||||
unsigned nargs = fun->nargs;
|
||||
if (nargs) {
|
||||
/*
|
||||
* NB: do not use AutoLocalNameArray because it will release space
|
||||
* allocated from cx->tempLifoAlloc by DefineArg.
|
||||
*/
|
||||
BindingVector names(cx);
|
||||
if (!GetOrderedBindings(cx, funsc.bindings, &names))
|
||||
for (unsigned i = 0; i < formals.length(); i++) {
|
||||
if (!DefineArg(&parser, fn, formals[i]))
|
||||
return false;
|
||||
|
||||
RootedPropertyName name(cx);
|
||||
for (unsigned i = 0; i < nargs; i++) {
|
||||
name = names[i].maybeName;
|
||||
if (!DefineArg(fn, name, i, &parser))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -331,7 +304,20 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
|
||||
if (!FoldConstants(cx, pn, &parser))
|
||||
return false;
|
||||
|
||||
if (!AnalyzeFunctions(&parser, nullCallerFrame))
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
|
||||
staticLevel, ss, 0, length));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
if (!funtc.generateBindings(cx, &script->bindings))
|
||||
return false;
|
||||
|
||||
BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL,
|
||||
/* hasGlobalScope = */ false, options.lineno);
|
||||
if (!funbce.init())
|
||||
return false;
|
||||
|
||||
if (!AnalyzeFunctions(&parser))
|
||||
return false;
|
||||
|
||||
if (fn->pn_body) {
|
||||
|
@ -20,7 +20,7 @@ CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
|
||||
|
||||
bool
|
||||
CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
|
||||
Bindings *bindings, const jschar *chars, size_t length);
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length);
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
@ -119,8 +119,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared
|
||||
emitLevel(0),
|
||||
constMap(sc->context),
|
||||
constList(sc->context),
|
||||
closedArgs(sc->context),
|
||||
closedVars(sc->context),
|
||||
typesetCount(0),
|
||||
hasSingletons(false),
|
||||
inForInit(false),
|
||||
@ -898,7 +896,7 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
|
||||
*/
|
||||
for (unsigned i = pn->pn_cookie.level(); i; i--) {
|
||||
skippedScopes += ClonedBlockDepth(bceOfDef);
|
||||
if (bceOfDef->sc->funIsHeavyweight()) {
|
||||
if (bceOfDef->sc->fun()->isHeavyweight()) {
|
||||
skippedScopes++;
|
||||
if (bceOfDef->sc->fun()->isNamedLambda())
|
||||
skippedScopes++;
|
||||
@ -913,15 +911,15 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
|
||||
ScopeCoordinate sc;
|
||||
if (IsArgOp(pn->getOp())) {
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = bceOfDef->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
|
||||
sc.slot = bceOfDef->script->bindings.formalIndexToSlot(pn->pn_cookie.slot());
|
||||
} else {
|
||||
JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
|
||||
unsigned local = pn->pn_cookie.slot();
|
||||
if (local < bceOfDef->sc->bindings.numVars()) {
|
||||
if (local < bceOfDef->script->bindings.numVars()) {
|
||||
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
|
||||
sc.slot = bceOfDef->sc->bindings.varIndexToSlot(local);
|
||||
sc.slot = bceOfDef->script->bindings.varIndexToSlot(local);
|
||||
} else {
|
||||
unsigned depth = local - bceOfDef->sc->bindings.numVars();
|
||||
unsigned depth = local - bceOfDef->script->bindings.numVars();
|
||||
StaticBlockObject *b = bceOfDef->blockChain;
|
||||
while (!b->containsVarAtDepth(depth)) {
|
||||
if (b->needsClone())
|
||||
@ -929,7 +927,7 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
|
||||
b = b->enclosingBlock();
|
||||
}
|
||||
sc.hops = skippedScopes;
|
||||
sc.slot = b->localIndexToSlot(bceOfDef->sc->bindings, local);
|
||||
sc.slot = b->localIndexToSlot(bceOfDef->script->bindings, local);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1013,43 +1011,12 @@ EmitVarIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
|
||||
bool
|
||||
BytecodeEmitter::isAliasedName(ParseNode *pn)
|
||||
{
|
||||
return sc->bindingsAccessedDynamically() || shouldNoteClosedName(pn->resolve());
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::shouldNoteClosedName(ParseNode *pn)
|
||||
{
|
||||
return !sc->bindingsAccessedDynamically() && pn->isDefn() && pn->isClosed();
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::noteClosedVar(ParseNode *pn)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(shouldNoteClosedName(pn));
|
||||
Definition *dn = (Definition *)pn;
|
||||
JS_ASSERT(dn->kind() == Definition::VAR || dn->kind() == Definition::CONST);
|
||||
JS_ASSERT(pn->pn_cookie.slot() < sc->bindings.numVars());
|
||||
for (size_t i = 0; i < closedVars.length(); ++i)
|
||||
JS_ASSERT(closedVars[i] != pn->pn_cookie.slot());
|
||||
#endif
|
||||
sc->setFunIsHeavyweight();
|
||||
return closedVars.append(pn->pn_cookie.slot());
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::noteClosedArg(ParseNode *pn)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(shouldNoteClosedName(pn));
|
||||
Definition *dn = (Definition *)pn;
|
||||
JS_ASSERT(dn->kind() == Definition::ARG);
|
||||
JS_ASSERT(pn->pn_cookie.slot() < sc->bindings.numArgs());
|
||||
for (size_t i = 0; i < closedArgs.length(); ++i)
|
||||
JS_ASSERT(closedArgs[i] != pn->pn_cookie.slot());
|
||||
#endif
|
||||
sc->setFunIsHeavyweight();
|
||||
return closedArgs.append(pn->pn_cookie.slot());
|
||||
if (sc->bindingsAccessedDynamically())
|
||||
return true;
|
||||
Definition *dn = pn->resolve();
|
||||
JS_ASSERT(dn->isDefn());
|
||||
JS_ASSERT(!dn->isPlaceholder());
|
||||
return dn->isClosed();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1065,7 +1032,7 @@ AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
|
||||
{
|
||||
JS_ASSERT((unsigned) slot < bce->maxStackDepth);
|
||||
if (bce->sc->inFunction()) {
|
||||
slot += bce->sc->bindings.numVars();
|
||||
slot += bce->script->bindings.numVars();
|
||||
if ((unsigned) slot >= SLOTNO_LIMIT) {
|
||||
bce->reportError(NULL, JSMSG_TOO_MANY_LOCALS);
|
||||
slot = -1;
|
||||
@ -1116,8 +1083,7 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool aliased = bce->sc->bindingsAccessedDynamically() || bce->shouldNoteClosedName(dn);
|
||||
blockObj->setAliased(i, aliased);
|
||||
blockObj->setAliased(i, bce->isAliasedName(dn));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1125,8 +1091,7 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
||||
* clones must get unique shapes; see the comments for
|
||||
* js::Bindings::extensibleParents.
|
||||
*/
|
||||
if (bce->sc->funHasExtensibleScope() ||
|
||||
bce->sc->bindings.extensibleParents()) {
|
||||
if (bce->sc->funHasExtensibleScope() || bce->script->bindings.extensibleParents()) {
|
||||
Shape *newShape = Shape::setExtensibleParents(cx, blockObj->lastProperty());
|
||||
if (!newShape)
|
||||
return false;
|
||||
@ -1390,7 +1355,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
* heavyweight, ensuring that the function name is represented in
|
||||
* the scope chain so that assignment will throw a TypeError.
|
||||
*/
|
||||
if (!bce->sc->funIsHeavyweight()) {
|
||||
if (!bce->sc->fun()->isHeavyweight()) {
|
||||
op = JSOP_CALLEE;
|
||||
pn->pn_dflags |= PND_CONST;
|
||||
}
|
||||
@ -2606,11 +2571,11 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
bce->switchToProlog();
|
||||
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
|
||||
return false;
|
||||
unsigned varIndex = bce->sc->bindings.argumentsVarIndex(cx);
|
||||
unsigned varIndex = bce->script->bindings.argumentsVarIndex(cx);
|
||||
if (bce->sc->bindingsAccessedDynamically()) {
|
||||
ScopeCoordinate sc;
|
||||
sc.hops = 0;
|
||||
sc.slot = bce->sc->bindings.varIndexToSlot(varIndex);
|
||||
sc.slot = bce->script->bindings.varIndexToSlot(varIndex);
|
||||
if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
|
||||
return false;
|
||||
} else {
|
||||
@ -2638,11 +2603,6 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
|
||||
return false;
|
||||
|
||||
// Initialize fun->script() so that the debugger has a valid fun->script().
|
||||
RootedFunction fun(cx, bce->script->function());
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
if (bce->sc->funIsHeavyweight())
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
|
||||
/* Mark functions which will only be executed once as singletons. */
|
||||
bool singleton =
|
||||
@ -2650,6 +2610,9 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
|
||||
bce->parent &&
|
||||
bce->parent->checkSingletonContext();
|
||||
|
||||
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
|
||||
RootedFunction fun(cx, bce->script->function());
|
||||
JS_ASSERT(fun->isInterpreted());
|
||||
JS_ASSERT(!fun->script());
|
||||
fun->setScript(bce->script);
|
||||
if (!fun->setTypeForScriptedFunction(cx, singleton))
|
||||
@ -2674,7 +2637,7 @@ MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *
|
||||
}
|
||||
|
||||
if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
|
||||
(!bce->sc->inFunction() || bce->sc->funIsHeavyweight()))
|
||||
(!bce->sc->inFunction() || bce->sc->fun()->isHeavyweight()))
|
||||
{
|
||||
bce->switchToProlog();
|
||||
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
|
||||
@ -2684,15 +2647,6 @@ MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *
|
||||
bce->switchToMain();
|
||||
}
|
||||
|
||||
if (bce->sc->inFunction() &&
|
||||
IsLocalOp(pn->getOp()) &&
|
||||
!pn->isLet() &&
|
||||
bce->shouldNoteClosedName(pn))
|
||||
{
|
||||
if (!bce->noteClosedVar(pn))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
*result = atomIndex;
|
||||
return true;
|
||||
@ -2801,7 +2755,7 @@ EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
||||
* The lhs is a simple name so the to-be-destructured value is
|
||||
* its initial value and there is nothing to do.
|
||||
*/
|
||||
JS_ASSERT(pn->getOp() == JSOP_SETLOCAL);
|
||||
JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
|
||||
JS_ASSERT(pn->pn_dflags & PND_BOUND);
|
||||
return true;
|
||||
}
|
||||
@ -3375,8 +3329,8 @@ EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption
|
||||
|
||||
if (pn3) {
|
||||
JS_ASSERT(emitOption != DefineVars);
|
||||
JS_ASSERT_IF(emitOption == PushInitialValues, op == JSOP_SETLOCAL);
|
||||
if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
|
||||
JS_ASSERT(emitOption != PushInitialValues);
|
||||
JSOp bindOp = (op == JSOP_SETNAME) ? JSOP_BINDNAME : JSOP_BINDGNAME;
|
||||
if (!EmitIndex32(cx, bindOp, atomIndex, bce))
|
||||
return false;
|
||||
@ -4849,7 +4803,6 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
sc.cxFlags = funbox->cxFlags;
|
||||
if (bce->sc->funMightAliasLocals())
|
||||
sc.setFunMightAliasLocals(); // inherit funMightAliasLocals from parent
|
||||
sc.bindings.transfer(&funbox->bindings);
|
||||
JS_ASSERT_IF(bce->sc->inStrictMode(), sc.inStrictMode());
|
||||
|
||||
// Inherit most things (principals, version, etc) from the parent.
|
||||
@ -4868,6 +4821,8 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
script->bindings.transferFrom(&funbox->bindings);
|
||||
|
||||
BytecodeEmitter bce2(bce, bce->parser, &sc, script, bce->callerFrame, bce->hasGlobalScope,
|
||||
pn->pn_pos.begin.lineno);
|
||||
if (!bce2.init())
|
||||
@ -4914,23 +4869,11 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return false;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
BindingIter bi(cx, bce->sc->bindings.lookup(cx, fun->atom->asPropertyName()));
|
||||
BindingIter bi(cx, bce->script->bindings.lookup(cx, fun->atom->asPropertyName()));
|
||||
JS_ASSERT(bi->kind == VARIABLE || bi->kind == CONSTANT || bi->kind == ARGUMENT);
|
||||
JS_ASSERT(bi.frameIndex() < JS_BIT(20));
|
||||
#endif
|
||||
pn->pn_index = index;
|
||||
if (bce->shouldNoteClosedName(pn)) {
|
||||
Definition::Kind kind = ((Definition *)pn)->kind();
|
||||
if (kind == Definition::ARG) {
|
||||
if (!bce->noteClosedArg(pn))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(kind == Definition::VAR);
|
||||
if (!bce->noteClosedVar(pn))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
|
||||
return false;
|
||||
if (!EmitIndexOp(cx, JSOP_LAMBDA, index, bce))
|
||||
@ -5350,7 +5293,7 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
|
||||
return false;
|
||||
break;
|
||||
case PNK_INTRINSICNAME:
|
||||
if (pn2->atom() == cx->runtime->atomState._CallFunctionAtom)
|
||||
if (pn2->name() == cx->runtime->atomState._CallFunctionAtom)
|
||||
{
|
||||
/*
|
||||
* Special-casing of %_CallFunction to emit bytecode that directly
|
||||
@ -6005,7 +5948,7 @@ EmitDefaults(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
// It doesn't matter if this is correct with respect to aliasing or
|
||||
// not. Only the decompiler is going to see it.
|
||||
BindingIter bi(cx, bce->sc->bindings.lookup(cx, arg->pn_left->atom()));
|
||||
BindingIter bi(cx, bce->script->bindings.lookup(cx, arg->pn_left->name()));
|
||||
if (!EmitUnaliasedVarOp(cx, JSOP_SETLOCAL, bi.frameIndex(), bce))
|
||||
return false;
|
||||
SET_JUMP_OFFSET(bce->code(hop), bce->offset() - hop);
|
||||
@ -6120,10 +6063,6 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
continue;
|
||||
if (!BindNameToSlot(cx, bce, pn2))
|
||||
return false;
|
||||
if (IsArgOp(pn2->getOp()) && bce->shouldNoteClosedName(pn2)) {
|
||||
if (!bce->noteClosedArg(pn2))
|
||||
return false;
|
||||
}
|
||||
if (pn2->pn_next == pnlast && fun->hasRest() && !fun->hasDefaults()) {
|
||||
// Fill rest parameter. We handled the case with defaults above.
|
||||
JS_ASSERT(!bce->sc->funArgumentsHasLocalBinding());
|
||||
@ -6142,12 +6081,6 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_UPVARS:
|
||||
JS_ASSERT(pn->pn_names->count() != 0);
|
||||
ok = EmitTree(cx, bce, pn->pn_tree);
|
||||
pn->pn_names.releaseMap(cx);
|
||||
break;
|
||||
|
||||
case PNK_IF:
|
||||
ok = EmitIf(cx, bce, pn);
|
||||
break;
|
||||
|
@ -106,11 +106,6 @@ struct BytecodeEmitter
|
||||
CGObjectList regexpList; /* list of emitted regexp that will be
|
||||
cloned during execution */
|
||||
|
||||
/* Vectors of pn_cookie slot values. */
|
||||
typedef Vector<uint32_t, 8> SlotVector;
|
||||
SlotVector closedArgs;
|
||||
SlotVector closedVars;
|
||||
|
||||
uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */
|
||||
|
||||
bool hasSingletons:1; /* script contains singleton initializer JSOP_OBJECT */
|
||||
@ -134,9 +129,6 @@ struct BytecodeEmitter
|
||||
~BytecodeEmitter();
|
||||
|
||||
bool isAliasedName(ParseNode *pn);
|
||||
bool shouldNoteClosedName(ParseNode *pn);
|
||||
bool noteClosedVar(ParseNode *pn);
|
||||
bool noteClosedArg(ParseNode *pn);
|
||||
|
||||
JS_ALWAYS_INLINE
|
||||
bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
|
||||
|
@ -49,7 +49,7 @@ ParseMapPool::allocate()
|
||||
}
|
||||
|
||||
inline Definition *
|
||||
AtomDecls::lookupFirst(JSAtom *atom)
|
||||
AtomDecls::lookupFirst(JSAtom *atom) const
|
||||
{
|
||||
JS_ASSERT(map);
|
||||
AtomDefnListPtr p = map->lookup(atom);
|
||||
@ -59,7 +59,7 @@ AtomDecls::lookupFirst(JSAtom *atom)
|
||||
}
|
||||
|
||||
inline DefinitionList::Range
|
||||
AtomDecls::lookupMulti(JSAtom *atom)
|
||||
AtomDecls::lookupMulti(JSAtom *atom) const
|
||||
{
|
||||
JS_ASSERT(map);
|
||||
if (AtomDefnListPtr p = map->lookup(atom))
|
||||
|
@ -363,10 +363,10 @@ class AtomDecls
|
||||
}
|
||||
|
||||
/* Return the definition at the head of the chain for |atom|. */
|
||||
inline Definition *lookupFirst(JSAtom *atom);
|
||||
inline Definition *lookupFirst(JSAtom *atom) const;
|
||||
|
||||
/* Perform a lookup that can iterate over the definitions associated with |atom|. */
|
||||
inline DefinitionList::Range lookupMulti(JSAtom *atom);
|
||||
inline DefinitionList::Range lookupMulti(JSAtom *atom) const;
|
||||
|
||||
/* Add-or-update a known-unique definition for |atom|. */
|
||||
inline bool addUnique(JSAtom *atom, Definition *defn);
|
||||
@ -394,7 +394,7 @@ class AtomDecls
|
||||
}
|
||||
}
|
||||
|
||||
AtomDefnListMap::Range all() {
|
||||
AtomDefnListMap::Range all() const {
|
||||
JS_ASSERT(map);
|
||||
return map->all();
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ UpvarCookie::set(JSContext *cx, unsigned newLevel, uint16_t newSlot)
|
||||
}
|
||||
|
||||
inline PropertyName *
|
||||
ParseNode::atom() const
|
||||
ParseNode::name() const
|
||||
{
|
||||
JS_ASSERT(isKind(PNK_FUNCTION) || isKind(PNK_NAME) || isKind(PNK_INTRINSICNAME));
|
||||
JSAtom *atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->atom : pn_atom;
|
||||
|
@ -14,11 +14,12 @@
|
||||
|
||||
#include "frontend/ParseMaps.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "frontend/TreeContext.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
struct TreeContext;
|
||||
|
||||
/*
|
||||
* Indicates a location in the stack that an upvar value can be retrieved from
|
||||
* as a two tuple of (level, slot).
|
||||
@ -161,7 +162,6 @@ enum ParseNodeKind {
|
||||
PNK_FORIN,
|
||||
PNK_FORHEAD,
|
||||
PNK_ARGSBODY,
|
||||
PNK_UPVARS,
|
||||
PNK_SPREAD,
|
||||
|
||||
/*
|
||||
@ -219,14 +219,13 @@ enum ParseNodeKind {
|
||||
* object containing arg and var properties. We
|
||||
* create the function object at parse (not emit)
|
||||
* time to specialize arg and var bytecodes early.
|
||||
* pn_body: PNK_UPVARS if the function's source body
|
||||
* depends on outer names,
|
||||
* PNK_ARGSBODY if formal parameters,
|
||||
* pn_body: PNK_ARGSBODY if formal parameters,
|
||||
* PNK_STATEMENTLIST node for function body
|
||||
* statements,
|
||||
* PNK_RETURN for expression closure, or
|
||||
* PNK_SEQ for expression closure with
|
||||
* destructured formal parameters
|
||||
* PNK_LP see isGeneratorExpr def below
|
||||
* pn_cookie: static level and var index for function
|
||||
* pn_dflags: PND_* definition/use flags (see below)
|
||||
* pn_blockid: block id number
|
||||
@ -234,10 +233,6 @@ enum ParseNodeKind {
|
||||
* PNK_STATEMENTLIST node for function body
|
||||
* statements as final element
|
||||
* pn_count: 1 + number of formal parameters
|
||||
* PNK_UPVARS nameset pn_names: lexical dependencies (js::Definitions)
|
||||
* defined in enclosing scopes, or ultimately not
|
||||
* defined (free variables, either global property
|
||||
* references or reference errors).
|
||||
* pn_tree: PNK_ARGSBODY or PNK_STATEMENTLIST node
|
||||
* PNK_SPREAD unary pn_kid: expression being spread
|
||||
*
|
||||
@ -701,7 +696,7 @@ struct ParseNode {
|
||||
newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
|
||||
Parser *parser);
|
||||
|
||||
inline PropertyName *atom() const;
|
||||
inline PropertyName *name() const;
|
||||
|
||||
/*
|
||||
* The pn_expr and lexdef members are arms of an unsafe union. Unless you
|
||||
@ -827,13 +822,8 @@ struct ParseNode {
|
||||
bool isGeneratorExpr() const {
|
||||
if (getKind() == PNK_LP) {
|
||||
ParseNode *callee = this->pn_head;
|
||||
if (callee->getKind() == PNK_FUNCTION) {
|
||||
ParseNode *body = (callee->pn_body->getKind() == PNK_UPVARS)
|
||||
? callee->pn_body->pn_tree
|
||||
: callee->pn_body;
|
||||
if (body->getKind() == PNK_LEXICALSCOPE)
|
||||
return true;
|
||||
}
|
||||
if (callee->getKind() == PNK_FUNCTION && callee->pn_body->getKind() == PNK_LEXICALSCOPE)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -841,9 +831,7 @@ struct ParseNode {
|
||||
ParseNode *generatorExpr() const {
|
||||
JS_ASSERT(isGeneratorExpr());
|
||||
ParseNode *callee = this->pn_head;
|
||||
ParseNode *body = callee->pn_body->getKind() == PNK_UPVARS
|
||||
? callee->pn_body->pn_tree
|
||||
: callee->pn_body;
|
||||
ParseNode *body = callee->pn_body;
|
||||
JS_ASSERT(body->getKind() == PNK_LEXICALSCOPE);
|
||||
return body->pn_expr;
|
||||
}
|
||||
@ -1456,45 +1444,6 @@ struct ObjectBox {
|
||||
ObjectBox(ObjectBox *traceLink, JSObject *obj);
|
||||
};
|
||||
|
||||
struct FunctionBox : public ObjectBox
|
||||
{
|
||||
ParseNode *node;
|
||||
FunctionBox *siblings;
|
||||
FunctionBox *kids;
|
||||
FunctionBox *parent;
|
||||
Bindings bindings; /* bindings for this function */
|
||||
size_t bufStart;
|
||||
size_t bufEnd;
|
||||
uint16_t level;
|
||||
uint16_t ndefaults;
|
||||
StrictMode::StrictModeState strictModeState;
|
||||
bool inLoop:1; /* in a loop in parent function */
|
||||
bool inWith:1; /* some enclosing scope is a with-statement
|
||||
or E4X filter-expression */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
|
||||
ContextFlags cxFlags;
|
||||
|
||||
FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseNode *fn, TreeContext *tc,
|
||||
StrictMode::StrictModeState sms);
|
||||
|
||||
bool funIsHeavyweight() const { return cxFlags.funIsHeavyweight; }
|
||||
bool funIsGenerator() const { return cxFlags.funIsGenerator; }
|
||||
bool funHasExtensibleScope() const { return cxFlags.funHasExtensibleScope; }
|
||||
|
||||
void setFunIsHeavyweight() { cxFlags.funIsHeavyweight = true; }
|
||||
|
||||
JSFunction *function() const { return (JSFunction *) object; }
|
||||
|
||||
/*
|
||||
* True if this function is inside the scope of a with-statement, an E4X
|
||||
* filter-expression, or a function that uses direct eval.
|
||||
*/
|
||||
bool inAnyDynamicScope() const;
|
||||
|
||||
void recursivelySetStrictMode(StrictMode::StrictModeState strictness);
|
||||
};
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -327,7 +327,8 @@ Parser::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
|
||||
}
|
||||
|
||||
bool
|
||||
DefineArg(ParseNode *pn, HandlePropertyName name, unsigned i, Parser *parser);
|
||||
DefineArg(Parser *parser, ParseNode *funcpn, HandlePropertyName name, bool destructuringArg = false,
|
||||
Definition **duplicatedArg = NULL);
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
@ -18,66 +18,6 @@
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
static void
|
||||
FlagHeavyweights(Definition *dn, FunctionBox *funbox, bool *isHeavyweight, bool topInFunction)
|
||||
{
|
||||
unsigned dnLevel = dn->frameLevel();
|
||||
|
||||
while ((funbox = funbox->parent) != NULL) {
|
||||
/*
|
||||
* Notice that funbox->level is the static level of the definition or
|
||||
* expression of the function parsed into funbox, not the static level
|
||||
* of its body. Therefore we must add 1 to match dn's level to find the
|
||||
* funbox whose body contains the dn definition.
|
||||
*/
|
||||
if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
|
||||
funbox->setFunIsHeavyweight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!funbox && topInFunction)
|
||||
*isHeavyweight = true;
|
||||
}
|
||||
|
||||
static void
|
||||
SetFunctionKinds(FunctionBox *funbox, bool *isHeavyweight, bool topInFunction, bool isDirectEval)
|
||||
{
|
||||
for (; funbox; funbox = funbox->siblings) {
|
||||
ParseNode *fn = funbox->node;
|
||||
if (!fn)
|
||||
continue;
|
||||
|
||||
ParseNode *pn = fn->pn_body;
|
||||
if (!pn)
|
||||
continue;
|
||||
|
||||
if (funbox->kids)
|
||||
SetFunctionKinds(funbox->kids, isHeavyweight, topInFunction, isDirectEval);
|
||||
|
||||
JS_ASSERT(funbox->function()->isInterpreted());
|
||||
if (pn->isKind(PNK_UPVARS)) {
|
||||
/*
|
||||
* We loop again over all upvars, and for each non-free upvar,
|
||||
* ensure that its containing function has been flagged as
|
||||
* heavyweight.
|
||||
*
|
||||
* The emitter must see funIsHeavyweight() accurately before
|
||||
* generating any code for a tree of nested functions.
|
||||
*/
|
||||
AtomDefnMapPtr upvars = pn->pn_names;
|
||||
JS_ASSERT(!upvars->empty());
|
||||
|
||||
for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
|
||||
Definition *defn = r.front().value();
|
||||
Definition *lexdep = defn->resolve();
|
||||
if (!lexdep->isFreeVar())
|
||||
FlagHeavyweights(lexdep, funbox, isHeavyweight, topInFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the FunctionBox tree looking for functions whose call objects may
|
||||
* acquire new bindings as they execute: non-strict functions that call eval,
|
||||
@ -119,18 +59,12 @@ MarkExtensibleScopeDescendants(JSContext *context, FunctionBox *funbox, bool has
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::AnalyzeFunctions(Parser *parser, StackFrame *callerFrame)
|
||||
frontend::AnalyzeFunctions(Parser *parser)
|
||||
{
|
||||
TreeContext *tc = parser->tc;
|
||||
SharedContext *sc = tc->sc;
|
||||
if (!tc->functionList)
|
||||
return true;
|
||||
if (!MarkExtensibleScopeDescendants(sc->context, tc->functionList, false))
|
||||
if (!MarkExtensibleScopeDescendants(tc->sc->context, tc->functionList, false))
|
||||
return false;
|
||||
bool isDirectEval = !!callerFrame;
|
||||
bool isHeavyweight = false;
|
||||
SetFunctionKinds(tc->functionList, &isHeavyweight, sc->inFunction(), isDirectEval);
|
||||
if (isHeavyweight)
|
||||
sc->setFunIsHeavyweight();
|
||||
return true;
|
||||
}
|
||||
|
@ -17,12 +17,11 @@ namespace frontend {
|
||||
struct Parser;
|
||||
|
||||
/*
|
||||
* For each function in the compilation unit given by sc and functionList,
|
||||
* decide whether the function is a full closure or a null closure and set
|
||||
* JSFunction flags accordingly.
|
||||
* Called between parsing a top-level function/statement and emitting it.
|
||||
* Currently, all this function does is set the "has extensible parents" bit.
|
||||
*/
|
||||
bool
|
||||
AnalyzeFunctions(Parser *parser, StackFrame *callerFrame);
|
||||
AnalyzeFunctions(Parser *parser);
|
||||
|
||||
} /* namespace frontend */
|
||||
} /* namespace js */
|
||||
|
@ -23,8 +23,6 @@ SharedContext::SharedContext(JSContext *cx, JSObject *scopeChain, JSFunction *fu
|
||||
fun_(cx, fun),
|
||||
funbox_(funbox),
|
||||
scopeChain_(cx, scopeChain),
|
||||
bindings(),
|
||||
bindingsRoot(cx, &bindings),
|
||||
cxFlags(cx),
|
||||
strictModeState(sms)
|
||||
{
|
||||
@ -69,7 +67,9 @@ TreeContext::TreeContext(Parser *prs, SharedContext *sc, unsigned staticLevel, u
|
||||
parenDepth(0),
|
||||
yieldCount(0),
|
||||
blockNode(NULL),
|
||||
decls(prs->context),
|
||||
decls_(prs->context),
|
||||
args_(prs->context),
|
||||
vars_(prs->context),
|
||||
yieldNode(NULL),
|
||||
functionList(NULL),
|
||||
queuedStrictModeError(NULL),
|
||||
@ -81,7 +81,8 @@ TreeContext::TreeContext(Parser *prs, SharedContext *sc, unsigned staticLevel, u
|
||||
hasReturnExpr(false),
|
||||
hasReturnVoid(false),
|
||||
inForInit(false),
|
||||
inDeclDestructuring(false)
|
||||
inDeclDestructuring(false),
|
||||
hasDuplicateArgument_(false)
|
||||
{
|
||||
prs->tc = this;
|
||||
}
|
||||
@ -92,7 +93,7 @@ TreeContext::init()
|
||||
if (!frontend::GenerateBlockId(this, this->bodyid))
|
||||
return false;
|
||||
|
||||
return decls.init() && lexdeps.ensureMap(sc->context);
|
||||
return decls_.init() && lexdeps.ensureMap(sc->context);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "jsatominlines.h"
|
||||
|
||||
#include "frontend/ParseNode-inl.h"
|
||||
#include "frontend/TreeContext-inl.h"
|
||||
#include "vm/ScopeObject-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
@ -17,12 +18,6 @@
|
||||
using namespace js;
|
||||
using namespace js::frontend;
|
||||
|
||||
void
|
||||
TreeContext::trace(JSTracer *trc)
|
||||
{
|
||||
sc->bindings.trace(trc);
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid)
|
||||
{
|
||||
@ -35,3 +30,182 @@ frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* See comment on member function declaration. */
|
||||
bool
|
||||
TreeContext::define(JSContext *cx, PropertyName *name, ParseNode *pn, Definition::Kind kind)
|
||||
{
|
||||
JS_ASSERT(!pn->isUsed());
|
||||
JS_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
|
||||
|
||||
Definition *prevDef = NULL;
|
||||
if (kind == Definition::LET)
|
||||
prevDef = decls_.lookupFirst(name);
|
||||
else
|
||||
JS_ASSERT(!decls_.lookupFirst(name));
|
||||
|
||||
if (!prevDef)
|
||||
prevDef = lexdeps.lookupDefn(name);
|
||||
|
||||
if (prevDef) {
|
||||
ParseNode **pnup = &prevDef->dn_uses;
|
||||
ParseNode *pnu;
|
||||
unsigned start = (kind == Definition::LET) ? pn->pn_blockid : bodyid;
|
||||
|
||||
while ((pnu = *pnup) != NULL && pnu->pn_blockid >= start) {
|
||||
JS_ASSERT(pnu->isUsed());
|
||||
pnu->pn_lexdef = (Definition *) pn;
|
||||
pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
|
||||
pnup = &pnu->pn_link;
|
||||
}
|
||||
|
||||
if (!pnu || pnu != prevDef->dn_uses) {
|
||||
*pnup = pn->dn_uses;
|
||||
pn->dn_uses = prevDef->dn_uses;
|
||||
prevDef->dn_uses = pnu;
|
||||
|
||||
if ((!pnu || pnu->pn_blockid < bodyid) && prevDef->isPlaceholder())
|
||||
lexdeps->remove(name);
|
||||
}
|
||||
|
||||
pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED;
|
||||
}
|
||||
|
||||
JS_ASSERT_IF(kind != Definition::LET, !lexdeps->lookup(name));
|
||||
pn->setDefn(true);
|
||||
pn->pn_dflags &= ~PND_PLACEHOLDER;
|
||||
if (kind == Definition::CONST)
|
||||
pn->pn_dflags |= PND_CONST;
|
||||
|
||||
Definition *dn = (Definition *)pn;
|
||||
switch (kind) {
|
||||
case Definition::ARG:
|
||||
JS_ASSERT(sc->inFunction());
|
||||
dn->setOp(JSOP_GETARG);
|
||||
dn->pn_dflags |= PND_BOUND;
|
||||
if (!dn->pn_cookie.set(cx, staticLevel, args_.length()))
|
||||
return false;
|
||||
if (!args_.append(dn))
|
||||
return false;
|
||||
if (name == cx->runtime->atomState.emptyAtom)
|
||||
break;
|
||||
if (!decls_.addUnique(name, dn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Definition::CONST:
|
||||
case Definition::VAR:
|
||||
if (sc->inFunction()) {
|
||||
dn->setOp(JSOP_GETLOCAL);
|
||||
dn->pn_dflags |= PND_BOUND;
|
||||
if (!dn->pn_cookie.set(cx, staticLevel, vars_.length()))
|
||||
return false;
|
||||
if (!vars_.append(dn))
|
||||
return false;
|
||||
}
|
||||
if (!decls_.addUnique(name, dn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Definition::LET:
|
||||
dn->setOp(JSOP_GETLOCAL);
|
||||
dn->pn_dflags |= (PND_LET | PND_BOUND);
|
||||
JS_ASSERT(dn->pn_cookie.level() == staticLevel); /* see BindLet */
|
||||
if (!decls_.addShadow(name, dn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Definition::PLACEHOLDER:
|
||||
case Definition::NAMED_LAMBDA:
|
||||
JS_NOT_REACHED("unexpected kind");
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TreeContext::prepareToAddDuplicateArg(Definition *prevDecl)
|
||||
{
|
||||
JS_ASSERT(prevDecl->kind() == Definition::ARG);
|
||||
JS_ASSERT(decls_.lookupFirst(prevDecl->name()) == prevDecl);
|
||||
hasDuplicateArgument_ = true;
|
||||
decls_.remove(prevDecl->name());
|
||||
}
|
||||
|
||||
void
|
||||
TreeContext::updateDecl(JSAtom *atom, ParseNode *pn)
|
||||
{
|
||||
Definition *oldDecl = decls_.lookupFirst(atom);
|
||||
|
||||
pn->setDefn(true);
|
||||
Definition *newDecl = (Definition *)pn;
|
||||
decls_.updateFirst(atom, newDecl);
|
||||
|
||||
if (!sc->inFunction()) {
|
||||
JS_ASSERT(newDecl->isFreeVar());
|
||||
return;
|
||||
}
|
||||
|
||||
JS_ASSERT(oldDecl->isBound());
|
||||
JS_ASSERT(!oldDecl->pn_cookie.isFree());
|
||||
newDecl->pn_cookie = oldDecl->pn_cookie;
|
||||
newDecl->pn_dflags |= PND_BOUND;
|
||||
if (JOF_OPTYPE(oldDecl->getOp()) == JOF_QARG) {
|
||||
newDecl->setOp(JSOP_GETARG);
|
||||
JS_ASSERT(args_[oldDecl->pn_cookie.slot()] == oldDecl);
|
||||
args_[oldDecl->pn_cookie.slot()] = newDecl;
|
||||
} else {
|
||||
JS_ASSERT(JOF_OPTYPE(oldDecl->getOp()) == JOF_LOCAL);
|
||||
newDecl->setOp(JSOP_GETLOCAL);
|
||||
JS_ASSERT(vars_[oldDecl->pn_cookie.slot()] == oldDecl);
|
||||
vars_[oldDecl->pn_cookie.slot()] = newDecl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeContext::popLetDecl(JSAtom *atom)
|
||||
{
|
||||
JS_ASSERT(decls_.lookupFirst(atom)->isLet());
|
||||
decls_.remove(atom);
|
||||
}
|
||||
|
||||
bool
|
||||
TreeContext::generateBindings(JSContext *cx, Bindings *bindings) const
|
||||
{
|
||||
JS_ASSERT(sc->inFunction());
|
||||
|
||||
if (hasDuplicateArgument_)
|
||||
bindings->noteDup();
|
||||
|
||||
if (!bindings->ensureShape(cx))
|
||||
return false;
|
||||
|
||||
bool hasAnyClosedVar = false;
|
||||
|
||||
Rooted<JSAtom *> atom(cx);
|
||||
for (unsigned i = 0; i < args_.length(); ++i) {
|
||||
Definition *arg = args_[i];
|
||||
JS_ASSERT(arg->kind() == Definition::ARG);
|
||||
atom = arg->name();
|
||||
if (arg->isClosed())
|
||||
hasAnyClosedVar = true;
|
||||
if (!bindings->add(cx, atom, ARGUMENT, arg->isClosed()))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < vars_.length(); ++i) {
|
||||
Definition *var = vars_[i];
|
||||
JS_ASSERT(var->kind() == Definition::VAR || var->kind() == Definition::CONST);
|
||||
atom = var->name();
|
||||
if (var->isClosed())
|
||||
hasAnyClosedVar = true;
|
||||
BindingKind kind = var->kind() == Definition::VAR ? VARIABLE : CONSTANT;
|
||||
if (!bindings->add(cx, atom, kind, var->isClosed()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasAnyClosedVar || sc->bindingsAccessedDynamically() || sc->funHasExtensibleScope())
|
||||
sc->fun()->flags |= JSFUN_HEAVYWEIGHT;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "jspubtd.h"
|
||||
|
||||
#include "frontend/ParseMaps.h"
|
||||
|
||||
#include "frontend/ParseNode.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
|
||||
namespace js {
|
||||
@ -54,9 +54,6 @@ class ContextFlags {
|
||||
//
|
||||
bool bindingsAccessedDynamically:1;
|
||||
|
||||
// The function needs Call object per call.
|
||||
bool funIsHeavyweight:1;
|
||||
|
||||
// We parsed a yield statement in the function.
|
||||
bool funIsGenerator:1;
|
||||
|
||||
@ -111,7 +108,6 @@ class ContextFlags {
|
||||
ContextFlags(JSContext *cx)
|
||||
: hasExplicitUseStrict(false),
|
||||
bindingsAccessedDynamically(false),
|
||||
funIsHeavyweight(false),
|
||||
funIsGenerator(false),
|
||||
funMightAliasLocals(false),
|
||||
funHasExtensibleScope(false),
|
||||
@ -139,10 +135,6 @@ struct SharedContext {
|
||||
const RootedObject scopeChain_; /* scope chain object for the script */
|
||||
|
||||
public:
|
||||
Bindings bindings; /* bindings in this code, including
|
||||
arguments if we're compiling a function */
|
||||
Bindings::AutoRooter bindingsRoot; /* root for stack allocated bindings. */
|
||||
|
||||
ContextFlags cxFlags;
|
||||
|
||||
|
||||
@ -179,22 +171,20 @@ struct SharedContext {
|
||||
|
||||
bool hasExplicitUseStrict() const { return cxFlags.hasExplicitUseStrict; }
|
||||
bool bindingsAccessedDynamically() const { return cxFlags.bindingsAccessedDynamically; }
|
||||
bool funIsHeavyweight() const { INFUNC; return cxFlags.funIsHeavyweight; }
|
||||
bool funIsGenerator() const { INFUNC; return cxFlags.funIsGenerator; }
|
||||
bool funMightAliasLocals() const { return cxFlags.funMightAliasLocals; }
|
||||
bool funHasExtensibleScope() const { return cxFlags.funHasExtensibleScope; }
|
||||
bool funArgumentsHasLocalBinding() const { INFUNC; return cxFlags.funArgumentsHasLocalBinding; }
|
||||
bool funDefinitelyNeedsArgsObj() const { INFUNC; return cxFlags.funDefinitelyNeedsArgsObj; }
|
||||
|
||||
void setExplicitUseStrict() { cxFlags.hasExplicitUseStrict = true; }
|
||||
void setBindingsAccessedDynamically() { cxFlags.bindingsAccessedDynamically = true; }
|
||||
void setFunIsHeavyweight() { cxFlags.funIsHeavyweight = true; }
|
||||
void setFunIsGenerator() { INFUNC; cxFlags.funIsGenerator = true; }
|
||||
void setFunMightAliasLocals() { cxFlags.funMightAliasLocals = true; }
|
||||
void setFunHasExtensibleScope() { cxFlags.funHasExtensibleScope = true; }
|
||||
void setFunArgumentsHasLocalBinding() { INFUNC; cxFlags.funArgumentsHasLocalBinding = true; }
|
||||
void setFunDefinitelyNeedsArgsObj() { JS_ASSERT(cxFlags.funArgumentsHasLocalBinding);
|
||||
INFUNC; cxFlags.funDefinitelyNeedsArgsObj = true; }
|
||||
void setExplicitUseStrict() { cxFlags.hasExplicitUseStrict = true; }
|
||||
void setBindingsAccessedDynamically() { cxFlags.bindingsAccessedDynamically = true; }
|
||||
void setFunIsGenerator() { INFUNC; cxFlags.funIsGenerator = true; }
|
||||
void setFunMightAliasLocals() { cxFlags.funMightAliasLocals = true; }
|
||||
void setFunHasExtensibleScope() { cxFlags.funHasExtensibleScope = true; }
|
||||
void setFunArgumentsHasLocalBinding() { INFUNC; cxFlags.funArgumentsHasLocalBinding = true; }
|
||||
void setFunDefinitelyNeedsArgsObj() { JS_ASSERT(cxFlags.funArgumentsHasLocalBinding);
|
||||
INFUNC; cxFlags.funDefinitelyNeedsArgsObj = true; }
|
||||
|
||||
#undef INFUNC
|
||||
|
||||
@ -213,6 +203,8 @@ typedef HashSet<JSAtom *> FuncStmtSet;
|
||||
struct Parser;
|
||||
struct StmtInfoTC;
|
||||
|
||||
typedef Vector<Definition *, 16> DeclVector;
|
||||
|
||||
/*
|
||||
* The struct TreeContext stores information about the current parsing context,
|
||||
* which is part of the parser state (see the field Parser::tc). The current
|
||||
@ -243,7 +235,87 @@ struct TreeContext { /* tree context for semantic checks */
|
||||
non-zero depth in current paren tree */
|
||||
ParseNode *blockNode; /* parse node for a block with let declarations
|
||||
(block with its own lexical scope) */
|
||||
AtomDecls decls; /* function, const, and var declarations */
|
||||
private:
|
||||
AtomDecls decls_; /* function, const, and var declarations */
|
||||
DeclVector args_; /* argument definitions */
|
||||
DeclVector vars_; /* var/const definitions */
|
||||
|
||||
public:
|
||||
const AtomDecls &decls() const {
|
||||
return decls_;
|
||||
}
|
||||
|
||||
uint32_t numArgs() const {
|
||||
JS_ASSERT(sc->inFunction());
|
||||
return args_.length();
|
||||
}
|
||||
|
||||
uint32_t numVars() const {
|
||||
JS_ASSERT(sc->inFunction());
|
||||
return vars_.length();
|
||||
}
|
||||
|
||||
/*
|
||||
* This function adds a definition to the lexical scope represented by this
|
||||
* TreeContext.
|
||||
*
|
||||
* Pre-conditions:
|
||||
* + The caller must have already taken care of name collisions:
|
||||
* - For non-let definitions, this means 'name' isn't in 'decls'.
|
||||
* - For let definitions, this means 'name' isn't already a name in the
|
||||
* current block.
|
||||
* + The given 'pn' is either a placeholder (created by a previous unbound
|
||||
* use) or an un-bound un-linked name node.
|
||||
* + The given 'kind' is one of ARG, CONST, VAR, or LET. In particular,
|
||||
* NAMED_LAMBDA is handled in an ad hoc special case manner (see
|
||||
* LeaveFunction) that we should consider rewriting.
|
||||
*
|
||||
* Post-conditions:
|
||||
* + tc->decls().lookupFirst(name) == pn
|
||||
* + The given name 'pn' has been converted in-place into a
|
||||
* non-placeholder definition.
|
||||
* + If this is a function scope (sc->inFunction), 'pn' is bound to a
|
||||
* particular local/argument slot.
|
||||
* + PND_CONST is set for Definition::COSNT
|
||||
* + Pre-existing uses of pre-existing placeholders have been linked to
|
||||
* 'pn' if they are in the scope of 'pn'.
|
||||
* + Pre-existing placeholders in the scope of 'pn' have been removed.
|
||||
*/
|
||||
bool define(JSContext *cx, PropertyName *name, ParseNode *pn, Definition::Kind);
|
||||
|
||||
/*
|
||||
* Let definitions may shadow same-named definitions in enclosing scopes.
|
||||
* To represesent this, 'decls' is not a plain map, but actually:
|
||||
* decls :: name -> stack of definitions
|
||||
* New bindings are pushed onto the stack, name lookup always refers to the
|
||||
* top of the stack, and leaving a block scope calls popLetDecl for each
|
||||
* name in the block's scope.
|
||||
*/
|
||||
void popLetDecl(JSAtom *atom);
|
||||
|
||||
/* See the sad story in DefineArg. */
|
||||
void prepareToAddDuplicateArg(Definition *prevDecl);
|
||||
|
||||
/* See the sad story in MakeDefIntoUse. */
|
||||
void updateDecl(JSAtom *atom, ParseNode *newDecl);
|
||||
|
||||
/*
|
||||
* After a function body has been parsed, the parser generates the
|
||||
* function's "bindings". Bindings are a data-structure, ultimately stored
|
||||
* in the compiled JSScript, that serve three purposes:
|
||||
* - After parsing, the TreeContext is destroyed and 'decls' along with
|
||||
* it. Mostly, the emitter just uses the binding information stored in
|
||||
* the use/def nodes, but the emitter occasionally needs 'bindings' for
|
||||
* various scope-related queries.
|
||||
* - Bindings provide the initial js::Shape to use when creating a dynamic
|
||||
* scope object (js::CallObject) for the function. This shape is used
|
||||
* during dynamic name lookup.
|
||||
* - Sometimes a script's bindings are accessed at runtime to retrieve the
|
||||
* contents of the lexical scope (e.g., from the debugger).
|
||||
*/
|
||||
bool generateBindings(JSContext *cx, Bindings *bindings) const;
|
||||
|
||||
public:
|
||||
ParseNode *yieldNode; /* parse node for a yield expression that might
|
||||
be an error if we turn out to be inside a
|
||||
generator expression */
|
||||
@ -290,7 +362,10 @@ struct TreeContext { /* tree context for semantic checks */
|
||||
// they need to be treated differently.
|
||||
bool inDeclDestructuring:1;
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
private:
|
||||
// Set to indicate args_ contains a duplicate
|
||||
bool hasDuplicateArgument_:1;
|
||||
public:
|
||||
|
||||
inline TreeContext(Parser *prs, SharedContext *sc, unsigned staticLevel, uint32_t bodyid);
|
||||
inline ~TreeContext();
|
||||
@ -413,6 +488,42 @@ struct StmtInfoTC : public StmtInfoBase {
|
||||
StmtInfoTC(JSContext *cx) : StmtInfoBase(cx), isFunctionBodyBlock(false) {}
|
||||
};
|
||||
|
||||
struct FunctionBox : public ObjectBox
|
||||
{
|
||||
ParseNode *node;
|
||||
FunctionBox *siblings;
|
||||
FunctionBox *kids;
|
||||
FunctionBox *parent;
|
||||
Bindings bindings; /* bindings for this function */
|
||||
size_t bufStart;
|
||||
size_t bufEnd;
|
||||
uint16_t level;
|
||||
uint16_t ndefaults;
|
||||
StrictMode::StrictModeState strictModeState;
|
||||
bool inLoop:1; /* in a loop in parent function */
|
||||
bool inWith:1; /* some enclosing scope is a with-statement
|
||||
or E4X filter-expression */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
|
||||
ContextFlags cxFlags;
|
||||
|
||||
FunctionBox(ObjectBox* traceListHead, JSObject *obj, ParseNode *fn, TreeContext *tc,
|
||||
StrictMode::StrictModeState sms);
|
||||
|
||||
bool funIsGenerator() const { return cxFlags.funIsGenerator; }
|
||||
bool funHasExtensibleScope() const { return cxFlags.funHasExtensibleScope; }
|
||||
|
||||
JSFunction *function() const { return (JSFunction *) object; }
|
||||
|
||||
/*
|
||||
* True if this function is inside the scope of a with-statement, an E4X
|
||||
* filter-expression, or a function that uses direct eval.
|
||||
*/
|
||||
bool inAnyDynamicScope() const;
|
||||
|
||||
void recursivelySetStrictMode(StrictMode::StrictModeState strictness);
|
||||
};
|
||||
|
||||
bool
|
||||
GenerateBlockId(TreeContext *tc, uint32_t &blockid);
|
||||
|
||||
|
@ -243,6 +243,7 @@ DeclMarkerImpl(String, JSAtom)
|
||||
DeclMarkerImpl(String, JSString)
|
||||
DeclMarkerImpl(String, JSFlatString)
|
||||
DeclMarkerImpl(String, JSLinearString)
|
||||
DeclMarkerImpl(String, PropertyName)
|
||||
DeclMarkerImpl(TypeObject, types::TypeObject)
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
DeclMarkerImpl(XML, JSXML)
|
||||
|
@ -85,6 +85,7 @@ DeclMarker(String, JSAtom)
|
||||
DeclMarker(String, JSString)
|
||||
DeclMarker(String, JSFlatString)
|
||||
DeclMarker(String, JSLinearString)
|
||||
DeclMarker(String, PropertyName)
|
||||
DeclMarker(TypeObject, types::TypeObject)
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
DeclMarker(XML, JSXML)
|
||||
|
@ -0,0 +1,6 @@
|
||||
// |jit-test| error:SyntaxError
|
||||
|
||||
function test() {
|
||||
arguments;
|
||||
let (arguments);
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
function runTest() {
|
||||
if (Math) {
|
||||
function createTester(options) {
|
||||
return function() {
|
||||
return options.blah;
|
||||
};
|
||||
}
|
||||
|
||||
return createTester({blah:"bar"});
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(runTest()(), "bar");
|
@ -0,0 +1,4 @@
|
||||
with ({b:1}) {
|
||||
const [ b ] = [];
|
||||
}
|
||||
assertEq(b, undefined);
|
@ -5487,11 +5487,10 @@ JS::CompileFunction(JSContext *cx, HandleObject obj, CompileOptions options,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Bindings bindings;
|
||||
AutoNameVector formals(cx);
|
||||
for (unsigned i = 0; i < nargs; i++) {
|
||||
uint16_t dummy;
|
||||
RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i])));
|
||||
if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy))
|
||||
if (!argAtom || !formals.append(argAtom->asPropertyName()))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -5499,7 +5498,7 @@ JS::CompileFunction(JSContext *cx, HandleObject obj, CompileOptions options,
|
||||
if (!fun)
|
||||
return NULL;
|
||||
|
||||
if (!frontend::CompileFunctionBody(cx, fun, options, &bindings, chars, length))
|
||||
if (!frontend::CompileFunctionBody(cx, fun, options, formals, chars, length))
|
||||
return NULL;
|
||||
|
||||
if (obj && funAtom) {
|
||||
|
@ -1062,7 +1062,8 @@ class JS_PUBLIC_API(AutoGCRooter) {
|
||||
BINDINGS = -23, /* js::Bindings::AutoRooter */
|
||||
GETTERSETTER =-24, /* js::AutoRooterGetterSetter */
|
||||
REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */
|
||||
HASHABLEVALUE=-26
|
||||
NAMEVECTOR = -26, /* js::AutoNameVector */
|
||||
HASHABLEVALUE=-27
|
||||
};
|
||||
|
||||
private:
|
||||
@ -1255,6 +1256,8 @@ class AutoVectorRooter : protected AutoGCRooter
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
typedef T ElementType;
|
||||
|
||||
size_t length() const { return vector.length(); }
|
||||
bool empty() const { return vector.empty(); }
|
||||
|
||||
|
@ -430,7 +430,7 @@ JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **markp)
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bindings.length(); i++)
|
||||
names[i] = reinterpret_cast<uintptr_t>(bindings[i].maybeName);
|
||||
names[i] = reinterpret_cast<uintptr_t>(bindings[i].name);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
@ -657,7 +657,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
||||
for (unsigned i = 0; i < fun->nargs; i++) {
|
||||
if ((i && !out.append(", ")) ||
|
||||
(i == unsigned(fun->nargs - 1) && fun->hasRest() && !out.append("...")) ||
|
||||
!out.append((*localNames)[i].maybeName)) {
|
||||
!out.append((*localNames)[i].name)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -1166,8 +1166,8 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
Bindings bindings;
|
||||
Bindings::AutoRooter bindingsRoot(cx, &bindings);
|
||||
AutoKeepAtoms keepAtoms(cx->runtime);
|
||||
AutoNameVector formals(cx);
|
||||
|
||||
bool hasRest = false;
|
||||
|
||||
@ -1288,18 +1288,7 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for a duplicate parameter name. */
|
||||
Rooted<PropertyName*> name(cx, ts.currentToken().name());
|
||||
if (bindings.hasBinding(cx, name)) {
|
||||
JSAutoByteString bytes;
|
||||
if (!js_AtomToPrintableString(cx, name, &bytes))
|
||||
return false;
|
||||
if (!ts.reportStrictWarning(JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t dummy;
|
||||
if (!bindings.addArgument(cx, name, &dummy))
|
||||
if (!formals.append(ts.currentToken().name()))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -1316,6 +1305,11 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < formals.length(); ++i) {
|
||||
JSString *str = formals[i];
|
||||
JS_ASSERT(str->asAtom().asPropertyName() == formals[i]);
|
||||
}
|
||||
|
||||
JS::Anchor<JSString *> strAnchor(NULL);
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
@ -1341,14 +1335,14 @@ Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
* and so would a call to f from another top-level's script or function.
|
||||
*/
|
||||
RootedFunction fun(cx, js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
|
||||
global, cx->runtime->atomState.anonymousAtom));
|
||||
global, cx->runtime->atomState.anonymousAtom));
|
||||
if (!fun)
|
||||
return false;
|
||||
|
||||
if (hasRest)
|
||||
fun->setHasRest();
|
||||
|
||||
bool ok = frontend::CompileFunctionBody(cx, fun, options, &bindings, chars, length);
|
||||
bool ok = frontend::CompileFunctionBody(cx, fun, options, formals, chars, length);
|
||||
args.rval().setObject(*fun);
|
||||
return ok;
|
||||
}
|
||||
|
@ -2341,6 +2341,12 @@ AutoGCRooter::trace(JSTracer *trc)
|
||||
return;
|
||||
}
|
||||
|
||||
case NAMEVECTOR: {
|
||||
AutoNameVector::VectorImpl &vector = static_cast<AutoNameVector *>(this)->vector;
|
||||
MarkStringRootRange(trc, vector.length(), vector.begin(), "js::AutoNameVector.vector");
|
||||
return;
|
||||
}
|
||||
|
||||
case VALARRAY: {
|
||||
AutoValueArray *array = static_cast<AutoValueArray *>(this);
|
||||
MarkValueRootRange(trc, array->length(), array->start(), "js::AutoValueArray");
|
||||
|
@ -1753,7 +1753,7 @@ GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
|
||||
{
|
||||
LOCAL_ASSERT_RV(jp->fun, NULL);
|
||||
LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.count(), NULL);
|
||||
JSAtom *name = (*jp->localNames)[slot].maybeName;
|
||||
JSAtom *name = (*jp->localNames)[slot].name;
|
||||
#if !JS_HAS_DESTRUCTURING
|
||||
LOCAL_ASSERT_RV(name, NULL);
|
||||
#endif
|
||||
@ -5932,8 +5932,6 @@ ExpressionDecompiler::decompilePC(jsbytecode *pc)
|
||||
case JSOP_CALLARG: {
|
||||
unsigned slot = GET_ARGNO(pc);
|
||||
JSAtom *atom = getArg(slot);
|
||||
if (!atom)
|
||||
break; // Destructuring
|
||||
return write(atom);
|
||||
}
|
||||
case JSOP_GETLOCAL:
|
||||
@ -6108,7 +6106,7 @@ ExpressionDecompiler::getArg(unsigned slot)
|
||||
{
|
||||
JS_ASSERT(fun);
|
||||
JS_ASSERT(slot < script->bindings.count());
|
||||
return (*localNames)[slot].maybeName;
|
||||
return (*localNames)[slot].name;
|
||||
}
|
||||
|
||||
JSAtom *
|
||||
@ -6117,7 +6115,7 @@ ExpressionDecompiler::getVar(unsigned slot)
|
||||
JS_ASSERT(fun);
|
||||
slot += fun->nargs;
|
||||
JS_ASSERT(slot < script->bindings.count());
|
||||
return (*localNames)[slot].maybeName;
|
||||
return (*localNames)[slot].name;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -3011,17 +3011,13 @@ ASTSerializer::function(ParseNode *pn, ASTType type, Value *dst)
|
||||
NodeVector args(cx);
|
||||
NodeVector defaults(cx);
|
||||
|
||||
ParseNode *argsAndBody = pn->pn_body->isKind(PNK_UPVARS)
|
||||
? pn->pn_body->pn_tree
|
||||
: pn->pn_body;
|
||||
|
||||
Value body;
|
||||
Value rest;
|
||||
if (func->hasRest())
|
||||
rest.setUndefined();
|
||||
else
|
||||
rest.setNull();
|
||||
return functionArgsAndBody(argsAndBody, args, defaults, &body, &rest) &&
|
||||
return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) &&
|
||||
builder.function(type, &pn->pn_pos, id, args, defaults, body,
|
||||
rest, isGenerator, isExpression, dst);
|
||||
}
|
||||
|
@ -265,8 +265,9 @@ class BaseShape : public js::gc::Cell
|
||||
ITERATED_SINGLETON = 0x800,
|
||||
NEW_TYPE_UNKNOWN = 0x1000,
|
||||
UNCACHEABLE_PROTO = 0x2000,
|
||||
CLOSED_VAR = 0x4000, /* This bit will be removed in bug 767013 */
|
||||
|
||||
OBJECT_FLAG_MASK = 0x3ff8
|
||||
OBJECT_FLAG_MASK = 0x7ff8
|
||||
};
|
||||
|
||||
private:
|
||||
@ -310,6 +311,7 @@ class BaseShape : public js::gc::Cell
|
||||
inline BaseShape &operator=(const BaseShape &other);
|
||||
|
||||
bool isOwned() const { return !!(flags & OWNED_SHAPE); }
|
||||
bool isClosedVar() const { return !!(flags & CLOSED_VAR); }
|
||||
|
||||
inline bool matchesGetterSetter(PropertyOp rawGetter,
|
||||
StrictPropertyOp rawSetter) const;
|
||||
|
@ -84,7 +84,7 @@ BindingIter::settle()
|
||||
return;
|
||||
Shape &shape = shape_.front();
|
||||
jsid id = shape_.front().propid();
|
||||
binding_.maybeName = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->asPropertyName() : NULL;
|
||||
binding_.name = JSID_TO_ATOM(id)->asPropertyName();
|
||||
binding_.kind = shape.slot() - CallObject::RESERVED_SLOTS < bindings_->numArgs()
|
||||
? ARGUMENT
|
||||
: shape.writable() ? VARIABLE : CONSTANT;
|
||||
@ -125,7 +125,7 @@ Bindings::argumentsVarIndex(JSContext *cx) const
|
||||
}
|
||||
|
||||
bool
|
||||
Bindings::add(JSContext *cx, HandleAtom name, BindingKind kind)
|
||||
Bindings::add(JSContext *cx, HandleAtom name, BindingKind kind, bool aliased)
|
||||
{
|
||||
if (!ensureShape(cx))
|
||||
return false;
|
||||
@ -169,7 +169,8 @@ Bindings::add(JSContext *cx, HandleAtom name, BindingKind kind)
|
||||
id = AtomToId(name);
|
||||
}
|
||||
|
||||
StackBaseShape base(&CallClass, cx->global(), BaseShape::VAROBJ);
|
||||
uint32_t flags = BaseShape::VAROBJ | (aliased ? BaseShape::CLOSED_VAR : 0);
|
||||
StackBaseShape base(&CallClass, cx->global(), flags);
|
||||
UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
|
||||
if (!nbase)
|
||||
return false;
|
||||
@ -226,6 +227,29 @@ Bindings::callObjectShape(JSContext *cx) const
|
||||
return shape;
|
||||
}
|
||||
|
||||
bool
|
||||
Bindings::extractClosedArgsAndVars(JSContext *cx, SlotVector *args, SlotVector *vars)
|
||||
{
|
||||
if (!ensureShape(cx))
|
||||
return false;
|
||||
|
||||
for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) {
|
||||
Shape &shape = r.front();
|
||||
if (shape.base()->isClosedVar()) {
|
||||
unsigned i = shape.slot() - CallObject::RESERVED_SLOTS;
|
||||
if (i < nargs) {
|
||||
if (!args->append(i))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(i < count());
|
||||
if (!vars->append(i - nargs))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Bindings::trace(JSTracer *trc)
|
||||
{
|
||||
@ -402,63 +426,21 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
if (nameCount > 0) {
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* */
|
||||
unsigned bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
|
||||
uint32_t *bitmap = cx->tempLifoAlloc().newArray<uint32_t>(bitmapLength);
|
||||
if (!bitmap) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
BindingVector names(cx);
|
||||
if (mode == XDR_ENCODE) {
|
||||
if (!GetOrderedBindings(cx, script->bindings, &names))
|
||||
return false;
|
||||
PodZero(bitmap, bitmapLength);
|
||||
for (unsigned i = 0; i < nameCount; i++) {
|
||||
if (i < nargs && names[i].maybeName)
|
||||
bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < bitmapLength; ++i) {
|
||||
if (!xdr->codeUint32(&bitmap[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < nameCount; i++) {
|
||||
if (i < nargs &&
|
||||
!(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))))
|
||||
{
|
||||
if (mode == XDR_DECODE) {
|
||||
uint16_t dummy;
|
||||
if (!bindings.addDestructuring(cx, &dummy))
|
||||
return false;
|
||||
} else {
|
||||
JS_ASSERT(!names[i].maybeName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
RootedAtom name(cx);
|
||||
if (mode == XDR_ENCODE)
|
||||
name = names[i].maybeName;
|
||||
name = names[i].name;
|
||||
if (!XDRAtom(xdr, name.address()))
|
||||
return false;
|
||||
if (mode == XDR_DECODE) {
|
||||
BindingKind kind = (i < nargs)
|
||||
? ARGUMENT
|
||||
: (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? CONSTANT
|
||||
: VARIABLE);
|
||||
if (!bindings.add(cx, name, kind))
|
||||
BindingKind kind = (i < nargs) ? ARGUMENT : VARIABLE;
|
||||
if (!bindings.add(cx, name, kind, /* aliased = */ false))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -589,7 +571,7 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
nClosedVars, nTypeSets))
|
||||
return JS_FALSE;
|
||||
|
||||
script->bindings.transfer(&bindings);
|
||||
script->bindings.transferFrom(&bindings);
|
||||
JS_ASSERT(!script->mainOffset);
|
||||
script->mainOffset = prologLength;
|
||||
script->nfixed = uint16_t(version >> 16);
|
||||
@ -1690,6 +1672,10 @@ JSScript::fullyInitTrivial(JSContext *cx, Handle<JSScript*> script)
|
||||
/* static */ bool
|
||||
JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, BytecodeEmitter *bce)
|
||||
{
|
||||
Bindings::SlotVector closedArgs(cx), closedVars(cx);
|
||||
if (!script->bindings.extractClosedArgsAndVars(cx, &closedArgs, &closedVars))
|
||||
return false;
|
||||
|
||||
/* The counts of indexed things must be checked during code generation. */
|
||||
JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
|
||||
JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
|
||||
@ -1697,15 +1683,11 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
|
||||
|
||||
uint32_t mainLength = bce->offset();
|
||||
uint32_t prologLength = bce->prologOffset();
|
||||
|
||||
if (!bce->sc->bindings.ensureShape(cx))
|
||||
return false;
|
||||
|
||||
uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
|
||||
uint16_t nClosedArgs = uint16_t(bce->closedArgs.length());
|
||||
JS_ASSERT(nClosedArgs == bce->closedArgs.length());
|
||||
uint16_t nClosedVars = uint16_t(bce->closedVars.length());
|
||||
JS_ASSERT(nClosedVars == bce->closedVars.length());
|
||||
uint16_t nClosedArgs = uint16_t(closedArgs.length());
|
||||
JS_ASSERT(nClosedArgs == closedArgs.length());
|
||||
uint16_t nClosedVars = uint16_t(closedVars.length());
|
||||
JS_ASSERT(nClosedVars == closedVars.length());
|
||||
if (!partiallyInit(cx, script, prologLength + mainLength, nsrcnotes, bce->atomIndices->count(),
|
||||
bce->objectList.length, bce->regexpList.length, bce->ntrynotes,
|
||||
bce->constList.length(), nClosedArgs, nClosedVars,
|
||||
@ -1716,7 +1698,7 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
|
||||
script->mainOffset = prologLength;
|
||||
PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
|
||||
PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
|
||||
uint32_t nfixed = bce->sc->inFunction() ? bce->sc->bindings.numVars() : 0;
|
||||
uint32_t nfixed = bce->sc->inFunction() ? bce->script->bindings.numVars() : 0;
|
||||
JS_ASSERT(nfixed < SLOTNO_LIMIT);
|
||||
script->nfixed = uint16_t(nfixed);
|
||||
InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms);
|
||||
@ -1766,11 +1748,9 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
|
||||
}
|
||||
|
||||
if (nClosedArgs)
|
||||
PodCopy<uint32_t>(script->closedArgs()->vector, &bce->closedArgs[0], nClosedArgs);
|
||||
PodCopy<uint32_t>(script->closedArgs()->vector, &closedArgs[0], nClosedArgs);
|
||||
if (nClosedVars)
|
||||
PodCopy<uint32_t>(script->closedVars()->vector, &bce->closedVars[0], nClosedVars);
|
||||
|
||||
script->bindings.transfer(&bce->sc->bindings);
|
||||
PodCopy<uint32_t>(script->closedVars()->vector, &closedVars[0], nClosedVars);
|
||||
|
||||
RootedFunction fun(cx, NULL);
|
||||
if (bce->sc->inFunction()) {
|
||||
@ -2144,15 +2124,9 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < names.length(); ++i) {
|
||||
if (JSAtom *atom = names[i].maybeName) {
|
||||
Rooted<JSAtom*> root(cx, atom);
|
||||
if (!bindings.add(cx, root, names[i].kind))
|
||||
return NULL;
|
||||
} else {
|
||||
uint16_t _;
|
||||
if (!bindings.addDestructuring(cx, &_))
|
||||
return NULL;
|
||||
}
|
||||
Rooted<JSAtom*> atom(cx, names[i].name);
|
||||
if (!bindings.add(cx, atom, names[i].kind, /* aliasedVar = */ false))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!bindings.ensureShape(cx))
|
||||
@ -2221,7 +2195,7 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dst->bindings.transfer(&bindings);
|
||||
dst->bindings.transferFrom(&bindings);
|
||||
|
||||
/* This assignment must occur before all the Rebase calls. */
|
||||
dst->data = data;
|
||||
|
@ -87,7 +87,7 @@ enum BindingKind { ARGUMENT, VARIABLE, CONSTANT };
|
||||
|
||||
struct Binding
|
||||
{
|
||||
PropertyName *maybeName; /* NULL for destructuring formals. */
|
||||
PropertyName *name;
|
||||
BindingKind kind;
|
||||
};
|
||||
|
||||
@ -166,7 +166,7 @@ class Bindings
|
||||
* Bindings instance. Once such a transfer occurs, the old bindings must
|
||||
* not be used again.
|
||||
*/
|
||||
inline void transfer(Bindings *bindings);
|
||||
inline void transferFrom(Bindings *bindings);
|
||||
|
||||
uint16_t numArgs() const { return nargs; }
|
||||
uint16_t numVars() const { return nvars; }
|
||||
@ -190,6 +190,10 @@ class Bindings
|
||||
*/
|
||||
Shape *callObjectShape(JSContext *cx) const;
|
||||
|
||||
/* Extract a list of the closed-over args and vars. */
|
||||
typedef Vector<uint32_t, 32> SlotVector;
|
||||
bool extractClosedArgsAndVars(JSContext *cx, SlotVector *args, SlotVector *vars);
|
||||
|
||||
/* See Scope::extensibleParents */
|
||||
inline bool extensibleParents();
|
||||
bool setExtensibleParents(JSContext *cx);
|
||||
@ -214,25 +218,7 @@ class Bindings
|
||||
* runtime, by calling an "add" method. All ARGUMENT bindings must be added
|
||||
* before before any VARIABLE or CONSTANT bindings.
|
||||
*/
|
||||
bool add(JSContext *cx, HandleAtom name, BindingKind kind);
|
||||
|
||||
/* Convenience specializations. */
|
||||
bool addVariable(JSContext *cx, HandleAtom name) {
|
||||
return add(cx, name, VARIABLE);
|
||||
}
|
||||
bool addConstant(JSContext *cx, HandleAtom name) {
|
||||
return add(cx, name, CONSTANT);
|
||||
}
|
||||
bool addArgument(JSContext *cx, HandleAtom name, uint16_t *slotp) {
|
||||
JS_ASSERT(name != NULL); /* not destructuring */
|
||||
*slotp = nargs;
|
||||
return add(cx, name, ARGUMENT);
|
||||
}
|
||||
bool addDestructuring(JSContext *cx, uint16_t *slotp) {
|
||||
*slotp = nargs;
|
||||
Rooted<JSAtom*> atom(cx, NULL);
|
||||
return add(cx, atom, ARGUMENT);
|
||||
}
|
||||
bool add(JSContext *cx, HandleAtom name, BindingKind kind, bool aliased);
|
||||
|
||||
void noteDup() { hasDup_ = true; }
|
||||
bool hasDup() const { return hasDup_; }
|
||||
|
@ -28,7 +28,7 @@ Bindings::Bindings()
|
||||
{}
|
||||
|
||||
inline void
|
||||
Bindings::transfer(Bindings *bindings)
|
||||
Bindings::transferFrom(Bindings *bindings)
|
||||
{
|
||||
JS_ASSERT(!lastBinding);
|
||||
JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary());
|
||||
|
@ -3713,10 +3713,13 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
|
||||
BindingVector names(cx);
|
||||
if (!GetOrderedBindings(cx, fun->script()->bindings, &names))
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < fun->nargs; i++) {
|
||||
PropertyName *name = names[i].maybeName;
|
||||
result->setDenseArrayElement(i, name ? StringValue(name) : UndefinedValue());
|
||||
Value v;
|
||||
if (names[i].name->length() == 0)
|
||||
v = UndefinedValue();
|
||||
else
|
||||
v = StringValue(names[i].name);
|
||||
result->setDenseArrayElement(i, v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -766,6 +766,24 @@ NameToId(PropertyName *name)
|
||||
|
||||
typedef HeapPtr<JSAtom> HeapPtrAtom;
|
||||
|
||||
class AutoNameVector : public AutoVectorRooter<PropertyName *>
|
||||
{
|
||||
typedef AutoVectorRooter<PropertyName *> BaseType;
|
||||
public:
|
||||
explicit AutoNameVector(JSContext *cx
|
||||
JS_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: AutoVectorRooter<PropertyName *>(cx, NAMEVECTOR)
|
||||
{
|
||||
JS_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
|
||||
HandlePropertyName operator[](size_t i) const {
|
||||
return HandlePropertyName::fromMarkedLocation(&BaseType::operator[](i));
|
||||
}
|
||||
|
||||
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
/* Avoid requiring vm/String-inl.h just to call getChars. */
|
||||
|
Loading…
Reference in New Issue
Block a user