Bug 775323 - build Bindings after, not during, parsing (r=ejpbruel)

--HG--
extra : rebase_source : f78cf919d2e205b688260f462ea109f2816b5ace
This commit is contained in:
Luke Wagner 2012-07-30 11:38:53 -07:00
parent abaee54e50
commit d80d33c1f5
33 changed files with 700 additions and 881 deletions

View File

@ -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) {

View File

@ -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 */

View File

@ -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;

View File

@ -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) {

View File

@ -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))

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,6 @@
// |jit-test| error:SyntaxError
function test() {
arguments;
let (arguments);
}

View File

@ -0,0 +1,13 @@
function runTest() {
if (Math) {
function createTester(options) {
return function() {
return options.blah;
};
}
return createTester({blah:"bar"});
}
}
assertEq(runTest()(), "bar");

View File

@ -0,0 +1,4 @@
with ({b:1}) {
const [ b ] = [];
}
assertEq(b, undefined);

View File

@ -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) {

View File

@ -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(); }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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_; }

View File

@ -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());

View File

@ -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 {

View File

@ -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. */