From 78a959193d70863ef254b6242b6b522408411058 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Sat, 13 Apr 2013 18:59:42 -0600 Subject: [PATCH] Bug 845596 - Keep track of free variables during syntax parsing, r=jorendorff. --- js/src/frontend/FullParseHandler.h | 62 +++++- js/src/frontend/ParseMaps-inl.h | 25 ++- js/src/frontend/ParseMaps.cpp | 65 ++---- js/src/frontend/ParseMaps.h | 143 ++++++++----- js/src/frontend/ParseNode.cpp | 4 +- js/src/frontend/ParseNode.h | 16 +- js/src/frontend/Parser.cpp | 288 ++++++++++++--------------- js/src/frontend/Parser.h | 15 +- js/src/frontend/SyntaxParseHandler.h | 31 ++- 9 files changed, 359 insertions(+), 290 deletions(-) diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index f292c8b306e..4152bc957c9 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -48,7 +48,7 @@ class FullParseHandler foldConstants(foldConstants) {} - static Definition *null() { return NULL; } + static ParseNode *null() { return NULL; } ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); } void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); } @@ -62,6 +62,16 @@ class FullParseHandler pn->setOp(JSOP_NAME); return pn; } + Definition *newPlaceholder(ParseNode *pn, ParseContext *pc) { + Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, this, pc); + if (!dn) + return NULL; + + dn->setOp(JSOP_NOP); + dn->setDefn(true); + dn->pn_dflags |= PND_PLACEHOLDER; + return dn; + } ParseNode *newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) { ParseNode *pn = NullaryNode::create(kind, this); if (!pn) @@ -168,6 +178,9 @@ class FullParseHandler void setFunctionBox(ParseNode *pn, FunctionBox *funbox) { pn->pn_funbox = funbox; } + void addFunctionArgument(ParseNode *pn, ParseNode *argpn) { + pn->pn_body->append(argpn); + } inline ParseNode *newLexicalScope(ObjectBox *blockbox); bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) { return pn->isKind(kind) && !pn->isInParens(); @@ -192,6 +205,9 @@ class FullParseHandler JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); } + void setPosition(ParseNode *pn, const TokenPos &pos) { + pn->pn_pos = pos; + } TokenPos getPosition(ParseNode *pn) { return pn->pn_pos; } @@ -254,6 +270,50 @@ class FullParseHandler } inline ParseNode *makeAssignment(ParseNode *pn, ParseNode *rhs); + + static Definition *getDefinitionNode(Definition *dn) { + return dn; + } + static Definition::Kind getDefinitionKind(Definition *dn) { + return dn->kind(); + } + void linkUseToDef(ParseNode *pn, Definition *dn) + { + JS_ASSERT(!pn->isUsed()); + JS_ASSERT(!pn->isDefn()); + JS_ASSERT(pn != dn->dn_uses); + JS_ASSERT(dn->isDefn()); + pn->pn_link = dn->dn_uses; + dn->dn_uses = pn; + dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS; + pn->setUsed(true); + pn->pn_lexdef = dn; + } + Definition *resolve(Definition *dn) { + return dn->resolve(); + } + void deoptimizeUsesWithin(Definition *dn, const TokenPos &pos) + { + for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { + JS_ASSERT(pnu->isUsed()); + JS_ASSERT(!pnu->isDefn()); + if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) + pnu->pn_dflags |= PND_DEOPTIMIZED; + } + } + bool dependencyCovered(ParseNode *pn, unsigned blockid, bool functionScope) { + return pn->pn_blockid >= blockid; + } + + static uintptr_t definitionToBits(Definition *dn) { + return uintptr_t(dn); + } + static Definition *definitionFromBits(uintptr_t bits) { + return (Definition *) bits; + } + static Definition *nullDefinition() { + return NULL; + } }; inline bool diff --git a/js/src/frontend/ParseMaps-inl.h b/js/src/frontend/ParseMaps-inl.h index 6d62fc344a8..55fca1a6eb6 100644 --- a/js/src/frontend/ParseMaps-inl.h +++ b/js/src/frontend/ParseMaps-inl.h @@ -48,18 +48,20 @@ ParseMapPool::allocate() return map; } -inline Definition * -AtomDecls::lookupFirst(JSAtom *atom) const +template +inline typename ParseHandler::DefinitionNode +AtomDecls::lookupFirst(JSAtom *atom) const { JS_ASSERT(map); AtomDefnListPtr p = map->lookup(atom); if (!p) - return NULL; - return p.value().front(); + return ParseHandler::nullDefinition(); + return p.value().front(); } +template inline DefinitionList::Range -AtomDecls::lookupMulti(JSAtom *atom) const +AtomDecls::lookupMulti(JSAtom *atom) const { JS_ASSERT(map); if (AtomDefnListPtr p = map->lookup(atom)) @@ -67,15 +69,16 @@ AtomDecls::lookupMulti(JSAtom *atom) const return DefinitionList::Range(); } +template inline bool -AtomDecls::addUnique(JSAtom *atom, Definition *defn) +AtomDecls::addUnique(JSAtom *atom, DefinitionNode defn) { JS_ASSERT(map); AtomDefnListAddPtr p = map->lookupForAdd(atom); if (!p) - return map->add(p, atom, DefinitionList(defn)); + return map->add(p, atom, DefinitionList(ParseHandler::definitionToBits(defn))); JS_ASSERT(!p.value().isMultiple()); - p.value() = DefinitionList(defn); + p.value() = DefinitionList(ParseHandler::definitionToBits(defn)); return true; } @@ -99,15 +102,17 @@ AtomThingMapPtr::releaseMap(JSContext *cx) map_ = NULL; } +template inline bool -AtomDecls::init() +AtomDecls::init() { map = cx->parseMapPool().acquire(); return map; } +template inline -AtomDecls::~AtomDecls() +AtomDecls::~AtomDecls() { if (map) cx->parseMapPool().release(map); diff --git a/js/src/frontend/ParseMaps.cpp b/js/src/frontend/ParseMaps.cpp index ba117d23066..3426d861d90 100644 --- a/js/src/frontend/ParseMaps.cpp +++ b/js/src/frontend/ParseMaps.cpp @@ -5,10 +5,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "ParseMaps-inl.h" #include "jscntxt.h" #include "jscompartment.h" +#include "FullParseHandler.h" +#include "SyntaxParseHandler.h" +#include "ParseMaps-inl.h" #include "vm/String-inl.h" using namespace js; @@ -58,7 +60,7 @@ ParseMapPool::allocateFresh() } DefinitionList::Node * -DefinitionList::allocNode(JSContext *cx, Definition *head, Node *tail) +DefinitionList::allocNode(JSContext *cx, uintptr_t head, Node *tail) { Node *result = cx->tempLifoAlloc().new_(head, tail); if (!result) @@ -66,58 +68,17 @@ DefinitionList::allocNode(JSContext *cx, Definition *head, Node *tail) return result; } -bool -DefinitionList::pushFront(JSContext *cx, Definition *val) -{ - Node *tail; - if (isMultiple()) { - tail = firstNode(); - } else { - tail = allocNode(cx, defn(), NULL); - if (!tail) - return false; - } - - Node *node = allocNode(cx, val, tail); - if (!node) - return false; - *this = DefinitionList(node); - return true; -} - -bool -DefinitionList::pushBack(JSContext *cx, Definition *val) -{ - Node *last; - if (isMultiple()) { - last = firstNode(); - while (last->next) - last = last->next; - } else { - last = allocNode(cx, defn(), NULL); - if (!last) - return false; - } - - Node *node = allocNode(cx, val, NULL); - if (!node) - return false; - last->next = node; - if (!isMultiple()) - *this = DefinitionList(last); - return true; -} - #ifdef DEBUG +template void -AtomDecls::dump() +AtomDecls::dump() { for (AtomDefnListRange r = map->all(); !r.empty(); r.popFront()) { fprintf(stderr, "atom: "); js_DumpAtom(r.front().key()); const DefinitionList &dlist = r.front().value(); for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) { - fprintf(stderr, " defn: %p\n", (void *) dr.front()); + fprintf(stderr, " defn: %p\n", (void *) dr.front()); } } } @@ -133,19 +94,20 @@ DumpAtomDefnMap(const AtomDefnMapPtr &map) for (AtomDefnRange r = map->all(); !r.empty(); r.popFront()) { fprintf(stderr, "atom: "); js_DumpAtom(r.front().key()); - fprintf(stderr, "defn: %p\n", (void *) r.front().value()); + fprintf(stderr, "defn: %p\n", (void *) r.front().value().get()); } } #endif +template bool -AtomDecls::addShadow(JSAtom *atom, Definition *defn) +AtomDecls::addShadow(JSAtom *atom, typename ParseHandler::DefinitionNode defn) { AtomDefnListAddPtr p = map->lookupForAdd(atom); if (!p) - return map->add(p, atom, DefinitionList(defn)); + return map->add(p, atom, DefinitionList(ParseHandler::definitionToBits(defn))); - return p.value().pushFront(cx, defn); + return p.value().pushFront(cx, defn); } void @@ -171,3 +133,6 @@ frontend::InitAtomMap(JSContext *cx, frontend::AtomIndexMap *indices, HeapPtrAto } } } + +template class AtomDecls; +template class AtomDecls; diff --git a/js/src/frontend/ParseMaps.h b/js/src/frontend/ParseMaps.h index 89451c1d3cc..a8026167be1 100644 --- a/js/src/frontend/ParseMaps.h +++ b/js/src/frontend/ParseMaps.h @@ -18,11 +18,11 @@ namespace js { namespace frontend { -struct Definition; +class DefinitionSingle; class DefinitionList; typedef InlineMap AtomIndexMap; -typedef InlineMap AtomDefnMap; +typedef InlineMap AtomDefnMap; typedef InlineMap AtomDefnListMap; /* @@ -135,15 +135,6 @@ struct AtomThingMapPtr Map &operator*() const { return *map_; } }; -struct AtomDefnMapPtr : public AtomThingMapPtr -{ - JS_ALWAYS_INLINE - Definition *lookupDefn(JSAtom *atom) { - AtomDefnMap::Ptr p = map_->lookup(atom); - return p ? p.value() : NULL; - } -}; - typedef AtomThingMapPtr AtomIndexMapPtr; /* @@ -165,9 +156,49 @@ class OwnedAtomThingMapPtr : public AtomThingMapPtrT } }; -typedef OwnedAtomThingMapPtr OwnedAtomDefnMapPtr; typedef OwnedAtomThingMapPtr OwnedAtomIndexMapPtr; +/* + * DefinitionSingle and DefinitionList represent either a single definition + * or a list of them. The representation of definitions varies between + * parse handlers, being either a Definition* (FullParseHandler) or a + * Definition::Kind (SyntaxParseHandler). Methods on the below classes are + * templated to distinguish the kind of value wrapped by the class. + */ + +/* Wrapper for a single definition. */ +class DefinitionSingle +{ + uintptr_t bits; + + public: + + template + static DefinitionSingle new_(typename ParseHandler::DefinitionNode defn) + { + DefinitionSingle res; + res.bits = ParseHandler::definitionToBits(defn); + return res; + } + + template + typename ParseHandler::DefinitionNode get() { + return ParseHandler::definitionFromBits(bits); + } +}; + +struct AtomDefnMapPtr : public AtomThingMapPtr +{ + template + JS_ALWAYS_INLINE + typename ParseHandler::DefinitionNode lookupDefn(JSAtom *atom) { + AtomDefnMap::Ptr p = map_->lookup(atom); + return p ? p.value().get() : ParseHandler::nullDefinition(); + } +}; + +typedef OwnedAtomThingMapPtr OwnedAtomDefnMapPtr; + /* * A nonempty list containing one or more pointers to Definitions. * @@ -191,30 +222,24 @@ class DefinitionList /* A node in a linked list of Definitions. */ struct Node { - Definition *defn; + uintptr_t bits; Node *next; - Node(Definition *defn, Node *next) : defn(defn), next(next) {} + Node(uintptr_t bits, Node *next) : bits(bits), next(next) {} }; union { - Definition *defn; - Node *head; uintptr_t bits; + Node *head; } u; - Definition *defn() const { - JS_ASSERT(!isMultiple()); - return u.defn; - } - Node *firstNode() const { JS_ASSERT(isMultiple()); return (Node *) (u.bits & ~0x1); } static Node * - allocNode(JSContext *cx, Definition *head, Node *tail); + allocNode(JSContext *cx, uintptr_t bits, Node *tail); public: class Range @@ -222,40 +247,41 @@ class DefinitionList friend class DefinitionList; Node *node; - Definition *defn; + uintptr_t bits; explicit Range(const DefinitionList &list) { if (list.isMultiple()) { node = list.firstNode(); - defn = node->defn; + bits = node->bits; } else { node = NULL; - defn = list.defn(); + bits = list.u.bits; } } public: /* An empty Range. */ - Range() : node(NULL), defn(NULL) {} + Range() : node(NULL), bits(0) {} void popFront() { JS_ASSERT(!empty()); if (!node) { - defn = NULL; + bits = 0; return; } node = node->next; - defn = node ? node->defn : NULL; + bits = node ? node->bits : 0; } - Definition *front() { + template + typename ParseHandler::DefinitionNode front() { JS_ASSERT(!empty()); - return defn; + return ParseHandler::definitionFromBits(bits); } bool empty() const { - JS_ASSERT_IF(!defn, !node); - return !defn; + JS_ASSERT_IF(!bits, !node); + return !bits; } }; @@ -263,8 +289,8 @@ class DefinitionList u.bits = 0; } - explicit DefinitionList(Definition *defn) { - u.defn = defn; + explicit DefinitionList(uintptr_t bits) { + u.bits = bits; JS_ASSERT(!isMultiple()); } @@ -276,8 +302,9 @@ class DefinitionList bool isMultiple() const { return (u.bits & 0x1) != 0; } - Definition *front() { - return isMultiple() ? firstNode()->defn : defn(); + template + typename ParseHandler::DefinitionNode front() { + return ParseHandler::definitionFromBits(isMultiple() ? firstNode()->bits : u.bits); } /* @@ -294,7 +321,7 @@ class DefinitionList if (next->next) *this = DefinitionList(next); else - *this = DefinitionList(next->defn); + *this = DefinitionList(next->bits); return true; } @@ -303,17 +330,31 @@ class DefinitionList * * Return true on success. On OOM, report on cx and return false. */ - bool pushFront(JSContext *cx, Definition *val); + template + bool pushFront(JSContext *cx, typename ParseHandler::DefinitionNode defn) { + Node *tail; + if (isMultiple()) { + tail = firstNode(); + } else { + tail = allocNode(cx, u.bits, NULL); + if (!tail) + return false; + } - /* Like pushFront, but add the given val to the end of the list. */ - bool pushBack(JSContext *cx, Definition *val); + Node *node = allocNode(cx, ParseHandler::definitionToBits(defn), tail); + if (!node) + return false; + *this = DefinitionList(node); + return true; + } /* Overwrite the first Definition in the list. */ - void setFront(Definition *val) { + template + void setFront(typename ParseHandler::DefinitionNode defn) { if (isMultiple()) - firstNode()->defn = val; + firstNode()->bits = ParseHandler::definitionToBits(defn); else - *this = DefinitionList(val); + *this = DefinitionList(ParseHandler::definitionToBits(defn)); } Range all() const { return Range(*this); } @@ -341,8 +382,11 @@ class DefinitionList * method addShadow. When we leave the block associated with the let, the method * remove is used to unshadow the declaration immediately preceding it. */ +template class AtomDecls { + typedef typename ParseHandler::DefinitionNode DefinitionNode; + /* AtomDeclsIter needs to get at the DefnListMap directly. */ friend class AtomDeclsIter; @@ -364,21 +408,21 @@ class AtomDecls } /* Return the definition at the head of the chain for |atom|. */ - inline Definition *lookupFirst(JSAtom *atom) const; + inline DefinitionNode lookupFirst(JSAtom *atom) const; /* Perform a lookup that can iterate over the definitions associated with |atom|. */ inline DefinitionList::Range lookupMulti(JSAtom *atom) const; /* Add-or-update a known-unique definition for |atom|. */ - inline bool addUnique(JSAtom *atom, Definition *defn); - bool addShadow(JSAtom *atom, Definition *defn); + inline bool addUnique(JSAtom *atom, DefinitionNode defn); + bool addShadow(JSAtom *atom, DefinitionNode defn); /* Updating the definition for an entry that is known to exist is infallible. */ - void updateFirst(JSAtom *atom, Definition *defn) { + void updateFirst(JSAtom *atom, DefinitionNode defn) { JS_ASSERT(map); AtomDefnListMap::Ptr p = map->lookup(atom); JS_ASSERT(p); - p.value().setFront(defn); + p.value().setFront(defn); } /* Remove the node at the head of the chain for |atom|. */ @@ -420,6 +464,9 @@ typedef AtomDefnListMap::Range AtomDefnListRange; namespace mozilla { +template <> +struct IsPod : TrueType {}; + template <> struct IsPod : TrueType {}; diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index efafe29caf6..27a1c21b372 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -461,7 +461,7 @@ Parser::cloneParseTree(ParseNode *opn) */ if (opn->isDefn()) { opn->setDefn(false); - LinkUseToDef(opn, (Definition *) pn); + handler.linkUseToDef(opn, (Definition *) pn); } } break; @@ -552,7 +552,7 @@ Parser::cloneLeftHandSide(ParseNode *opn) pn->pn_dflags &= ~PND_BOUND; pn->setDefn(false); - LinkUseToDef(pn, (Definition *) opn); + handler.linkUseToDef(pn, (Definition *) opn); } } return pn; diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 08a25c9d8ba..f280390e260 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -12,7 +12,6 @@ #include "jsscript.h" -#include "frontend/ParseMaps.h" #include "frontend/TokenStream.h" namespace js { @@ -1215,7 +1214,7 @@ struct Definition : public ParseNode return pn_cookie.isFree(); } - enum Kind { VAR, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER }; + enum Kind { MISSING = 0, VAR, CONST, LET, ARG, NAMED_LAMBDA, PLACEHOLDER }; bool canHaveInitializer() { return int(kind()) <= int(ARG); } @@ -1298,19 +1297,6 @@ ParseNode::isConstant() } } -inline void -LinkUseToDef(ParseNode *pn, Definition *dn) -{ - JS_ASSERT(!pn->isUsed()); - JS_ASSERT(!pn->isDefn()); - JS_ASSERT(pn != dn->dn_uses); - pn->pn_link = dn->dn_uses; - dn->dn_uses = pn; - dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS; - pn->setUsed(true); - pn->pn_lexdef = dn; -} - class ObjectBox { public: JSObject *object; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 38d83598256..88320dad663 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -125,7 +125,7 @@ ParseContext::define(JSContext *cx, HandlePropertyName name, JS_ASSERT(!decls_.lookupFirst(name)); if (!prevDef) - prevDef = lexdeps.lookupDefn(name); + prevDef = lexdeps.lookupDefn(name); if (prevDef) { ParseNode **pnup = &prevDef->dn_uses; @@ -196,8 +196,7 @@ ParseContext::define(JSContext *cx, HandlePropertyName name, return false; break; - case Definition::PLACEHOLDER: - case Definition::NAMED_LAMBDA: + default: JS_NOT_REACHED("unexpected kind"); break; } @@ -210,17 +209,20 @@ bool ParseContext::define(JSContext *cx, HandlePropertyName name, Node pn, Definition::Kind kind) { - return true; + JS_ASSERT(!decls_.lookupFirst(name)); + + if (lexdeps.lookupDefn(name)) + lexdeps->remove(name); + + return decls_.addUnique(name, kind); } template void -ParseContext::prepareToAddDuplicateArg(Definition *prevDecl) +ParseContext::prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl) { - JS_ASSERT(prevDecl->kind() == Definition::ARG); - JS_ASSERT(decls_.lookupFirst(prevDecl->name()) == prevDecl); - JS_ASSERT(!prevDecl->isClosed()); - decls_.remove(prevDecl->name()); + JS_ASSERT(decls_.lookupFirst(name) == prevDecl); + decls_.remove(name); } template @@ -258,7 +260,7 @@ template void ParseContext::popLetDecl(JSAtom *atom) { - JS_ASSERT(decls_.lookupFirst(atom)->isLet()); + JS_ASSERT(ParseHandler::getDefinitionKind(decls_.lookupFirst(atom)) == Definition::LET); decls_.remove(atom); } @@ -281,9 +283,7 @@ AppendPackedBindings(const ParseContext *pc, const DeclVector &vec case Definition::ARG: kind = ARGUMENT; break; - case Definition::LET: - case Definition::NAMED_LAMBDA: - case Definition::PLACEHOLDER: + default: JS_NOT_REACHED("unexpected dn->kind"); } @@ -821,11 +821,6 @@ Parser::checkStrictBinding(HandlePropertyName name, Node pn) return true; } -template <> -bool -Parser::defineArg(ParseNode *funcpn, HandlePropertyName name, - bool disallowDuplicateArgs, Definition **duplicatedArg); - template <> ParseNode * Parser::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, @@ -875,27 +870,21 @@ template <> bool Parser::checkFunctionArguments() { - /* Time to implement the odd semantics of 'arguments'. */ - HandlePropertyName arguments = context->names().arguments; - /* * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope * operation which means it aliases any bindings with the same name. - * Due to the implicit declaration mechanism (below), 'arguments' will not - * have decls and, even if it did, they will not be noted as closed in the - * emitter. Thus, in the corner case of function-statement-overridding- - * arguments, flag the whole scope as dynamic. */ if (FuncStmtSet *set = pc->funcStmts) { for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { PropertyName *name = r.front()->asPropertyName(); - if (name == arguments) - pc->sc->setBindingsAccessedDynamically(); - else if (Definition *dn = pc->decls().lookupFirst(name)) + if (Definition *dn = pc->decls().lookupFirst(name)) dn->pn_dflags |= PND_CLOSED; } } + /* Time to implement the odd semantics of 'arguments'. */ + HandlePropertyName arguments = context->names().arguments; + /* * As explained by the ContextFlags::funArgumentsHasLocalBinding comment, * create a declaration for 'arguments' if there are any unbound uses in @@ -903,7 +892,7 @@ Parser::checkFunctionArguments() */ for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { - Definition *dn = r.front().value(); + Definition *dn = r.front().value().get(); pc->lexdeps->remove(arguments); dn->pn_dflags |= PND_IMPLICITARGUMENTS; if (!pc->define(context, arguments, dn, Definition::VAR)) @@ -979,7 +968,7 @@ Parser::checkFunctionArguments() for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) { DefinitionList &dlist = r.front().value(); for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) { - Definition *dn = dr.front(); + Definition *dn = dr.front(); if (dn->kind() == Definition::ARG && dn->isAssigned()) funbox->setDefinitelyNeedsArgsObj(); } @@ -1045,23 +1034,6 @@ Parser::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ return pn; } -// Create a placeholder Definition node for |atom|. -// Nb: unlike most functions that are passed a Parser, this one gets a -// SharedContext passed in separately, because in this case |pc| may not equal -// |parser->pc|. -static Definition * -MakePlaceholder(ParseNode *pn, FullParseHandler *handler, ParseContext *pc) -{ - Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, handler, pc); - if (!dn) - return NULL; - - dn->setOp(JSOP_NOP); - dn->setDefn(true); - dn->pn_dflags |= PND_PLACEHOLDER; - return dn; -} - static void ForgetUse(ParseNode *pn) { @@ -1253,23 +1225,6 @@ MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts) return true; } -static bool -DeoptimizeUsesWithin(Definition *dn, const TokenPos &pos) -{ - unsigned ndeoptimized = 0; - - for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { - JS_ASSERT(pnu->isUsed()); - JS_ASSERT(!pnu->isDefn()); - if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end) { - pnu->pn_dflags |= PND_DEOPTIMIZED; - ++ndeoptimized; - } - } - - return ndeoptimized != 0; -} - /* * Beware: this function is called for functions nested in other functions or * global scripts but not for functions compiled through the Function @@ -1294,7 +1249,7 @@ Parser::leaveFunction(ParseNode *fn, HandlePropertyName funNam if (pc->lexdeps->count()) { for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { JSAtom *atom = r.front().key(); - Definition *dn = r.front().value(); + Definition *dn = r.front().value().get(); JS_ASSERT(dn->isPlaceholder()); if (atom == funName && kind == Expression) { @@ -1337,12 +1292,12 @@ Parser::leaveFunction(ParseNode *fn, HandlePropertyName funNam * having an extensible scope) or any enclosing 'with'. */ if (funbox->hasExtensibleScope() || outerpc->parsingWith) - DeoptimizeUsesWithin(dn, fn->pn_pos); + handler.deoptimizeUsesWithin(dn, fn->pn_pos); if (!outer_dn) { AtomDefnAddPtr p = outerpc->lexdeps->lookupForAdd(atom); if (p) { - outer_dn = p.value(); + outer_dn = p.value().get(); } else { /* * Create a new placeholder for our outer lexdep. We could @@ -1365,8 +1320,11 @@ Parser::leaveFunction(ParseNode *fn, HandlePropertyName funNam * inherited lexdeps into uses of a new outer definition * allows us to handle both these cases in a natural way. */ - outer_dn = MakePlaceholder(dn, &handler, outerpc); - if (!outer_dn || !outerpc->lexdeps->add(p, atom, outer_dn)) + outer_dn = handler.newPlaceholder(dn, outerpc); + if (!outer_dn) + return false; + DefinitionSingle def = DefinitionSingle::new_(outer_dn); + if (!outerpc->lexdeps->add(p, atom, def)) return false; } } @@ -1438,26 +1396,28 @@ Parser::leaveFunction(Node fn, HandlePropertyName funName, * argument with the same name. The caller may use this to report an error when * one of the abovementioned features occurs after a duplicate. */ -template <> +template bool -Parser::defineArg(ParseNode *funcpn, HandlePropertyName name, - bool disallowDuplicateArgs, Definition **duplicatedArg) +Parser::defineArg(Node funcpn, HandlePropertyName name, + bool disallowDuplicateArgs, Node *duplicatedArg) { SharedContext *sc = pc->sc; /* Handle duplicate argument names. */ - if (Definition *prevDecl = pc->decls().lookupFirst(name)) { + if (DefinitionNode prevDecl = pc->decls().lookupFirst(name)) { + Node pn = handler.getDefinitionNode(prevDecl); + /* * Strict-mode disallows duplicate args. We may not know whether we are * in strict mode or not (since the function body hasn't been parsed). - * In such cases, reportStrictModeError will queue up the potential - * error and return 'true'. + * In such cases, report will queue up the potential error and return + * 'true'. */ if (sc->needStrictChecks()) { JSAutoByteString bytes; if (!js_AtomToPrintableString(context, name, &bytes)) return false; - if (!report(ParseStrictError, pc->sc->strict, prevDecl, + if (!report(ParseStrictError, pc->sc->strict, pn, JSMSG_DUPLICATE_FORMAL, bytes.ptr())) { return false; @@ -1465,36 +1425,29 @@ Parser::defineArg(ParseNode *funcpn, HandlePropertyName name, } if (disallowDuplicateArgs) { - report(ParseError, false, prevDecl, JSMSG_BAD_DUP_ARGS); + report(ParseError, false, pn, JSMSG_BAD_DUP_ARGS); return false; } if (duplicatedArg) - *duplicatedArg = prevDecl; + *duplicatedArg = pn; /* ParseContext::define assumes and asserts prevDecl is not in decls. */ - pc->prepareToAddDuplicateArg(prevDecl); + JS_ASSERT(handler.getDefinitionKind(prevDecl) == Definition::ARG); + pc->prepareToAddDuplicateArg(name, prevDecl); } - ParseNode *argpn = handler.newName(name, pc); + Node argpn = handler.newName(name, pc); if (!argpn) return false; if (!checkStrictBinding(name, argpn)) return false; - funcpn->pn_body->append(argpn); + handler.addFunctionArgument(funcpn, argpn); return pc->define(context, name, argpn, Definition::ARG); } -template <> -bool -Parser::defineArg(Node funcpn, HandlePropertyName name, - bool disallowDuplicateArgs, DefinitionNode *duplicatedArg) -{ - return true; -} - #if JS_HAS_DESTRUCTURING template /* static */ bool @@ -1547,7 +1500,7 @@ Parser::functionArguments(FunctionSyntaxKind kind, Node *listp, No if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) { bool hasDefaults = false; - DefinitionNode duplicatedArg = null(); + Node duplicatedArg = null(); bool destructuringArg = false; #if JS_HAS_DESTRUCTURING Node list = null(); @@ -1737,7 +1690,7 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, * pre-created definition node for this function that primaryExpr * put in pc->lexdeps on first forward reference, and recycle pn. */ - if (Definition *fn = pc->lexdeps.lookupDefn(funName)) { + if (Definition *fn = pc->lexdeps.lookupDefn(funName)) { JS_ASSERT(fn->isDefn()); fn->setKind(PNK_FUNCTION); fn->setArity(PN_CODE); @@ -1790,6 +1743,15 @@ Parser::checkFunctionDefinition(HandlePropertyName funName, } if (!pc->funcStmts->put(funName)) return false; + + /* + * Due to the implicit declaration mechanism, 'arguments' will not + * have decls and, even if it did, they will not be noted as closed + * in the emitter. Thus, in the corner case of function statements + * overridding arguments, flag the whole scope as dynamic. + */ + if (funName == context->names().arguments) + pc->sc->setBindingsAccessedDynamically(); } /* No further binding (in BindNameToSlot) is needed for functions. */ @@ -1807,6 +1769,36 @@ bool Parser::checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind) { + /* Function statements add a binding to the enclosing scope. */ + bool bodyLevel = pc->atBodyLevel(); + + if (kind == Statement) { + /* + * Handle redeclaration and optimize cases where we can statically bind the + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). + */ + if (DefinitionNode dn = pc->decls().lookupFirst(funName)) { + if (dn == Definition::CONST) { + JSAutoByteString name; + if (!js_AtomToPrintableString(context, funName, &name) || + !report(ParseError, false, null(), JSMSG_REDECLARED_VAR, + Definition::kindString(dn), name.ptr())) + { + return false; + } + } + } else if (bodyLevel) { + if (pc->lexdeps.lookupDefn(funName)) + pc->lexdeps->remove(funName); + + if (!pc->define(context, funName, *pn, Definition::VAR)) + return false; + } + + if (!bodyLevel && funName == context->names().arguments) + pc->sc->setBindingsAccessedDynamically(); + } + return true; } @@ -2474,17 +2466,17 @@ OuterLet(ParseContext *pc, StmtInfoPC *stmt, HandleAtom atom) return false; } -template <> +template /* static */ bool -Parser::bindVarOrConst(JSContext *cx, BindData *data, - HandlePropertyName name, Parser *parser) +Parser::bindVarOrConst(JSContext *cx, BindData *data, + HandlePropertyName name, Parser *parser) { - ParseContext *pc = parser->pc; - ParseNode *pn = data->pn; + ParseContext *pc = parser->pc; + Node pn = data->pn; bool isConstDecl = data->op == JSOP_DEFCONST; /* Default best op for pn is JSOP_NAME; we'll try to improve below. */ - pn->setOp(JSOP_NAME); + parser->handler.setOp(pn, JSOP_NAME); if (!parser->checkStrictBinding(name, pn)) return false; @@ -2492,7 +2484,7 @@ Parser::bindVarOrConst(JSContext *cx, BindDatatype == STMT_WITH) { - pn->pn_dflags |= PND_DEOPTIMIZED; + parser->handler.setFlag(pn, PND_DEOPTIMIZED); if (pc->sc->isFunctionBox()) { FunctionBox *funbox = pc->sc->asFunctionBox(); funbox->setMightAliasLocals(); @@ -2523,8 +2515,8 @@ Parser::bindVarOrConst(JSContext *cx, BindDatakind(); + DefinitionNode dn = defs.front(); + Definition::Kind dn_kind = parser->handler.getDefinitionKind(dn); if (dn_kind == Definition::ARG) { JSAutoByteString bytes; if (!js_AtomToPrintableString(cx, name, &bytes)) @@ -2557,16 +2549,7 @@ Parser::bindVarOrConst(JSContext *cx, BindData -/* static */ bool -Parser::bindVarOrConst(JSContext *cx, BindData *data, - HandlePropertyName name, - Parser *parser) -{ + parser->handler.linkUseToDef(pn, dn); return true; } @@ -2589,21 +2572,20 @@ Parser::makeSetCall(ParseNode *pn, unsigned msg) return true; } -template <> +template bool -Parser::noteNameUse(ParseNode *pn) +Parser::noteNameUse(HandlePropertyName name, Node pn) { - RootedPropertyName name(context, pn->pn_atom->asPropertyName()); StmtInfoPC *stmt = LexicalLookup(pc, name, NULL, (StmtInfoPC *)NULL); DefinitionList::Range defs = pc->decls().lookupMulti(name); - Definition *dn; + DefinitionNode dn; if (!defs.empty()) { - dn = defs.front(); + dn = defs.front(); } else { if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) { - dn = p.value(); + dn = p.value().get(); } else { /* * No definition before this use in any lexical scope. @@ -2613,28 +2595,23 @@ Parser::noteNameUse(ParseNode *pn) * - Be left as a free variable definition if we never * see the real definition. */ - dn = MakePlaceholder(pn, &handler, pc); - if (!dn || !pc->lexdeps->add(p, name, dn)) + dn = handler.newPlaceholder(pn, pc); + if (!dn) + return false; + DefinitionSingle def = DefinitionSingle::new_(dn); + if (!pc->lexdeps->add(p, name, def)) return false; } } - JS_ASSERT(dn->isDefn()); - LinkUseToDef(pn, dn); + handler.linkUseToDef(pn, dn); if (stmt && stmt->type == STMT_WITH) - pn->pn_dflags |= PND_DEOPTIMIZED; + handler.setFlag(pn, PND_DEOPTIMIZED); return true; } -template <> -bool -Parser::noteNameUse(Node pn) -{ - return true; -} - #if JS_HAS_DESTRUCTURING template <> @@ -2816,7 +2793,8 @@ Parser::checkDestructuring(BindData *data, * officially linked to its def or registered in lexdeps. Do * that now. */ - if (pair->pn_right == pair->pn_left && !noteNameUse(pn)) + RootedPropertyName name(context, pn->pn_atom->asPropertyName()); + if (pair->pn_right == pair->pn_left && !noteNameUse(name, pn)) return false; ok = bindDestructuringLHS(pn); } @@ -3143,9 +3121,9 @@ PushBlocklikeStatement(StmtInfoPC *stmt, StmtType type, ParseContextblockid); } -template <> -ParseNode * -Parser::newBindingNode(PropertyName *name, VarContext varContext) +template +typename ParseHandler::Node +Parser::newBindingNode(PropertyName *name, bool functionScope, VarContext varContext) { /* * If this name is being injected into an existing block/function, see if @@ -3156,13 +3134,15 @@ Parser::newBindingNode(PropertyName *name, VarContext varConte */ if (varContext == HoistVars) { if (AtomDefnPtr p = pc->lexdeps->lookup(name)) { - ParseNode *lexdep = p.value(); - JS_ASSERT(lexdep->isPlaceholder()); - if (lexdep->pn_blockid >= pc->blockid()) { - lexdep->pn_blockid = pc->blockid(); + DefinitionNode lexdep = p.value().get(); + JS_ASSERT(handler.getDefinitionKind(lexdep) == Definition::PLACEHOLDER); + + Node pn = handler.getDefinitionNode(lexdep); + if (handler.dependencyCovered(pn, pc->blockid(), functionScope)) { + handler.setBlockId(pn, pc->blockid()); pc->lexdeps->remove(p); - lexdep->pn_pos = tokenStream.currentToken().pos; - return lexdep; + handler.setPosition(pn, tokenStream.currentToken().pos); + return pn; } } } @@ -3172,13 +3152,6 @@ Parser::newBindingNode(PropertyName *name, VarContext varConte return handler.newName(name, pc); } -template <> -SyntaxParseHandler::Node -Parser::newBindingNode(PropertyName *name, VarContext varContext) -{ - return SyntaxParseHandler::NodeGeneric; -} - template typename ParseHandler::Node Parser::switchStatement() @@ -3904,7 +3877,7 @@ Parser::tryStatement() case TOK_NAME: { RootedPropertyName label(context, tokenStream.currentToken().name()); - catchName = newBindingNode(label); + catchName = newBindingNode(label, false); if (!catchName) return null(); data.pn = catchName; @@ -4020,9 +3993,10 @@ Parser::withStatement() * to safely optimize binding globals (see bug 561923). */ for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) { - Definition *defn = r.front().value(); - Definition *lexdep = defn->resolve(); - DeoptimizeUsesWithin(lexdep, TokenPos::make(begin, tokenStream.currentToken().pos.begin)); + DefinitionNode defn = r.front().value().get(); + DefinitionNode lexdep = handler.resolve(defn); + handler.deoptimizeUsesWithin(lexdep, + TokenPos::make(begin, tokenStream.currentToken().pos.begin)); } Node pn = handler.newBinary(PNK_WITH, objectExpr, innerBlock); @@ -4594,7 +4568,7 @@ Parser::variables(ParseNodeKind kind, bool *psimple, } RootedPropertyName name(context, tokenStream.currentToken().name()); - pn2 = newBindingNode(name, varContext); + pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext); if (!pn2) return null(); if (data.op == JSOP_DEFCONST) @@ -5445,7 +5419,7 @@ CompExprTransplanter::transplant(ParseNode *pn) * generator) a use of a new placeholder in the generator's * lexdeps. */ - Definition *dn2 = MakePlaceholder(pn, &parser->handler, parser->pc); + Definition *dn2 = parser->handler.newPlaceholder(pn, parser->pc); if (!dn2) return false; dn2->pn_pos = root->pn_pos; @@ -5464,7 +5438,8 @@ CompExprTransplanter::transplant(ParseNode *pn) dn2->dn_uses = dn->dn_uses; dn->dn_uses = *pnup; *pnup = NULL; - if (!pc->lexdeps->put(atom, dn2)) + DefinitionSingle def = DefinitionSingle::new_(dn2); + if (!pc->lexdeps->put(atom, def)) return false; if (dn->isClosed()) dn2->pn_dflags |= PND_CLOSED; @@ -5475,7 +5450,8 @@ CompExprTransplanter::transplant(ParseNode *pn) * from the parent's lexdeps into the generator's lexdeps. */ outerpc->lexdeps->remove(atom); - if (!pc->lexdeps->put(atom, dn)) + DefinitionSingle def = DefinitionSingle::new_(dn); + if (!pc->lexdeps->put(atom, def)) return false; } else if (dn->isImplicitArguments()) { /* @@ -5629,7 +5605,7 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bo * and it tries to bind all names to slots, so we must let it do * the deed. */ - pn3 = newBindingNode(name); + pn3 = newBindingNode(name, false); if (!pn3) return null(); break; @@ -6145,12 +6121,12 @@ Parser::identifierName() { JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME)); - PropertyName *name = tokenStream.currentToken().name(); + RootedPropertyName name(context, tokenStream.currentToken().name()); Node pn = handler.newName(name, pc); if (!pn) return null(); - if (!pc->inDeclDestructuring && !noteNameUse(pn)) + if (!pc->inDeclDestructuring && !noteNameUse(name, pn)) return null(); return pn; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 060f94e3b1f..2dcab9c3242 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -85,6 +85,7 @@ struct ParseContext : public GenericParseContext { typedef StmtInfoPC StmtInfo; typedef typename ParseHandler::Node Node; + typedef typename ParseHandler::DefinitionNode DefinitionNode; uint32_t bodyid; /* block number of program/function body */ uint32_t blockidGen; /* preincremented block number generator */ @@ -103,12 +104,12 @@ struct ParseContext : public GenericParseContext Node blockNode; /* parse node for a block with let declarations (block with its own lexical scope) */ private: - AtomDecls decls_; /* function, const, and var declarations */ + AtomDecls decls_; /* function, const, and var declarations */ DeclVector args_; /* argument definitions */ DeclVector vars_; /* var/const definitions */ public: - const AtomDecls &decls() const { + const AtomDecls &decls() const { return decls_; } @@ -161,7 +162,7 @@ struct ParseContext : public GenericParseContext void popLetDecl(JSAtom *atom); /* See the sad story in defineArg. */ - void prepareToAddDuplicateArg(Definition *prevDecl); + void prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl); /* See the sad story in MakeDefIntoUse. */ void updateDecl(JSAtom *atom, Node newDecl); @@ -494,13 +495,13 @@ struct Parser : private AutoGCRooter, public StrictModeGetter bool checkStrictBinding(HandlePropertyName name, Node pn); bool checkDeleteExpression(Node *pn); bool defineArg(Node funcpn, HandlePropertyName name, - bool disallowDuplicateArgs = false, DefinitionNode *duplicatedArg = NULL); + bool disallowDuplicateArgs = false, Node *duplicatedArg = NULL); Node pushLexicalScope(StmtInfoPC *stmt); Node pushLexicalScope(Handle blockObj, StmtInfoPC *stmt); Node pushLetScope(Handle blockObj, StmtInfoPC *stmt); - bool noteNameUse(Node pn); + bool noteNameUse(HandlePropertyName name, Node pn); Node newRegExp(const jschar *chars, size_t length, RegExpFlag flags); - Node newBindingNode(PropertyName *name, VarContext varContext = HoistVars); + Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars); bool checkDestructuring(BindData *data, Node left, bool toplevel = true); bool bindDestructuringVar(BindData *data, Node pn); bool bindDestructuringLHS(Node pn); @@ -521,7 +522,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter bindVarOrConst(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser); - static DefinitionNode null() { return ParseHandler::null(); } + static Node null() { return ParseHandler::null(); } bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom); bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 085dec56800..dd0ba4faedf 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -27,7 +27,7 @@ class SyntaxParseHandler NodeStringExprStatement, NodeLValue }; - typedef Node DefinitionNode; + typedef Definition::Kind DefinitionNode; SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants) : lastAtom(NULL), @@ -43,6 +43,9 @@ class SyntaxParseHandler lastAtom = name; return NodeName; } + DefinitionNode newPlaceholder(Node pn, ParseContext *pc) { + return Definition::PLACEHOLDER; + } Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) { if (kind == PNK_STRING) { lastAtom = atom; @@ -102,6 +105,7 @@ class SyntaxParseHandler Node newFunctionDefinition() { return NodeGeneric; } void setFunctionBody(Node pn, Node kid) {} void setFunctionBox(Node pn, FunctionBox *funbox) {} + void addFunctionArgument(Node pn, Node argpn) {} Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; } bool isOperationWithoutParens(Node pn, ParseNodeKind kind) { // It is OK to return false here, callers should only use this method @@ -119,6 +123,8 @@ class SyntaxParseHandler void setEndPosition(Node pn, Node oth) {} void setEndPosition(Node pn, uint32_t end) {} + + void setPosition(Node pn, const TokenPos &pos) {} TokenPos getPosition(Node pn) { return tokenStream.currentToken().pos; } @@ -154,6 +160,29 @@ class SyntaxParseHandler bool isEmptySemicolon(Node pn) { return false; } Node makeAssignment(Node pn, Node rhs) { return NodeGeneric; } + + static Node getDefinitionNode(DefinitionNode dn) { return NodeGeneric; } + static Definition::Kind getDefinitionKind(DefinitionNode dn) { return dn; } + void linkUseToDef(Node pn, DefinitionNode dn) {} + DefinitionNode resolve(DefinitionNode dn) { return dn; } + void deoptimizeUsesWithin(DefinitionNode dn, const TokenPos &pos) {} + bool dependencyCovered(Node pn, unsigned blockid, bool functionScope) { + // Only resolve lexical dependencies in cases where a definition covers + // the entire function. Not enough information is kept to compare the + // dependency location with blockid. + return functionScope; + } + + static uintptr_t definitionToBits(DefinitionNode dn) { + // Use a shift, as DefinitionList tags the lower bit of its associated union. + return uintptr_t(dn << 1); + } + static DefinitionNode definitionFromBits(uintptr_t bits) { + return (DefinitionNode) (bits >> 1); + } + static DefinitionNode nullDefinition() { + return Definition::MISSING; + } }; } // namespace frontend