| author | Shu-yu Guo <shu@rfrn.org> |
| Mon, 14 Dec 2015 13:28:14 -0800 | |
| changeset 276353 | d2bec6ed7b30 |
| parent 276352 | 9c9c92d39b01 |
| child 276354 | fe18b8f63cd6 |
| push id | 69135 |
| push user | shu@rfrn.org |
| push date | 2015-12-14 21:40 +0000 |
| treeherder | mozilla-inbound@d2bec6ed7b30 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | woe |
| bugs | 1071646, 1231758 |
| milestone | 45.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2035,19 +2035,16 @@ ASTSerializer::declaration(ParseNode* pn pn->isKind(PNK_VAR) || pn->isKind(PNK_LET) || pn->isKind(PNK_CONST)); switch (pn->getKind()) { case PNK_FUNCTION: return function(pn, AST_FUNC_DECL, dst); - case PNK_ANNEXB_FUNCTION: - return function(pn->pn_left, AST_FUNC_DECL, dst); - case PNK_VAR: return variableDeclaration(pn, false, dst); default: MOZ_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_CONST)); return variableDeclaration(pn, true, dst); } } @@ -2409,19 +2406,16 @@ bool ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst) { JS_CHECK_RECURSION(cx, return false); switch (pn->getKind()) { case PNK_FUNCTION: case PNK_VAR: return declaration(pn, dst); - case PNK_ANNEXB_FUNCTION: - return declaration(pn->pn_left, dst); - case PNK_LETBLOCK: return letBlock(pn, dst); case PNK_LET: case PNK_CONST: return declaration(pn, dst); case PNK_IMPORT:
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1367,30 +1367,30 @@ BytecodeEmitter::emitVarIncDec(ParseNode return false; if (post && !emit1(JSOP_POP)) // RESULT return false; return true; } bool -BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const +BytecodeEmitter::atBodyLevel() const { // 'eval' and non-syntactic scripts are always under an invisible lexical // scope, but since it is not syntactic, it should still be considered at // body level. if (sc->staticScope()->is<StaticEvalObject>()) { - bool bl = !stmt->enclosing; - MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, stmt->staticScope - ->as<StaticBlockObject>() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !innermostStmt()->enclosing; + MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, innermostStmt()->staticScope + ->as<StaticBlockObject>() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !stmt || sc->isModuleBox(); + return !innermostStmt() || sc->isModuleBox(); } uint32_t BytecodeEmitter::computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut) { Definition* dn = pn->resolve(); MOZ_ASSERT(dn->isDefn()); MOZ_ASSERT(!dn->isPlaceholder()); @@ -1733,17 +1733,17 @@ BytecodeEmitter::bindNameToSlotHelper(Pa */ Definition* dn; if (pn->isUsed()) { MOZ_ASSERT(pn->pn_scopecoord.isFree()); dn = pn->pn_lexdef; MOZ_ASSERT(dn->isDefn()); pn->pn_dflags |= (dn->pn_dflags & PND_CONST); } else if (pn->isDefn()) { - dn = &pn->as<Definition>(); + dn = (Definition*) pn; } else { return true; } if (dn->pn_scopecoord.isFree()) { if (evalCaller) { MOZ_ASSERT(script->treatAsRunOnce() || sc->isFunctionBox()); @@ -2339,20 +2339,16 @@ BytecodeEmitter::checkSideEffects(ParseN *answer = pn->pn_count > 1; return true; case PNK_ARRAYCOMP: MOZ_ASSERT(pn->isArity(PN_LIST)); MOZ_ASSERT(pn->pn_count == 1); return checkSideEffects(pn->pn_head, answer); - case PNK_ANNEXB_FUNCTION: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - return checkSideEffects(pn->pn_left, answer); - case PNK_ARGSBODY: *answer = true; return true; case PNK_FORIN: // by PNK_FOR/PNK_COMPREHENSIONFOR case PNK_FOROF: // by PNK_FOR/PNK_COMPREHENSIONFOR case PNK_FORHEAD: // by PNK_FOR/PNK_COMPREHENSIONFOR case PNK_CLASSMETHOD: // by PNK_CLASS @@ -3093,33 +3089,21 @@ BytecodeEmitter::emitSwitch(ParseNode* p return false; StmtInfoBCE stmtInfo(cx); ptrdiff_t top; if (cases->isKind(PNK_LEXICALSCOPE)) { if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; + stmtInfo.type = StmtType::SWITCH; + stmtInfo.update = top = offset(); + // Advance |cases| to refer to the switch case list. cases = cases->expr(); - - // A switch statement may contain hoisted functions inside its - // cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST - // bodies of the cases to the case list. - if (cases->pn_xflags & PNX_FUNCDEFS) { - for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) { - if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) { - if (!emitHoistedFunctionsInList(caseNode->pn_right)) - return false; - } - } - } - - stmtInfo.type = StmtType::SWITCH; - stmtInfo.update = top = offset(); } else { MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST)); top = offset(); pushStatement(&stmtInfo, StmtType::SWITCH, top); } // Switch bytecodes run from here till end of final case. uint32_t caseCount = cases->pn_count; @@ -4409,34 +4393,22 @@ BytecodeEmitter::emitVariables(ParseNode /* * A destructuring initialiser assignment preceded by var will * never occur to the left of 'in' in a for-in loop. As with 'for * (var x = i in o)...', this will cause the entire 'var [a, b] = * i' to be hoisted out of the loop. */ MOZ_ASSERT(binding->isOp(JSOP_NOP)); MOZ_ASSERT(emitOption != DefineVars); - MOZ_ASSERT_IF(emitOption == AnnexB, binding->pn_left->isKind(PNK_NAME)); - - // To allow the front end to rewrite |var f = x;| as |f = x;| when a - // |function f(){}| precedes the var, detect simple name assignment - // here and initialize the name. - // - // There is a corner case where a function declaration synthesizes - // an Annex B declaration, which in turn gets rewritten later as a - // simple assignment due to hoisted function declaration of the - // same name. For example, - // - // { - // // Synthesizes an Annex B declaration because no 'f' binding - // // yet exists. This later gets rewritten as an assignment when - // // the outer function 'f' gets hoisted. - // function f() {} - // } - // function f() {} + + /* + * To allow the front end to rewrite var f = x; as f = x; when a + * function f(){} precedes the var, detect simple name assignment + * here and initialize the name. + */ if (binding->pn_left->isKind(PNK_NAME)) { if (!emitSingleVariable(pn, binding->pn_left, binding->pn_right, emitOption)) return false; } else { ParseNode* initializer = binding->pn_left; if (!emitDestructuringDecls(pn->getOp(), initializer)) return false; @@ -4481,28 +4453,23 @@ BytecodeEmitter::emitSingleVariable(Pars if (initializer) { MOZ_ASSERT(emitOption != DefineVars); if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME || op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) { MOZ_ASSERT(emitOption != PushInitialValues); - if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) { - if (!emitIndex32(JSOP_BINDGNAME, atomIndex)) - return false; - } else if (emitOption == AnnexB) { - // Annex B vars always go on the nearest variable environment, - // even if scopes on the chain contain same-named bindings. - if (!emit1(JSOP_BINDVAR)) - return false; - } else { - if (!emitIndex32(JSOP_BINDNAME, atomIndex)) - return false; - } + JSOp bindOp; + if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME) + bindOp = JSOP_BINDNAME; + else + bindOp = JSOP_BINDGNAME; + if (!emitIndex32(bindOp, atomIndex)) + return false; } bool oldEmittingForInit = emittingForInit; emittingForInit = false; if (!emitTree(initializer)) return false; emittingForInit = oldEmittingForInit; } else if (op == JSOP_INITLEXICAL || @@ -4516,17 +4483,17 @@ BytecodeEmitter::emitSingleVariable(Pars return false; } else { // The declaration is like `var x;`. Nothing to do. return true; } // If we are not initializing, nothing to pop. If we are initializing // lets, we must emit the pops. - if (emitOption == InitializeVars || emitOption == AnnexB) { + if (emitOption == InitializeVars) { MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr); if (!binding->pn_scopecoord.isFree()) { if (!emitVarOp(binding, op)) return false; } else { if (!emitIndexOp(op, atomIndex)) return false; } @@ -5354,60 +5321,28 @@ BytecodeEmitter::emitLetBlock(ParseNode* return false; if (!leaveNestedScope(&stmtInfo)) return false; return true; } -bool -BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list) -{ - MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS); - - for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) { - if (!sc->strict()) { - while (pn->isKind(PNK_LABEL)) - pn = pn->as<LabeledStatement>().statement(); - } - - if (pn->isKind(PNK_ANNEXB_FUNCTION) || - (pn->isKind(PNK_FUNCTION) && pn->functionIsHoisted())) - { - if (!emitTree(pn)) - return false; - } - } - - return true; -} - // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See // the comment on emitSwitch. MOZ_NEVER_INLINE bool BytecodeEmitter::emitLexicalScope(ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_LEXICALSCOPE)); StmtInfoBCE stmtInfo(cx); if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0)) return false; - ParseNode* body = pn->pn_expr; - - if (body->isKind(PNK_STATEMENTLIST) && body->pn_xflags & PNX_FUNCDEFS) { - // This block contains function statements whose definitions are - // hoisted to the top of the block. Emit these as a separate pass - // before the rest of the block. - if (!emitHoistedFunctionsInList(body)) - return false; - } - - if (!emitTree(body)) + if (!emitTree(pn->pn_expr)) return false; if (!leaveNestedScope(&stmtInfo)) return false; return true; } @@ -6258,51 +6193,29 @@ BytecodeEmitter::emitComprehensionFor(Pa return compFor->pn_left->isKind(PNK_FORIN) ? emitComprehensionForIn(compFor) : emitComprehensionForOf(compFor); } MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) { - ParseNode* assignmentForAnnexB = nullptr; - if (pn->isKind(PNK_ANNEXB_FUNCTION)) { - assignmentForAnnexB = pn->pn_right; - pn = pn->pn_left; - } - FunctionBox* funbox = pn->pn_funbox; RootedFunction fun(cx, funbox->function()); MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript()); /* * Set the |wasEmitted| flag in the funbox once the function has been * emitted. Function definitions that need hoisting to the top of the * function will be seen by emitFunction in two places. */ if (funbox->wasEmitted) { - // Annex B block-scoped functions are hoisted like any other - // block-scoped function to the top of their scope. When their - // definitions are seen for the second time, we need to emit the - // assignment that assigns the function to the outer 'var' binding. - if (assignmentForAnnexB) { - if (assignmentForAnnexB->isKind(PNK_VAR)) { - if (!emitVariables(assignmentForAnnexB, AnnexB)) - return false; - } else { - MOZ_ASSERT(assignmentForAnnexB->isKind(PNK_ASSIGN)); - if (!emitTree(assignmentForAnnexB)) - return false; - if (!emit1(JSOP_POP)) - return false; - } - } - MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript()); MOZ_ASSERT(pn->functionIsHoisted()); + MOZ_ASSERT(sc->isFunctionBox()); return true; } funbox->wasEmitted = true; /* * Mark as singletons any function which will only be executed once, or * which is inner to a lambda we only expect to run once. In the latter @@ -6400,38 +6313,20 @@ BytecodeEmitter::emitFunction(ParseNode* * * Functions are fully parsed prior to invocation of the emitter and calls * to emitTree for function definitions are scheduled before generating * the rest of code. * * For modules, we record the function and instantiate the binding during * ModuleDeclarationInstantiation(), before the script is run. */ - - // Check for functions that were parsed under labeled statements per ES6 - // Annex B.3.2. - bool blockScopedFunction = !atBodyLevel(); - if (!sc->strict() && blockScopedFunction) { - StmtInfoBCE* stmt = innermostStmt(); - while (stmt && stmt->type == StmtType::LABEL) - stmt = stmt->enclosing; - blockScopedFunction = !atBodyLevel(stmt); - } - - if (blockScopedFunction) { - if (!emitIndexOp(JSOP_LAMBDA, index)) - return false; - MOZ_ASSERT(pn->getOp() == JSOP_INITLEXICAL); - if (!emitVarOp(pn, pn->getOp())) - return false; - if (!emit1(JSOP_POP)) - return false; - } else if (sc->isGlobalContext()) { + if (sc->isGlobalContext()) { MOZ_ASSERT(pn->pn_scopecoord.isFree()); MOZ_ASSERT(pn->getOp() == JSOP_NOP); + MOZ_ASSERT(atBodyLevel()); switchToPrologue(); if (!emitIndex32(JSOP_DEFFUN, index)) return false; if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; switchToMain(); } else if (sc->isFunctionBox()) { #ifdef DEBUG @@ -8058,16 +7953,17 @@ bool BytecodeEmitter::emitArgsBody(ParseNode *pn) { RootedFunction fun(cx, sc->asFunctionBox()->function()); ParseNode* pnlast = pn->last(); // Carefully emit everything in the right order: // 1. Defaults and Destructuring for each argument // 2. Functions + ParseNode* pnchild = pnlast->pn_head; bool hasDefaults = sc->asFunctionBox()->hasDefaults(); ParseNode* rest = nullptr; bool restIsDefn = false; if (fun->hasRest() && hasDefaults) { // Defaults with a rest parameter need special handling. The // rest parameter needs to be undefined while defaults are being // processed. To do this, we create the rest argument and let it // sit on the stack while processing defaults. The rest @@ -8118,21 +8014,31 @@ BytecodeEmitter::emitArgsBody(ParseNode if (!emitVarOp(pn2, JSOP_SETARG)) return false; if (!emit1(JSOP_POP)) return false; switchToMain(); } } if (pnlast->pn_xflags & PNX_FUNCDEFS) { - // This function contains top-level inner function definitions. To - // ensure that we emit the bytecode defining them before the rest - // of code in the block we use a separate pass over functions. - if (!emitHoistedFunctionsInList(pnlast)) - return false; + // This block contains top-level function definitions. To ensure + // that we emit the bytecode defining them before the rest of code + // in the block we use a separate pass over functions. During the + // main pass later the emitter will add JSOP_NOP with source notes + // for the function to preserve the original functions position + // when decompiling. + // + // Currently this is used only for functions, as compile-as-we go + // mode for scripts does not allow separate emitter passes. + for (ParseNode* pn2 = pnchild; pn2; pn2 = pn2->pn_next) { + if (pn2->isKind(PNK_FUNCTION) && pn2->functionIsHoisted()) { + if (!emitTree(pn2)) + return false; + } + } } return emitTree(pnlast); } bool BytecodeEmitter::emitDefaultsAndDestructuring(ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_ARGSBODY)); @@ -8341,17 +8247,16 @@ BytecodeEmitter::emitTree(ParseNode* pn, However, a couple trees require special treatment; see the relevant emitter functions for details. */ if (emitLineNote == EMIT_LINENOTE && pn->getKind() != PNK_WHILE && pn->getKind() != PNK_FOR && !updateLineNumberNotes(pn->pn_pos.begin)) return false; switch (pn->getKind()) { case PNK_FUNCTION: - case PNK_ANNEXB_FUNCTION: if (!emitFunction(pn)) return false; break; case PNK_ARGSBODY: if (!emitArgsBody(pn)) return false; break;
--- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -116,22 +116,17 @@ enum VarEmitOption { // used in one case: `for (var $BindingPattern in/of obj)`. If we're at // toplevel, the variable(s) must be defined with JSOP_DEFVAR, but they're // populated inside the loop, via emitAssignment. DefineVars, // Emit code to evaluate initializer expressions and leave those values on // the stack. This is used to implement `for (let/const ...;;)` and // deprecated `let` blocks. - PushInitialValues, - - // Like InitializeVars, but bind using BINDVAR instead of - // BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for - // Annex B block-scoped function semantics. - AnnexB, + PushInitialValues }; struct BytecodeEmitter { SharedContext* const sc; /* context shared between parsing and bytecode generation */ ExclusiveContext* const cx; @@ -249,20 +244,17 @@ struct BytecodeEmitter StmtInfoBCE* innermostStmt() const { return stmtStack.innermost(); } StmtInfoBCE* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } JSObject* innermostStaticScope() const; JSObject* blockScopeOfDef(Definition* dn) const { return parser->blockScopes[dn->pn_blockid]; } - bool atBodyLevel(StmtInfoBCE* stmt) const; - bool atBodyLevel() const { - return atBodyLevel(innermostStmt()); - } + bool atBodyLevel() const; uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut); bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn); bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op); MOZ_ALWAYS_INLINE bool makeAtomIndex(JSAtom* atom, jsatomid* indexp) { AtomIndexAddPtr p = atomIndices->lookupForAdd(atom); if (p) { @@ -467,18 +459,16 @@ struct BytecodeEmitter bool emitInternedObjectOp(uint32_t index, JSOp op); bool emitObjectOp(ObjectBox* objbox, JSOp op); bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op); bool emitRegExp(uint32_t index); MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE bool emitObject(ParseNode* pn); - bool emitHoistedFunctionsInList(ParseNode* pn); - bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type); // To catch accidental misuse, emitUint16Operand/emit3 assert that they are // not used to unconditionally emit JSOP_GETLOCAL. Variable access should // instead be emitted using EmitVarOp. In special cases, when the caller // definitely knows that a given local slot is unaliased, this function may be // used as a non-asserting version of emitUint16Operand. bool emitLocalOp(JSOp op, uint32_t slot);
--- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -95,21 +95,16 @@ ContainsHoistedDeclaration(ExclusiveCont // the function statement is evaluated. Thus any declaration introduced // by a function statement, as observed by this function, isn't a hoisted // declaration. case PNK_FUNCTION: MOZ_ASSERT(node->isArity(PN_CODE)); *result = false; return true; - case PNK_ANNEXB_FUNCTION: - MOZ_ASSERT(node->isArity(PN_BINARY)); - *result = false; - return true; - case PNK_MODULE: *result = false; return true; // Statements with no sub-components at all. case PNK_NOP: // induced by function f() {} function f() {} case PNK_DEBUGGER: MOZ_ASSERT(node->isArity(PN_NULLARY)); @@ -1783,19 +1778,16 @@ Fold(ExclusiveContext* cx, ParseNode** p case PNK_AND: case PNK_OR: return FoldAndOr(cx, pnp, parser, inGenexpLambda); case PNK_FUNCTION: return FoldFunction(cx, pn, parser, inGenexpLambda); - case PNK_ANNEXB_FUNCTION: - return FoldFunction(cx, pn->pn_left, parser, inGenexpLambda); - case PNK_MODULE: return FoldModule(cx, pn, parser); case PNK_SUB: case PNK_STAR: case PNK_LSH: case PNK_RSH: case PNK_URSH:
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -440,49 +440,33 @@ class FullParseHandler ParseNode* newStatementList(unsigned blockid, const TokenPos& pos) { ParseNode* pn = new_<ListNode>(PNK_STATEMENTLIST, pos); if (pn) pn->pn_blockid = blockid; return pn; } template <typename PC> - bool isFunctionStmt(ParseNode* stmt, PC* pc) { - if (!pc->sc->strict()) { - while (stmt->isKind(PNK_LABEL)) - stmt = stmt->as<LabeledStatement>().statement(); - } - - return stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION); - } - - template <typename PC> void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) { MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); - list->append(stmt); - - if (isFunctionStmt(stmt, pc)) { - // PNX_FUNCDEFS notifies the emitter that the block contains - // body-level function definitions that should be processed - // before the rest of nodes. - list->pn_xflags |= PNX_FUNCDEFS; + if (stmt->isKind(PNK_FUNCTION)) { + if (pc->atBodyLevel()) { + // PNX_FUNCDEFS notifies the emitter that the block contains + // body-level function definitions that should be processed + // before the rest of nodes. + list->pn_xflags |= PNX_FUNCDEFS; + } else { + // General deoptimization was done in Parser::functionDef. + MOZ_ASSERT_IF(pc->sc->isFunctionBox(), + pc->sc->asFunctionBox()->hasExtensibleScope()); + } } - } - template <typename PC> - void addCaseStatementToList(ParseNode* list, ParseNode* casepn, PC* pc) { - MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST)); - MOZ_ASSERT(casepn->isKind(PNK_CASE)); - MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST)); - - list->append(casepn); - - if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS) - list->pn_xflags |= PNX_FUNCDEFS; + list->append(stmt); } bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) { MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST)); TokenPos yieldPos(stmtList->pn_pos.begin, stmtList->pn_pos.begin + 1); ParseNode* makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos); if (!makeGen) @@ -667,21 +651,16 @@ class FullParseHandler } void setFunctionBody(ParseNode* pn, ParseNode* kid) { pn->pn_body = kid; } void setFunctionBox(ParseNode* pn, FunctionBox* funbox) { MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); pn->pn_funbox = funbox; } - ParseNode* newFunctionDefinitionForAnnexB(ParseNode* pn, ParseNode* assignment) { - MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); - MOZ_ASSERT(assignment->isKind(PNK_ASSIGN) || assignment->isKind(PNK_VAR)); - return new_<BinaryNode>(PNK_ANNEXB_FUNCTION, JSOP_NOP, pos(), pn, assignment); - } void addFunctionArgument(ParseNode* pn, ParseNode* argpn) { pn->pn_body->append(argpn); } void setDerivedClassConstructor(ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_FUNCTION)); pn->pn_funbox->setDerivedClassConstructor(); } @@ -743,17 +722,17 @@ class FullParseHandler return kind == PNK_FUNCTION || kind == PNK_VAR || kind == PNK_BREAK || kind == PNK_THROW || (kind == PNK_SEMI && !node->pn_kid); } bool isSuperBase(ParseNode* node) { return node->isKind(PNK_SUPERBASE); } - inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init); + inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op); inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op); void setBeginPosition(ParseNode* pn, ParseNode* oth) { setBeginPosition(pn, oth->pn_pos.begin); } void setBeginPosition(ParseNode* pn, uint32_t begin) { pn->pn_pos.begin = begin; MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end); @@ -1006,17 +985,17 @@ FullParseHandler::setLastFunctionArgumen ParseNode* arg = funcpn->pn_body->last(); MOZ_ASSERT(arg->isKind(PNK_NAME)); MOZ_ASSERT(!arg->isUsed()); MOZ_ASSERT(arg->isDefn()); arg->pn_expr = destruct; } inline bool -FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init) +FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op) { if (pn->isUsed()) { pn = makeAssignment(pn, init); if (!pn) return false; } else { pn->pn_expr = init; }
--- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -786,22 +786,16 @@ class NameResolver case PNK_FUNCTION: case PNK_MODULE: MOZ_ASSERT(cur->isArity(PN_CODE)); if (!resolve(cur->pn_body, prefix)) return false; break; - case PNK_ANNEXB_FUNCTION: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - if (!resolve(cur->pn_left, prefix)) - return false; - break; - // Kinds that should be handled by parent node resolution. case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST case PNK_EXPORT_SPEC: // by PNK_EXPORT_SPEC_LIST case PNK_CALLSITEOBJ: // by PNK_TAGGED_TEMPLATE case PNK_CLASSNAMES: // by PNK_CLASS MOZ_CRASH("should have been handled by a parent node");
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -278,18 +278,17 @@ PushNodeChildren(ParseNode* pn, NodeStac case PNK_DOWHILE: case PNK_WHILE: case PNK_SWITCH: case PNK_LETBLOCK: case PNK_CLASSMETHOD: case PNK_NEWTARGET: case PNK_SETTHIS: case PNK_FOR: - case PNK_COMPREHENSIONFOR: - case PNK_ANNEXB_FUNCTION: { + case PNK_COMPREHENSIONFOR: { MOZ_ASSERT(pn->isArity(PN_BINARY)); stack->push(pn->pn_left); stack->push(pn->pn_right); return PushResult::Recyclable; } // Default clauses are PNK_CASE but do not have case expressions. // Named class expressions do not have outer binding nodes.
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -160,17 +160,16 @@ class PackedScopeCoordinate F(EXPORT_FROM) \ F(EXPORT_DEFAULT) \ F(EXPORT_SPEC_LIST) \ F(EXPORT_SPEC) \ F(EXPORT_BATCH_SPEC) \ F(FORIN) \ F(FOROF) \ F(FORHEAD) \ - F(ANNEXB_FUNCTION) \ F(ARGSBODY) \ F(SPREAD) \ F(MUTATEPROTO) \ F(CLASS) \ F(CLASSMETHOD) \ F(CLASSMETHODLIST) \ F(CLASSNAMES) \ F(NEWTARGET) \ @@ -267,19 +266,16 @@ IsDeleteKind(ParseNodeKind kind) * 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_ARGSBODY, ordinarily; * PNK_LEXICALSCOPE for implicit function in genexpr * pn_scopecoord: hops and var index for function * pn_dflags: PND_* definition/use flags (see below) * pn_blockid: block id number - * PNK_ANNEXB_FUNCTION binary pn_left: PNK_FUNCTION - * pn_right: assignment for annex B semantics for - * block-scoped function * PNK_ARGSBODY list list of formal parameters with * PNK_NAME node with non-empty name for * SingleNameBinding without Initializer * PNK_ASSIGN node for SingleNameBinding with * Initializer * PNK_NAME node with empty name for destructuring * pn_expr: PNK_ARRAY, PNK_OBJECT, or PNK_ASSIGN * PNK_ARRAY or PNK_OBJECT for BindingPattern @@ -779,18 +775,17 @@ class ParseNode bool functionIsHoisted() const { MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION); MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr isOp(JSOP_LAMBDA_ARROW) || // arrow function isOp(JSOP_DEFFUN) || // non-body-level function statement isOp(JSOP_NOP) || // body-level function stmt in global code isOp(JSOP_GETLOCAL) || // body-level function stmt in function code - isOp(JSOP_GETARG) || // body-level function redeclaring formal - isOp(JSOP_INITLEXICAL)); // block-level function stmt + isOp(JSOP_GETARG)); // body-level function redeclaring formal return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN); } /* * True if this statement node could be a member of a Directive Prologue: an * expression statement consisting of a single string literal. * * This considers only the node and its children, not its context. After @@ -1607,28 +1602,24 @@ struct Definition : public ParseNode CONSTANT, LET, ARG, NAMED_LAMBDA, PLACEHOLDER, IMPORT }; - static bool test(const ParseNode& pn) { return pn.isDefn(); } - bool canHaveInitializer() { return int(kind()) <= int(ARG); } static const char* kindString(Kind kind); Kind kind() { if (getKind() == PNK_FUNCTION) { if (isOp(JSOP_GETARG)) return ARG; - if (isOp(JSOP_INITLEXICAL)) - return LET; return VAR; } MOZ_ASSERT(getKind() == PNK_NAME); if (isOp(JSOP_CALLEE)) return NAMED_LAMBDA; if (isPlaceholder()) return PLACEHOLDER; if (isOp(JSOP_GETARG))
--- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -98,17 +98,19 @@ ParseContext<FullParseHandler>::checkLoc return false; } return true; } static void MarkUsesAsHoistedLexical(ParseNode* pn) { - Definition* dn = &pn->as<Definition>(); + MOZ_ASSERT(pn->isDefn()); + + Definition* dn = (Definition*)pn; ParseNode** pnup = &dn->dn_uses; ParseNode* pnu; unsigned start = pn->pn_blockid; // In ES6, lexical bindings cannot be accessed until initialized. // Distinguish hoisted uses as a different JSOp for easier compilation. while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->isUsed()); @@ -215,18 +217,16 @@ SharedContext::markSuperScopeNeedsHomeOb template <> bool ParseContext<FullParseHandler>::define(TokenStream& ts, HandlePropertyName name, ParseNode* pn, Definition::Kind kind) { MOZ_ASSERT(!pn->isUsed()); MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder()); - pn->setDefn(true); - Definition* prevDef = nullptr; if (kind == Definition::LET || kind == Definition::CONSTANT) prevDef = decls_.lookupFirst(name); else MOZ_ASSERT(!decls_.lookupFirst(name)); if (!prevDef) prevDef = lexdeps.lookupDefn<FullParseHandler>(name); @@ -235,17 +235,17 @@ ParseContext<FullParseHandler>::define(T ParseNode** pnup = &prevDef->dn_uses; ParseNode* pnu; unsigned start = (kind == Definition::LET || kind == Definition::CONSTANT) ? pn->pn_blockid : bodyid; while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) { MOZ_ASSERT(pnu->pn_blockid >= bodyid); MOZ_ASSERT(pnu->isUsed()); - pnu->pn_lexdef = &pn->as<Definition>(); + 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; @@ -253,21 +253,22 @@ ParseContext<FullParseHandler>::define(T if (!pnu && prevDef->isPlaceholder()) lexdeps->remove(name); } pn->pn_dflags |= prevDef->pn_dflags & PND_CLOSED; } MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONSTANT, !lexdeps->lookup(name)); + pn->setDefn(true); pn->pn_dflags &= ~PND_PLACEHOLDER; if (kind == Definition::CONSTANT) pn->pn_dflags |= PND_CONST; - Definition* dn = &pn->as<Definition>(); + Definition* dn = (Definition*)pn; switch (kind) { case Definition::ARG: MOZ_ASSERT(sc->isFunctionBox()); dn->setOp((CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETARG : JSOP_GETARG); dn->pn_blockid = bodyid; dn->pn_dflags |= PND_BOUND; if (!dn->pn_scopecoord.setSlot(ts, args_.length())) return false; @@ -387,17 +388,17 @@ ParseContext<ParseHandler>::prepareToAdd template <typename ParseHandler> void ParseContext<ParseHandler>::updateDecl(TokenStream& ts, JSAtom* atom, Node pn) { Definition* oldDecl = decls_.lookupFirst(atom); pn->setDefn(true); - Definition* newDecl = &pn->template as<Definition>(); + Definition* newDecl = (Definition*)pn; decls_.updateFirst(atom, newDecl); if (sc->isGlobalContext() || oldDecl->isDeoptimized()) { MOZ_ASSERT(newDecl->isFreeVar()); // Global 'var' bindings have no slots, but are still tracked for // redeclaration checks. for (uint32_t i = 0; i < vars_.length(); i++) { if (vars_[i] == oldDecl) { @@ -1193,16 +1194,28 @@ Parser<FullParseHandler>::standaloneFunc return fn; } template <> bool Parser<FullParseHandler>::checkFunctionArguments() { + /* + * Non-top-level functions use JSOP_DEFFUN which is a dynamic scope + * operation which means it aliases any bindings with the same name. + */ + if (FuncStmtSet* set = pc->funcStmts) { + for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) { + PropertyName* name = r.front()->asPropertyName(); + 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 * the function body. */ @@ -1387,17 +1400,17 @@ Parser<FullParseHandler>::makeDefIntoUse { /* Turn pn into a definition. */ pc->updateDecl(tokenStream, atom, pn); /* Change all uses of dn to be uses of pn. */ for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) { MOZ_ASSERT(pnu->isUsed()); MOZ_ASSERT(!pnu->isDefn()); - pnu->pn_lexdef = &pn->as<Definition>(); + pnu->pn_lexdef = (Definition*) pn; pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS; } pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS; pn->dn_uses = dn; /* * A PNK_FUNCTION node must be a definition, so convert shadowed function * statements into nops. This is valid since all body-level function @@ -1430,28 +1443,28 @@ Parser<FullParseHandler>::makeDefIntoUse */ if (dn->canHaveInitializer()) { if (ParseNode* rhs = dn->expr()) { ParseNode* lhs = handler.makeAssignment(dn, rhs); if (!lhs) return false; pn->dn_uses = lhs; dn->pn_link = nullptr; - dn = &lhs->as<Definition>(); + dn = (Definition*) lhs; } } /* Turn dn into a use of pn. */ MOZ_ASSERT(dn->isKind(PNK_NAME)); MOZ_ASSERT(dn->isArity(PN_NAME)); MOZ_ASSERT(dn->pn_atom == atom); dn->setOp((CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_GETNAME); dn->setDefn(false); dn->setUsed(true); - dn->pn_lexdef = &pn->as<Definition>(); + dn->pn_lexdef = (Definition*) pn; dn->pn_scopecoord.makeFree(); dn->pn_dflags &= ~PND_BOUND; return true; } /* * Helper class for creating bindings. * @@ -1473,32 +1486,28 @@ struct BindData explicit BindData(ExclusiveContext* cx) : kind_(Uninitialized), nameNode_(ParseHandler::null()), letData_(cx) {} void initLexical(VarContext varContext, JSOp op, StaticBlockObject* blockObj, unsigned overflow) { - init(LexicalBinding, op, op == JSOP_DEFCONST, false); + init(LexicalBinding, op, op == JSOP_DEFCONST); letData_.varContext = varContext; letData_.blockObj = blockObj; letData_.overflow = overflow; } void initVar(JSOp op) { - init(VarBinding, op, false, false); - } - - void initAnnexBVar() { - init(VarBinding, JSOP_DEFVAR, false, true); + init(VarBinding, op, false); } void initDestructuring(JSOp op) { - init(DestructuringBinding, op, false, false); + init(DestructuringBinding, op, false); } void setNameNode(typename ParseHandler::Node pn) { MOZ_ASSERT(isInitialized()); nameNode_ = pn; } typename ParseHandler::Node nameNode() { @@ -1511,21 +1520,16 @@ struct BindData return op_; } bool isConst() { MOZ_ASSERT(isInitialized()); return isConst_; } - bool isAnnexB() { - MOZ_ASSERT(isInitialized()); - return isAnnexB_; - } - const LetData& letData() { MOZ_ASSERT(kind_ == LexicalBinding); return letData_; } bool bind(HandlePropertyName name, Parser<ParseHandler>* parser) { MOZ_ASSERT(isInitialized()); MOZ_ASSERT(nameNode_ != ParseHandler::null()); @@ -1552,29 +1556,27 @@ struct BindData BindingKind kind_; // Name node for definition processing and error source coordinates. typename ParseHandler::Node nameNode_; JSOp op_; // Prologue bytecode or nop. bool isConst_; // Whether this is a const binding. - bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3. LetData letData_; bool isInitialized() { return kind_ != Uninitialized; } - void init(BindingKind kind, JSOp op, bool isConst, bool isAnnexB) { + void init(BindingKind kind, JSOp op, bool isConst) { MOZ_ASSERT(!isInitialized()); kind_ = kind; op_ = op; isConst_ = isConst; - isAnnexB_ = isAnnexB; } }; template <typename ParseHandler> JSFunction* Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind, HandleObject proto) { @@ -2230,223 +2232,146 @@ Parser<ParseHandler>::functionArguments( return false; } return true; } template <> bool -Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName, - ParseNode** pn_) -{ - MOZ_ASSERT(pc->atBodyLevel() || !pc->sc->strict()); - - ParseNode*& pn = *pn_; - - /* - * Handle redeclaration and optimize cases where we can statically bind the - * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). - */ - if (Definition* dn = pc->decls().lookupFirst(funName)) { - MOZ_ASSERT(!dn->isUsed()); - MOZ_ASSERT(dn->isDefn()); - - if (dn->kind() == Definition::CONSTANT || dn->kind() == Definition::LET) - return reportRedeclaration(nullptr, Definition::VAR, funName); - - /* - * Body-level function statements are effectively variable - * declarations where the initialization is hoisted to the - * beginning of the block. This means that any other variable - * declaration with the same name is really just an assignment to - * the function's binding (which is mutable), so turn any existing - * declaration into a use. - */ - if (dn->kind() == Definition::ARG) { - // The exception to the above comment is when the function - // has the same name as an argument. Then the argument node - // remains a definition. But change the function node pn so - // that it knows where the argument is located. - pn->setOp(JSOP_GETARG); - pn->setDefn(true); - pn->pn_scopecoord = dn->pn_scopecoord; - pn->pn_blockid = dn->pn_blockid; - pn->pn_dflags |= PND_BOUND; - dn->markAsAssigned(); - } else { - if (!makeDefIntoUse(dn, pn, funName)) - return false; - } - } else { - /* - * If this function was used before it was defined, claim the - * 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<FullParseHandler>(funName)) { - MOZ_ASSERT(fn->isDefn()); - fn->setKind(PNK_FUNCTION); - fn->setArity(PN_CODE); - fn->pn_pos.begin = pn->pn_pos.begin; - fn->pn_pos.end = pn->pn_pos.end; - - fn->pn_body = nullptr; - fn->pn_scopecoord.makeFree(); - - pc->lexdeps->remove(funName); - handler.freeTree(pn); - pn = fn; - } - - if (!pc->define(tokenStream, funName, pn, Definition::VAR)) - return false; - } - - /* No further binding (in BindNameToSlot) is needed for functions. */ - pn->pn_dflags |= PND_BOUND; - - MOZ_ASSERT(pn->functionIsHoisted()); - MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); - - return true; -} - -template <> -bool -Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName, - ParseNode* pn); - -template <> -bool Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName, ParseNode** pn_, FunctionSyntaxKind kind, - bool* pbodyProcessed, - ParseNode** assignmentForAnnexBOut) + bool* pbodyProcessed) { ParseNode*& pn = *pn_; *pbodyProcessed = false; + /* Function statements add a binding to the enclosing scope. */ + bool bodyLevel = pc->atBodyLevel(); + if (kind == Statement) { - MOZ_ASSERT(assignmentForAnnexBOut); - *assignmentForAnnexBOut = nullptr; - - // In sloppy mode, ES6 Annex B.3.2 allows labelled function - // declarations. Otherwise it is a parse error. - bool bodyLevelFunction = pc->atBodyLevel(); - if (!bodyLevelFunction) { - StmtInfoPC* stmt = pc->innermostStmt(); - if (stmt->type == StmtType::LABEL) { - if (pc->sc->strict()) { - report(ParseError, false, null(), JSMSG_FUNCTION_LABEL); - return false; - } - - stmt = pc->innermostNonLabelStmt(); - // A switch statement is always braced, so it's okay to label - // functions in sloppy mode under switch. - if (stmt && stmt->type != StmtType::BLOCK && stmt->type != StmtType::SWITCH) { - report(ParseError, false, null(), JSMSG_SLOPPY_FUNCTION_LABEL); + /* + * Handle redeclaration and optimize cases where we can statically bind the + * function (thereby avoiding JSOP_DEFFUN and dynamic name lookup). + */ + if (Definition* dn = pc->decls().lookupFirst(funName)) { + MOZ_ASSERT(!dn->isUsed()); + MOZ_ASSERT(dn->isDefn()); + + bool throwRedeclarationError = dn->kind() == Definition::CONSTANT || + dn->kind() == Definition::LET; + if (options().extraWarningsOption || throwRedeclarationError) { + JSAutoByteString name; + ParseReportKind reporter = throwRedeclarationError + ? ParseError + : ParseExtraWarning; + if (!AtomToPrintableString(context, funName, &name) || + !report(reporter, false, nullptr, JSMSG_REDECLARED_VAR, + Definition::kindString(dn->kind()), name.ptr())) + { return false; } - - bodyLevelFunction = pc->atBodyLevel(stmt); } - } - - if (bodyLevelFunction) { - if (!bindBodyLevelFunctionName(funName, pn_)) - return false; - } else { - Definition* annexDef = nullptr; - Node synthesizedDeclarationList = null(); - - if (!pc->sc->strict()) { - // Under non-strict mode, try ES6 Annex B.3.3 semantics. If - // making an additional 'var' binding of the same name does - // not throw an early error, do so. This 'var' binding would - // be assigned the function object when its declaration is - // reached, not at the start of the block. - - annexDef = pc->decls().lookupFirst(funName); - if (annexDef) { - if (annexDef->kind() == Definition::CONSTANT || - annexDef->kind() == Definition::LET) - { - // Do not emit Annex B assignment if we would've - // thrown a redeclaration error. - annexDef = nullptr; - } + + /* + * Body-level function statements are effectively variable + * declarations where the initialization is hoisted to the + * beginning of the block. This means that any other variable + * declaration with the same name is really just an assignment to + * the function's binding (which is mutable), so turn any existing + * declaration into a use. + */ + if (bodyLevel) { + if (dn->kind() == Definition::ARG) { + // The exception to the above comment is when the function + // has the same name as an argument. Then the argument node + // remains a definition. But change the function node pn so + // that it knows where the argument is located. + pn->setOp(JSOP_GETARG); + pn->setDefn(true); + pn->pn_scopecoord = dn->pn_scopecoord; + pn->pn_blockid = dn->pn_blockid; + pn->pn_dflags |= PND_BOUND; + dn->markAsAssigned(); } else { - // Synthesize a new 'var' binding if one does not exist. - ParseNode* varNode = newBindingNode(funName, /* functionScope = */ true); - if (!varNode) + if (!makeDefIntoUse(dn, pn, funName)) return false; - - // Treat the 'var' binding as body level. Otherwise the - // lexical binding of the function name below would result - // in a redeclaration. That is, - // { var x; let x; } is an early error. - // var x; { let x; } is not. - varNode->pn_blockid = pc->bodyid; - - BindData<FullParseHandler> data(context); - data.initAnnexBVar(); - data.setNameNode(varNode); - if (!data.bind(funName, this)) - return false; - - annexDef = &varNode->as<Definition>(); - - synthesizedDeclarationList = handler.newDeclarationList(PNK_VAR, JSOP_DEFVAR); - if (!synthesizedDeclarationList) - return false; - handler.addList(synthesizedDeclarationList, annexDef); } } - - if (!bindLexicalFunctionName(funName, pn)) + } else if (bodyLevel) { + /* + * If this function was used before it was defined, claim the + * 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<FullParseHandler>(funName)) { + MOZ_ASSERT(fn->isDefn()); + fn->setKind(PNK_FUNCTION); + fn->setArity(PN_CODE); + fn->pn_pos.begin = pn->pn_pos.begin; + fn->pn_pos.end = pn->pn_pos.end; + + fn->pn_body = nullptr; + fn->pn_scopecoord.makeFree(); + + pc->lexdeps->remove(funName); + handler.freeTree(pn); + pn = fn; + } + + if (!pc->define(tokenStream, funName, pn, Definition::VAR)) return false; - - if (annexDef) { - MOZ_ASSERT(!pc->sc->strict()); - - // Synthesize an assignment assigning the lexical name to the - // 'var' name for Annex B. - - ParseNode* rhs = newName(funName); - if (!rhs) - return false; - if (!noteNameUse(funName, rhs)) + } + + if (bodyLevel) { + MOZ_ASSERT(pn->functionIsHoisted()); + MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree()); + } else { + /* + * As a SpiderMonkey-specific extension, non-body-level function + * statements (e.g., functions in an "if" or "while" block) are + * dynamically bound when control flow reaches the statement. + */ + MOZ_ASSERT(!pc->sc->strict()); + MOZ_ASSERT(pn->pn_scopecoord.isFree()); + if (pc->sc->isFunctionBox()) { + FunctionBox* funbox = pc->sc->asFunctionBox(); + funbox->setMightAliasLocals(); + funbox->setHasExtensibleScope(); + } + pn->setOp(JSOP_DEFFUN); + + /* + * Instead of setting bindingsAccessedDynamically, which would be + * overly conservative, remember the names of all function + * statements and mark any bindings with the same as aliased at the + * end of functionBody. + */ + if (!pc->funcStmts) { + pc->funcStmts = alloc.new_<FuncStmtSet>(alloc); + if (!pc->funcStmts || !pc->funcStmts->init()) { + ReportOutOfMemory(context); return false; - - // If we synthesized a new definition, emit the declaration to - // ensure DEFVAR is correctly emitted in global scripts. - // Otherwise, synthesize a simple assignment and emit that. - if (synthesizedDeclarationList) { - if (!handler.finishInitializerAssignment(annexDef, rhs)) - return false; - *assignmentForAnnexBOut = synthesizedDeclarationList; - } else { - ParseNode* lhs = newName(funName); - if (!lhs) - return false; - lhs->setOp(JSOP_SETNAME); - - // Manually link up the LHS with the non-lexical definition. - handler.linkUseToDef(lhs, annexDef); - - ParseNode* assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP); - if (!assign) - return false; - - *assignmentForAnnexBOut = assign; } } - } + 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. */ + pn->pn_dflags |= PND_BOUND; } else { /* A function expression does not introduce any binding. */ pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA); } // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only // parse recorded the free variables of nested functions and their extents, @@ -2545,54 +2470,48 @@ Parser<ParseHandler>::addFreeVariablesFr PropagateTransitiveParseFlags(lazy, pc->sc); return true; } template <> bool Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed, - Node* assignmentForAnnexBOut) + bool* pbodyProcessed) { *pbodyProcessed = false; /* Function statements add a binding to the enclosing scope. */ bool bodyLevel = pc->atBodyLevel(); if (kind == Statement) { - *assignmentForAnnexBOut = null(); - - if (!bodyLevel) { - // Block-scoped functions cannot yet be parsed lazily. - return abortIfSyntaxParser(); - } - /* * 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::CONSTANT || dn == Definition::LET) { JSAutoByteString name; if (!AtomToPrintableString(context, funName, &name) || !report(ParseError, false, null(), JSMSG_REDECLARED_VAR, Definition::kindString(dn), name.ptr())) { return false; } } - } else { + } else if (bodyLevel) { if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName)) pc->lexdeps->remove(funName); if (!pc->define(tokenStream, funName, *pn, Definition::VAR)) return false; } + + if (!bodyLevel && funName == context->names().arguments) + pc->sc->setBindingsAccessedDynamically(); } if (kind == Arrow) { /* Arrow functions cannot yet be parsed lazily. */ return abortIfSyntaxParser(); } return true; @@ -2663,32 +2582,30 @@ Parser<ParseHandler>::templateLiteral(Yi } while (tt == TOK_TEMPLATE_HEAD); return nodeList; } template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHandling, HandlePropertyName funName, FunctionSyntaxKind kind, - GeneratorKind generatorKind, InvokedPrediction invoked, - Node* assignmentForAnnexBOut) + GeneratorKind generatorKind, InvokedPrediction invoked) { MOZ_ASSERT_IF(kind == Statement, funName); /* Make a TOK_FUNCTION node. */ Node pn = handler.newFunctionDefinition(); if (!pn) return null(); - handler.setBlockId(pn, pc->blockid()); if (invoked) pn = handler.setLikelyIIFE(pn); bool bodyProcessed; - if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed, assignmentForAnnexBOut)) + if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed)) return null(); if (bodyProcessed) return pn; RootedObject proto(context); if (generatorKind == StarGenerator) { // If we are off the main thread, the generator meta-objects have @@ -2865,16 +2782,17 @@ Parser<FullParseHandler>::functionArgsAn // Update the end position of the parse node. pn->pn_pos.end = tokenStream.currentToken().pos.end; } if (!addFreeVariablesFromLazyFunction(fun, pc)) return false; + pn->pn_blockid = outerpc->blockid(); PropagateTransitiveParseFlags(funbox, outerpc->sc); return true; } while (false); blockScopes.resize(oldBlockScopesLength); // Continue doing a full parse for this inner function. ParseContext<FullParseHandler> funpc(this, pc, pn, funbox, newDirectives); @@ -2882,16 +2800,18 @@ Parser<FullParseHandler>::functionArgsAn return false; if (!functionArgsAndBodyGeneric(inHandling, yieldHandling, pn, fun, kind)) return false; if (!leaveFunction(pn, outerpc, kind)) return false; + pn->pn_blockid = outerpc->blockid(); + /* * Fruit of the poisonous tree: if a closure contains a dynamic name access * (eval, with, etc), we consider the parent to do the same. The reason is * that the deoptimizing effects of dynamic name access apply equally to * parents: any local can be read at runtime. */ PropagateTransitiveParseFlags(funbox, outerpc->sc); return true; @@ -3123,34 +3043,16 @@ Parser<ParseHandler>::checkYieldNameVali } template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); - // ES6 Annex B.3.4 says we can parse function declarations unbraced under if or - // else as if it were braced. That is, |if (x) function f() {}| is parsed as - // |if (x) { function f() {} }|. - Maybe<AutoPushStmtInfoPC> synthesizedStmtInfoForAnnexB; - Node synthesizedBlockForAnnexB = null(); - StmtInfoPC *stmt = pc->innermostStmt(); - if (!pc->sc->strict() && stmt) { - if (stmt->type == StmtType::IF || stmt->type == StmtType::ELSE) { - if (!abortIfSyntaxParser()) - return null(); - - synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK); - synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB); - if (!synthesizedBlockForAnnexB) - return null(); - } - } - RootedPropertyName name(context); GeneratorKind generatorKind = NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); if (tt == TOK_MUL) { generatorKind = StarGenerator; @@ -3168,45 +3070,22 @@ Parser<ParseHandler>::functionStmt(Yield name = context->names().starDefaultStar; tokenStream.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT); return null(); } - Node assignmentForAnnexB; - Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind, - PredictUninvoked, &assignmentForAnnexB); - if (!fun) - return null(); - - if (assignmentForAnnexB) { - fun = handler.newFunctionDefinitionForAnnexB(fun, assignmentForAnnexB); - if (!fun) - return null(); - } - - // Note that we may have synthesized a block for Annex B.3.4 without - // having synthesized an assignment for Annex B.3.3, e.g., - // - // let f = 1; - // { - // if (1) function f() {} - // } - if (synthesizedBlockForAnnexB) { - Node body = handler.newStatementList(pc->blockid(), handler.getPosition(fun)); - if (!body) - return null(); - handler.addStatementToList(body, fun, pc); - handler.setLexicalScopeBody(synthesizedBlockForAnnexB, body); - return synthesizedBlockForAnnexB; - } - - return fun; + /* We forbid function statements in strict mode code. */ + if (!pc->atBodyLevel() && pc->sc->needStrictChecks() && + !report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT)) + return null(); + + return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind); } template <typename ParseHandler> typename ParseHandler::Node Parser<ParseHandler>::functionExpr(InvokedPrediction invoked) { MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); @@ -3800,34 +3679,29 @@ Parser<ParseHandler>::bindVar(BindData<P parser->handler.setOp(pn, JSOP_GETNAME); if (!parser->checkStrictBinding(name, pn)) return false; StmtInfoPC* stmt = LexicalLookup(pc, name); if (stmt && stmt->type == StmtType::WITH) { - // Do not deoptimize if we are binding a synthesized 'var' binding for - // Annex B.3.3, which states that the synthesized binding is to go on - // the nearest VariableEnvironment. Deoptimizing here would - // erroneously emit NAME ops when assigning to the Annex B 'var'. - if (!data->isAnnexB()) { - parser->handler.setFlag(pn, PND_DEOPTIMIZED); - if (pc->sc->isFunctionBox()) { - FunctionBox* funbox = pc->sc->asFunctionBox(); - funbox->setMightAliasLocals(); - } - - // Make sure to indicate the need to deoptimize the script's - // arguments object. Mark the function as if it contained a - // debugger statement, which will deoptimize arguments as much as - // possible. - if (name == cx->names().arguments) - pc->sc->setHasDebuggerStatement(); - } + parser->handler.setFlag(pn, PND_DEOPTIMIZED); + if (pc->sc->isFunctionBox()) { + FunctionBox* funbox = pc->sc->asFunctionBox(); + funbox->setMightAliasLocals(); + } + + /* + * Make sure to indicate the need to deoptimize the script's arguments + * object. Mark the function as if it contained a debugger statement, + * which will deoptimize arguments as much as possible. + */ + if (name == cx->names().arguments) + pc->sc->setHasDebuggerStatement(); // Find the nearest enclosing non-with scope that defined name, if // any, for redeclaration checks below. while (stmt && stmt->type == StmtType::WITH) { if (stmt->enclosingScope) stmt = LexicalLookup(pc, name, stmt->enclosingScope); else stmt = nullptr; @@ -3952,37 +3826,33 @@ Parser<ParseHandler>::noteNameUse(Handle } } return true; } template <> bool -Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, HandlePropertyName name, - ParseNode* pn) -{ +Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn) +{ + MOZ_ASSERT(pn->isKind(PNK_NAME)); + + RootedPropertyName name(context, pn->pn_atom->asPropertyName()); + data->setNameNode(pn); - return data->bind(name, this); + if (!data->bind(name, this)) + return false; + return true; } template <> bool -Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn) -{ - RootedPropertyName name(context, pn->name()); - return bindUninitialized(data, name, pn); -} - -template <> -bool -Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, HandlePropertyName name, - ParseNode* pn) -{ - if (!bindUninitialized(data, name, pn)) +Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn) +{ + if (!bindUninitialized(data, pn)) return false; /* * Select the appropriate name-setting opcode, respecting eager selection * done by the data->bind function. */ if (data->op() == JSOP_DEFLET || data->op() == JSOP_DEFCONST) pn->setOp(pn->pn_scopecoord.isFree() ? JSOP_INITGLEXICAL : JSOP_INITLEXICAL); @@ -3995,24 +3865,16 @@ Parser<FullParseHandler>::bindInitialize pn->pn_dflags |= PND_CONST; pn->markAsAssigned(); return true; } template <> bool -Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn) -{ - RootedPropertyName name(context, pn->name()); - return bindInitialized(data, name, pn); -} - -template <> -bool Parser<FullParseHandler>::checkDestructuringName(BindData<FullParseHandler>* data, ParseNode* expr) { MOZ_ASSERT(!handler.isUnparenthesizedDestructuringPattern(expr)); // Parentheses are forbidden around destructuring *patterns* (but allowed // around names). Use our nicer error message for parenthesized, nested // patterns. if (handler.isParenthesizedDestructuringPattern(expr)) { @@ -4549,17 +4411,17 @@ Parser<ParseHandler>::variables(YieldHan } } } if (performAssignment) { if (!bindBeforeInitializer && !data.bind(name, this)) return null(); - if (!handler.finishInitializerAssignment(pn2, init)) + if (!handler.finishInitializerAssignment(pn2, init, data.op())) return null(); } } handler.setLexicalDeclarationOp(pn2, data.op()); handler.setEndPosition(pn, pn2); } while (false); @@ -4570,66 +4432,49 @@ Parser<ParseHandler>::variables(YieldHan break; } return pn; } template <> bool -Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat, - const TokenPos& errorPos) +Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos) { /* - * This is a lexical declaration. We must be directly under a block for - * 'let' and 'const' declarations. If we pass this error test, make the - * enclosing StmtInfoPC be our scope. Further let declarations in this - * block will find this scope statement and use the same block object. - * - * Function declarations behave like 'let', except that they are allowed - * per ES6 Annex B.3.2 to be labeled, unlike plain 'let' and 'const' - * declarations. + * This is a lexical declaration. We must be directly under a block per the + * proposed ES4 specs, but not an implicit block created due to + * 'for (let ...)'. If we pass this error test, make the enclosing + * StmtInfoPC be our scope. Further let declarations in this block will + * find this scope statement and use the same block object. * * If we are the first let declaration in this block (i.e., when the * enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then * we also need to set pc->blockNode to be our PNK_LEXICALSCOPE. */ - - // ES6 Annex B.3.2 does not apply in strict mode, and labeled functions in - // strict mode should have been rejected by checkFunctionDefinition. - MOZ_ASSERT_IF(pc->innermostStmt() && - pc->innermostStmt()->type == StmtType::LABEL && - prepareWhat == PrepareFunction, - !pc->sc->strict()); - - StmtInfoPC* stmt = prepareWhat == PrepareFunction - ? pc->innermostNonLabelStmt() - : pc->innermostStmt(); + StmtInfoPC* stmt = pc->innermostStmt(); if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) { - reportWithOffset(ParseError, false, errorPos.begin, - stmt->type == StmtType::LABEL - ? JSMSG_LEXICAL_DECL_LABEL - : JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, - prepareWhat == PrepareConst ? "const" : "lexical"); + reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, + isConst ? "const" : "lexical"); return false; } if (!stmt) { - MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel()); + MOZ_ASSERT(pc->atBodyLevel()); /* * Self-hosted code must be usable against *any* global object, * including ones with other let variables -- variables possibly * placed in conflicting slots. Forbid top-level let declarations to * prevent such conflicts from ever occurring. */ bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt(); if (options().selfHostingMode && isGlobal) { report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, - prepareWhat == PrepareConst ? "'const'" : "'let'"); + isConst ? "'const'" : "'let'"); return false; } return true; } if (stmt->isBlockScope) { // Nothing to do, the top statement already has a block scope. MOZ_ASSERT(pc->innermostScopeStmt() == stmt); @@ -4645,22 +4490,18 @@ Parser<FullParseHandler>::checkAndPrepar return false; /* * Some obvious assertions here, but they may help clarify the * situation. This stmt is not yet a scope, so it must not be a * catch block (catch is a lexical scope by definition). */ MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH); - if (prepareWhat == PrepareFunction) { - stmt->isBlockScope = true; - pc->stmtStack.linkAsInnermostScopeStmt(stmt, *blockObj); - } else { - pc->stmtStack.makeInnermostLexicalScope(*blockObj); - } + + pc->stmtStack.makeInnermostLexicalScope(*blockObj); MOZ_ASSERT(!blockScopes[stmt->blockid]); blockScopes[stmt->blockid].set(blockObj); #ifdef DEBUG ParseNode* tmp = pc->blockNode; MOZ_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE)); #endif @@ -4680,64 +4521,43 @@ CurrentLexicalStaticBlock(ParseContext<F return &pc->innermostStaticScope()->as<StaticBlockObject>(); MOZ_ASSERT(pc->atBodyLevel() && (!pc->sc->isGlobalContext() || HasNonSyntacticStaticScopeChain(pc->innermostStaticScope()))); return nullptr; } template <> -bool -Parser<FullParseHandler>::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, - PrepareLexicalKind prepareWhat, - ParseNode* pn, - const TokenPos& pos) +ParseNode* +Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, + const TokenPos& pos) { BindData<FullParseHandler> data(context); - if (!checkAndPrepareLexical(prepareWhat, pos)) - return false; - data.initLexical(HoistVars, prepareWhat == PrepareConst ? JSOP_DEFCONST : JSOP_DEFLET, + if (!checkAndPrepareLexical(isConst, pos)) + return null(); + data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET, CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS); - return bindInitialized(&data, name, pn); -} - -template <> -ParseNode* -Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, - PrepareLexicalKind prepareWhat, - const TokenPos& pos) -{ ParseNode* dn = newBindingNode(name, false); if (!dn) return null(); handler.setPosition(dn, pos); - if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos)) + if (!bindInitialized(&data, dn)) return null(); return dn; } template <> -bool -Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName, - ParseNode* pn) -{ - MOZ_ASSERT(!pc->atBodyLevel()); - pn->pn_blockid = pc->blockid(); - return prepareAndBindInitializedLexicalWithNode(funName, PrepareFunction, pn, pos()); -} - -template <> ParseNode* Parser<FullParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst) { handler.disableSyntaxParser(); - if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos())) + if (!checkAndPrepareLexical(isConst, pos())) return null(); /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment * is the VariableEnvironment, i.e., body-level lets are in * the same environment record as vars. * @@ -5239,17 +5059,17 @@ Parser<FullParseHandler>::exportDeclarat case TOK_CLASS: kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName); if (!kid) return null(); break; default: tokenStream.ungetToken(); RootedPropertyName name(context, context->names().starDefaultStar); - binding = makeInitializedLexicalBinding(name, PrepareConst, pos()); + binding = makeInitializedLexicalBinding(name, true, pos()); if (!binding) return null(); kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited); if (!kid) return null(); if (!MatchOrInsertSemicolonAfterExpression(tokenStream)) return null(); break; @@ -5986,17 +5806,17 @@ Parser<ParseHandler>::switchStatement(Yi return null(); } warnedAboutStatementsAfterReturn = true; } } else if (handler.isReturnStatement(stmt)) { afterReturn = true; } } - handler.addStatementToList(body, stmt, pc); + handler.addList(body, stmt); } // In ES6, lexical bindings cannot be accessed until initialized. If // there was a 'let' declaration in the case we just parsed, remember // the slot starting at which new lexical bindings will be // assigned. Since lexical bindings from previous cases will not // dominate uses in the current case, any such uses will require a // dead zone check. @@ -6005,17 +5825,17 @@ Parser<ParseHandler>::switchStatement(Yi // declaring lexical bindings within switch cases without introducing // a new block is poor form and should be avoided. if (stmtInfo->isBlockScope) stmtInfo->firstDominatingLexicalInCase = stmtInfo->staticBlock().numVariables(); Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body); if (!casepn) return null(); - handler.addCaseStatementToList(caseList, casepn, pc); + handler.addList(caseList, casepn); } /* * Handle the case where there was a let declaration in any case in * the switch body, but not within an inner block. If it replaced * pc->blockNode with a new block node then we must refresh caseList and * then restore pc->blockNode. */ @@ -6832,28 +6652,28 @@ Parser<FullParseHandler>::classDefinitio JSOp op = JSOpFromPropertyType(propType); if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic)) return null(); } ParseNode* nameNode = null(); ParseNode* methodsOrBlock = classMethods; if (name) { - ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos); + ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos); if (!innerBinding) return null(); MOZ_ASSERT(classBlock); handler.setLexicalScopeBody(classBlock, classMethods); methodsOrBlock = classBlock; classStmt.reset(); ParseNode* outerBinding = null(); if (classContext == ClassStatement) { - outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos); + outerBinding = makeInitializedLexicalBinding(name, false, namePos); if (!outerBinding) return null(); } nameNode = handler.newClassNames(outerBinding, innerBinding, namePos); if (!nameNode) return null(); }
--- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -48,16 +48,17 @@ struct StmtInfoPC : public StmtInfoBase explicit StmtInfoPC(ExclusiveContext* cx) : StmtInfoBase(cx), blockid(BlockIdLimit), innerBlockScopeDepth(0), firstDominatingLexicalInCase(0) {} }; +typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> FuncStmtSet; class SharedContext; typedef Vector<Definition*, 16> DeclVector; struct GenericParseContext { // Enclosing function or global context. GenericParseContext* parent; @@ -230,16 +231,20 @@ struct MOZ_STACK_CLASS ParseContext : pu // Value for parserPC to restore at the end. Use 'parent' instead for // information about the parse chain, this may be nullptr if // parent != nullptr. ParseContext<ParseHandler>* oldpc; public: OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ + FuncStmtSet* funcStmts; /* Set of (non-top-level) function statements + that will alias any top-level bindings with + the same name. */ + // All inner functions in this context. Only filled in when parsing syntax. Rooted<TraceableVector<JSFunction*>> innerFunctions; // In a function context, points to a Directive struct that can be updated // to reflect new directives encountered in the Directive Prologue that // require reparsing the function. In global/module/generator-tail contexts, // we don't need to reparse when encountering a DirectivePrologue so this // pointer may be nullptr. @@ -267,16 +272,17 @@ struct MOZ_STACK_CLASS ParseContext : pu blockNode(ParseHandler::null()), decls_(prs->context, prs->alloc), args_(prs->context), vars_(prs->context), bodyLevelLexicals_(prs->context), parserPC(&prs->pc), oldpc(prs->pc), lexdeps(prs->context), + funcStmts(nullptr), innerFunctions(prs->context, TraceableVector<JSFunction*>(prs->context)), newDirectives(newDirectives), inDeclDestructuring(false) { prs->pc = this; if (sc->isFunctionBox()) parseUsingFunctionBox.emplace(prs->context, sc->asFunctionBox()); } @@ -284,47 +290,42 @@ struct MOZ_STACK_CLASS ParseContext : pu ~ParseContext(); bool init(Parser<ParseHandler>& parser); unsigned blockid() { return stmtStack.innermost() ? stmtStack.innermost()->blockid : bodyid; } StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); } StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); } - StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); } JSObject* innermostStaticScope() const { if (StmtInfoPC* stmt = innermostScopeStmt()) return stmt->staticScope; return sc->staticScope(); } // True if we are at the topmost level of a entire script or function body. // For example, while parsing this code we would encounter f1 and f2 at // body level, but we would not encounter f3 or f4 at body level: // // function f1() { function f2() { } } // if (cond) { function f3() { if (cond) { function f4() { } } } } // - bool atBodyLevel(StmtInfoPC* stmt) { + bool atBodyLevel() { // 'eval' and non-syntactic scripts are always under an invisible // lexical scope, but since it is not syntactic, it should still be // considered at body level. if (sc->staticScope()->is<StaticEvalObject>()) { - bool bl = !stmt->enclosing; - MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK); - MOZ_ASSERT_IF(bl, stmt->staticScope - ->template as<StaticBlockObject>() - .enclosingStaticScope() == sc->staticScope()); + bool bl = !innermostStmt()->enclosing; + MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK); + MOZ_ASSERT_IF(bl, innermostStmt()->staticScope + ->template as<StaticBlockObject>() + .enclosingStaticScope() == sc->staticScope()); return bl; } - return !stmt; - } - - bool atBodyLevel() { - return atBodyLevel(innermostStmt()); + return !innermostStmt(); } bool atGlobalLevel() { return atBodyLevel() && sc->isGlobalContext() && !innermostScopeStmt(); } // True if we are at the topmost level of a module only. bool atModuleLevel() { @@ -722,18 +723,17 @@ class Parser : private JS::AutoGCRooter, /* * Additional JS parsers. */ bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, Node funcpn, bool* hasRest); Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name, FunctionSyntaxKind kind, GeneratorKind generatorKind, - InvokedPrediction invoked = PredictUninvoked, - Node* assignmentForAnnexBOut = nullptr); + InvokedPrediction invoked = PredictUninvoked); bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun, FunctionSyntaxKind kind, GeneratorKind generatorKind, Directives inheritedDirectives, Directives* newDirectives); Node unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op, uint32_t begin); Node condition(InHandling inHandling, YieldHandling yieldHandling); @@ -789,20 +789,18 @@ class Parser : private JS::AutoGCRooter, bool matchInOrOf(bool* isForInp, bool* isForOfp); bool checkFunctionArguments(); bool defineFunctionThis(); Node newThisName(); bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom); - bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn); - bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn); bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind, - bool* pbodyProcessed, Node* assignmentForAnnexBOut); + bool* pbodyProcessed); bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body); bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc); bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach, ParseNodeKind headKind); bool checkForHeadConstInitializers(Node pn1); // Use when the current token is TOK_NAME and is known to be 'let'. @@ -841,44 +839,33 @@ class Parser : private JS::AutoGCRooter, Node propertyName(YieldHandling yieldHandling, Node propList, PropertyType* propType, MutableHandleAtom propAtom); Node computedPropertyName(YieldHandling yieldHandling, Node literal); Node arrayInitializer(YieldHandling yieldHandling); Node newRegExp(); Node objectLiteral(YieldHandling yieldHandling); - enum PrepareLexicalKind { - PrepareLet, - PrepareConst, - PrepareFunction - }; - bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos); - bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name, - PrepareLexicalKind prepareWhat, - ParseNode* pn, const TokenPos& pos); - Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat, - const TokenPos& pos); + bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos); + Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos); Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars); // Top-level entrypoint into destructuring pattern checking/name-analyzing. bool checkDestructuringPattern(BindData<ParseHandler>* data, Node pattern); // Recursive methods for checking/name-analyzing subcomponents of a // destructuring pattern. The array/object methods *must* be passed arrays // or objects. The name method may be passed anything but will report an // error if not passed a name. bool checkDestructuringArray(BindData<ParseHandler>* data, Node arrayPattern); bool checkDestructuringObject(BindData<ParseHandler>* data, Node objectPattern); bool checkDestructuringName(BindData<ParseHandler>* data, Node expr); - bool bindInitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn); bool bindInitialized(BindData<ParseHandler>* data, Node pn); - bool bindUninitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn); bool bindUninitialized(BindData<ParseHandler>* data, Node pn); bool makeSetCall(Node node, unsigned errnum); Node cloneDestructuringDefault(Node opn); Node cloneLeftHandSide(Node opn); Node cloneParseTree(Node opn); Node newNumber(const Token& tok) { return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
--- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -578,22 +578,16 @@ class MOZ_STACK_CLASS StmtInfoStack public: explicit StmtInfoStack(ExclusiveContext* cx) : innermostStmt_(nullptr), innermostScopeStmt_(nullptr) { } StmtInfo* innermost() const { return innermostStmt_; } StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; } - StmtInfo* innermostNonLabel() const { - StmtInfo* stmt = innermost(); - while (stmt && stmt->type == StmtType::LABEL) - stmt = stmt->enclosing; - return stmt; - } void push(StmtInfo* stmt, StmtType type) { stmt->type = type; stmt->isBlockScope = false; stmt->isForLetBlock = false; stmt->label = nullptr; stmt->staticScope = nullptr; stmt->enclosing = innermostStmt_;
--- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -285,17 +285,16 @@ class SyntaxParseHandler bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; } Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } // Statements Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; } void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {} - void addCaseStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {} bool prependInitialYield(Node stmtList, Node gen) { return true; } Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; } Node newSetThis(Node thisName, Node value) { return value; } Node newExprStatement(Node expr, uint32_t end) { return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric; } @@ -329,17 +328,16 @@ class SyntaxParseHandler bool addCatchBlock(Node catchList, Node letBlock, Node catchName, Node catchGuard, Node catchBody) { return true; } bool setLastFunctionArgumentDefault(Node funcpn, Node pn) { return true; } void setLastFunctionArgumentDestructuring(Node funcpn, Node pn) {} Node newFunctionDefinition() { return NodeHoistableDeclaration; } void setFunctionBody(Node pn, Node kid) {} void setFunctionBox(Node pn, FunctionBox* funbox) {} - Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeHoistableDeclaration; } void addFunctionArgument(Node pn, Node argpn) {} Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) { return NodeGeneric; } Node newComprehensionFor(uint32_t begin, Node forHead, Node body) { return NodeGeneric; @@ -351,17 +349,17 @@ class SyntaxParseHandler Node newLexicalScope(ObjectBox* blockbox) { return NodeGeneric; } void setLexicalScopeBody(Node block, Node body) {} Node newLetBlock(Node vars, Node block, const TokenPos& pos) { return NodeGeneric; } - bool finishInitializerAssignment(Node pn, Node init) { return true; } + bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; } void setLexicalDeclarationOp(Node pn, JSOp op) {} void setBeginPosition(Node pn, Node oth) {} void setBeginPosition(Node pn, uint32_t begin) {} void setEndPosition(Node pn, Node oth) {} void setEndPosition(Node pn, uint32_t end) {}
--- a/js/src/jit-test/tests/auto-regress/bug771027.js +++ b/js/src/jit-test/tests/auto-regress/bug771027.js @@ -1,9 +1,9 @@ // |jit-test| error:TypeError // Binary: cache/js-dbg-32-b6aa44d8f11f-linux // Flags: // -Array.prototype.iterator = (function() { { while(0) { function Uint8ClampedArray() { } } } }); +Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() { } } }); var s = new Set(["testing", "testing", 123]); assertEq(s.size(), 2);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/baseline/bug1081850.js @@ -0,0 +1,18 @@ +// |jit-test| ion-eager + +var ARR = []; +try { + function f() { + ARR.push(eval.prototype) + } + f() + function eval()(0) + f() +} catch (e) {} + +if (ARR.length !== 2) + throw new Error("ERROR 1"); +if (typeof(ARR[0]) !== 'undefined') + throw new Error("ERROR 2"); +if (typeof(ARR[1]) !== 'object') + throw new Error("ERROR 3");
--- a/js/src/jit-test/tests/basic/bug667504-syntax.js +++ b/js/src/jit-test/tests/basic/bug667504-syntax.js @@ -1,3 +1,2 @@ -for (var x in x) { +for (var x in x) function x() {} -}
--- a/js/src/jit-test/tests/ion/bug1148973-1.js +++ b/js/src/jit-test/tests/ion/bug1148973-1.js @@ -3,16 +3,12 @@ try { String(b = Proxy.createFunction(function() { return { get: function(r, z) { return x[z] } } }(), function() {})) } catch (e) {}; -var log = ""; -evaluate(` try { function x() {} assertEq(String(b), "function () {}"); -} catch (e) { log += "e"; } -`); -assertEq(log, "e"); +} catch (e) { throw (e); }
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2254,36 +2254,16 @@ BaselineCompiler::emit_JSOP_BINDGNAME() } // Otherwise we have to use the dynamic scope chain. } return emit_JSOP_BINDNAME(); } -typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); -static const VMFunction BindVarInfo = FunctionInfo<BindVarFn>(jit::BindVar); - -bool -BaselineCompiler::emit_JSOP_BINDVAR() -{ - frame.syncStack(0); - masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg()); - - prepareVMCall(); - pushArg(R0.scratchReg()); - - if (!callVM(BindVarInfo)) - return false; - - masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); - frame.push(R0); - return true; -} - bool BaselineCompiler::emit_JSOP_SETPROP() { // Keep lhs in R0, rhs in R1. frame.popRegsAndSync(2); // Call IC. ICSetProp_Fallback::Compiler compiler(cx);
--- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -136,17 +136,16 @@ namespace jit { _(JSOP_GETXPROP) \ _(JSOP_GETALIASEDVAR) \ _(JSOP_SETALIASEDVAR) \ _(JSOP_GETNAME) \ _(JSOP_BINDNAME) \ _(JSOP_DELNAME) \ _(JSOP_GETIMPORT) \ _(JSOP_GETINTRINSIC) \ - _(JSOP_BINDVAR) \ _(JSOP_DEFVAR) \ _(JSOP_DEFCONST) \ _(JSOP_DEFLET) \ _(JSOP_DEFFUN) \ _(JSOP_GETLOCAL) \ _(JSOP_SETLOCAL) \ _(JSOP_GETARG) \ _(JSOP_SETARG) \
--- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -153,17 +153,16 @@ BytecodeAnalysis::init(TempAllocator& al for (size_t i = 0; i < catchFinallyRanges.length(); i++) { if (catchFinallyRanges[i].contains(offset)) infos_[offset].loopEntryInCatchOrFinally = true; } break; case JSOP_GETNAME: case JSOP_BINDNAME: - case JSOP_BINDVAR: case JSOP_SETNAME: case JSOP_STRICTSETNAME: case JSOP_DELNAME: case JSOP_GETALIASEDVAR: case JSOP_SETALIASEDVAR: case JSOP_LAMBDA: case JSOP_LAMBDA_ARROW: case JSOP_DEFFUN:
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -8423,26 +8423,16 @@ CodeGenerator::visitOutOfLineUnboxFloati Label bail; masm.branchTestInt32(Assembler::NotEqual, value, &bail); bailoutFrom(&bail, ins->snapshot()); } masm.int32ValueToFloatingPoint(value, ToFloatRegister(ins->output()), ins->type()); masm.jump(ool->rejoin()); } -typedef JSObject* (*BindVarFn)(JSContext*, HandleObject); -static const VMFunction BindVarInfo = FunctionInfo<BindVarFn>(jit::BindVar); - -void -CodeGenerator::visitCallBindVar(LCallBindVar* lir) -{ - pushArg(ToRegister(lir->scopeChain())); - callVM(BindVarInfo, lir); -} - typedef bool (*GetPropertyFn)(JSContext*, HandleValue, HandlePropertyName, MutableHandleValue); static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty); void CodeGenerator::visitCallGetProperty(LCallGetProperty* lir) { pushArg(ImmGCPtr(lir->mir()->name())); pushArg(ToValue(lir, LCallGetProperty::Value));
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -324,17 +324,16 @@ class CodeGenerator : public CodeGenerat void visitInstanceOfV(LInstanceOfV* ins); void visitCallInstanceOf(LCallInstanceOf* ins); void visitGetDOMProperty(LGetDOMProperty* lir); void visitGetDOMMemberV(LGetDOMMemberV* lir); void visitGetDOMMemberT(LGetDOMMemberT* lir); void visitSetDOMProperty(LSetDOMProperty* lir); void visitCallDOMNative(LCallDOMNative* lir); void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir); - void visitCallBindVar(LCallBindVar* lir); void visitIsCallable(LIsCallable* lir); void visitOutOfLineIsCallable(OutOfLineIsCallable* ool); void visitIsObject(LIsObject* lir); void visitIsObjectAndBranch(LIsObjectAndBranch* lir); void visitHasClass(LHasClass* lir); void visitAsmJSParameter(LAsmJSParameter* lir); void visitAsmJSReturn(LAsmJSReturn* ret); void visitAsmJSVoidReturn(LAsmJSVoidReturn* ret);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1938,19 +1938,16 @@ IonBuilder::inspectOpcode(JSOp op) if (!script()->hasNonSyntacticScope()) { if (JSObject* scope = testGlobalLexicalBinding(info().getName(pc))) return pushConstant(ObjectValue(*scope)); } // Fall through to JSOP_BINDNAME case JSOP_BINDNAME: return jsop_bindname(info().getName(pc)); - case JSOP_BINDVAR: - return jsop_bindvar(); - case JSOP_DUP: current->pushSlot(current->stackDepth() - 1); return true; case JSOP_DUP2: return jsop_dup2(); case JSOP_SWAP: @@ -8416,26 +8413,16 @@ IonBuilder::jsop_bindname(PropertyName* MBindNameCache* ins = MBindNameCache::New(alloc(), scopeChain, name, script(), pc); current->add(ins); current->push(ins); return resumeAfter(ins); } -bool -IonBuilder::jsop_bindvar() -{ - MOZ_ASSERT(analysis().usesScopeChain()); - MCallBindVar* ins = MCallBindVar::New(alloc(), current->scopeChain()); - current->add(ins); - current->push(ins); - return true; -} - static MIRType GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types) { MIRType knownType = types->getKnownMIRType(); // Null and undefined have no payload so they can't be specialized. // Since folding null/undefined while building SSA is not safe (see the // comment in IsPhiObservable), we just add an untyped load instruction
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -684,17 +684,16 @@ class IonBuilder bool getStaticName(JSObject* staticObject, PropertyName* name, bool* psucceeded, MDefinition* lexicalCheck = nullptr); bool setStaticName(JSObject* staticObject, PropertyName* name); bool jsop_getgname(PropertyName* name); bool jsop_getname(PropertyName* name); bool jsop_intrinsic(PropertyName* name); bool jsop_getimport(PropertyName* name); bool jsop_bindname(PropertyName* name); - bool jsop_bindvar(); bool jsop_getelem(); bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType); bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); bool jsop_setelem(); bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* object, MDefinition* index, MDefinition* value, JSValueType unboxedType, bool writeHole); bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType,
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3357,26 +3357,16 @@ LIRGenerator::visitBindNameCache(MBindNa MOZ_ASSERT(ins->type() == MIRType_Object); LBindNameCache* lir = new(alloc()) LBindNameCache(useRegister(ins->scopeChain())); define(lir, ins); assignSafepoint(lir, ins); } void -LIRGenerator::visitCallBindVar(MCallBindVar* ins) -{ - MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object); - MOZ_ASSERT(ins->type() == MIRType_Object); - - LCallBindVar* lir = new(alloc()) LCallBindVar(useRegister(ins->scopeChain())); - define(lir, ins); -} - -void LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) { LGuardObjectIdentity* guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->obj()), useRegister(ins->expected())); assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); add(guard, ins); redefine(ins, ins->obj()); }
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -225,17 +225,16 @@ class LIRGenerator : public LIRGenerator void visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole* ins); void visitClampToUint8(MClampToUint8* ins); void visitLoadFixedSlot(MLoadFixedSlot* ins); void visitStoreFixedSlot(MStoreFixedSlot* ins); void visitGetPropertyCache(MGetPropertyCache* ins); void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins); void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins); void visitBindNameCache(MBindNameCache* ins); - void visitCallBindVar(MCallBindVar* ins); void visitGuardObjectIdentity(MGuardObjectIdentity* ins); void visitGuardClass(MGuardClass* ins); void visitGuardObject(MGuardObject* ins); void visitGuardString(MGuardString* ins); void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins); void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins); void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins); void visitPolyInlineGuard(MPolyInlineGuard* ins);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10753,49 +10753,16 @@ class MBindNameCache JSScript* script() const { return script_; } jsbytecode* pc() const { return pc_; } }; -class MCallBindVar - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MCallBindVar(MDefinition* scopeChain) - : MUnaryInstruction(scopeChain) - { - setResultType(MIRType_Object); - setMovable(); - } - - public: - INSTRUCTION_HEADER(CallBindVar) - - static MCallBindVar* New(TempAllocator& alloc, MDefinition* scopeChain) { - return new(alloc) MCallBindVar(scopeChain); - } - - MDefinition* scopeChain() const { - return getOperand(0); - } - - bool congruentTo(const MDefinition* ins) const override { - if (!ins->isCallBindVar()) - return false; - return congruentIfOperandsEqual(ins); - } - - AliasSet getAliasSet() const override { - return AliasSet::None(); - } -}; - // Guard on an object's shape. class MGuardShape : public MUnaryInstruction, public SingleObjectPolicy::Data { CompilerShape shape_; BailoutKind bailoutKind_;
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -157,17 +157,16 @@ namespace jit { _(FilterTypeSet) \ _(TypeBarrier) \ _(MonitorTypes) \ _(PostWriteBarrier) \ _(GetPropertyCache) \ _(GetPropertyPolymorphic) \ _(SetPropertyPolymorphic) \ _(BindNameCache) \ - _(CallBindVar) \ _(GuardShape) \ _(GuardReceiverPolymorphic) \ _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ _(GuardUnboxedExpando) \ _(LoadUnboxedExpando) \ _(ArrayLength) \
--- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -162,42 +162,38 @@ CheckOverRecursedWithExtra(JSContext* cx #else JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false); #endif gc::MaybeVerifyBarriers(cx); return cx->runtime()->handleInterrupt(cx); } -JSObject* -BindVar(JSContext* cx, HandleObject scopeChain) -{ - JSObject* obj = scopeChain; - while (!obj->isQualifiedVarObj()) - obj = obj->enclosingScope(); - MOZ_ASSERT(obj); - return obj; -} - bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain) { // Given the ScopeChain, extract the VarObj. - RootedObject obj(cx, BindVar(cx, scopeChain)); + RootedObject obj(cx, scopeChain); + while (!obj->isQualifiedVarObj()) + obj = obj->enclosingScope(); + return DefVarOperation(cx, obj, dn, attrs); } bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain) { // Find the extensible lexical scope. Rooted<ClonedBlockObject*> lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain)); // Find the variables object. - RootedObject varObj(cx, BindVar(cx, scopeChain)); + RootedObject varObj(cx, scopeChain); + while (!varObj->isQualifiedVarObj()) + varObj = varObj->enclosingScope(); + return DefLexicalOperation(cx, lexical, varObj, dn, attrs); } bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs) { Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope()); return DefLexicalOperation(cx, globalLexical, cx->global(), dn, attrs); @@ -852,34 +848,36 @@ GeneratorThrowOrClose(JSContext* cx, Bas MOZ_ALWAYS_FALSE(js::GeneratorThrowOrClose(cx, frame, genObj, arg, resumeKind)); return false; } bool InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame) { RootedScript script(cx, frame->script()); - RootedObject scopeChain(cx, frame->scopeChain()); - RootedObject varObj(cx, BindVar(cx, scopeChain)); + RootedObject varObj(cx, frame->scopeChain()); + while (!varObj->isQualifiedVarObj()) + varObj = varObj->enclosingScope(); if (script->isForEval()) { // Strict eval needs its own call object. // // Non-strict eval may introduce 'var' bindings that conflict with // lexical bindings in an enclosing lexical scope. if (script->strict()) { if (!frame->initStrictEvalScopeObjects(cx)) return false; } else { + RootedObject scopeChain(cx, frame->scopeChain()); if (!CheckEvalDeclarationConflicts(cx, script, scopeChain, varObj)) return false; } } else { Rooted<ClonedBlockObject*> lexicalScope(cx, - &NearestEnclosingExtensibleLexicalScope(scopeChain)); + &NearestEnclosingExtensibleLexicalScope(frame->scopeChain())); if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj)) return false; } return true; } bool
--- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -583,17 +583,16 @@ bool InvokeFunction(JSContext* cx, Handl Value* argv, MutableHandleValue rval); bool InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj, uint32_t numActualArgs, uint32_t numFormalArgs, Value* argv, MutableHandleValue rval); bool CheckOverRecursed(JSContext* cx); bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame, uint32_t extra, uint32_t earlyCheck); -JSObject* BindVar(JSContext* cx, HandleObject scopeChain); bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain); bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs); bool MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value); bool InitProp(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value, jsbytecode* pc); template<bool Equal>
--- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5769,32 +5769,16 @@ class LBindNameCache : public LInstructi const LAllocation* scopeChain() { return getOperand(0); } const MBindNameCache* mir() const { return mir_->toBindNameCache(); } }; -class LCallBindVar : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(CallBindVar) - - explicit LCallBindVar(const LAllocation& scopeChain) { - setOperand(0, scopeChain); - } - const LAllocation* scopeChain() { - return getOperand(0); - } - const MCallBindVar* mir() const { - return mir_->toCallBindVar(); - } -}; - // Load a value from an object's dslots or a slots vector. class LLoadSlotV : public LInstructionHelper<BOX_PIECES, 1, 0> { public: LIR_HEADER(LoadSlotV) explicit LLoadSlotV(const LAllocation& in) { setOperand(0, in);
--- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -272,17 +272,16 @@ _(StoreFixedSlotV) \ _(StoreFixedSlotT) \ _(FunctionEnvironment) \ _(GetPropertyCacheV) \ _(GetPropertyCacheT) \ _(GetPropertyPolymorphicV) \ _(GetPropertyPolymorphicT) \ _(BindNameCache) \ - _(CallBindVar) \ _(CallGetProperty) \ _(GetNameCache) \ _(CallGetIntrinsicValue) \ _(CallGetElement) \ _(CallSetElement) \ _(CallInitElementArray) \ _(CallSetProperty) \ _(CallDeleteProperty) \
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -264,19 +264,16 @@ MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0 MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module") MSG_DEF(JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,1,JSEXN_SYNTAXERR,"for-{0} loop head declarations may not have initializers") MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found") MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class") MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable") MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block") -MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled") -MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled") -MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks") MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression") MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence") MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'") MSG_DEF(JSMSG_MISSING_EXPONENT, 0, JSEXN_SYNTAXERR, "missing exponent") MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression") MSG_DEF(JSMSG_MISSING_FORMAL, 0, JSEXN_SYNTAXERR, "missing formal parameter") MSG_DEF(JSMSG_MISSING_HEXDIGITS, 0, JSEXN_SYNTAXERR, "missing hexadecimal digits after '0x'") MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits after '0o'")
--- a/js/src/tests/ecma_5/extensions/function-definition-with.js +++ b/js/src/tests/ecma_5/extensions/function-definition-with.js @@ -14,41 +14,38 @@ print(BUGNUMBER + ": " + summary); * BEGIN TEST * **************/ var called, obj; function inFile1() { return "in file"; } called = false; obj = { set inFile1(v) { called = true; } }; -with (obj) { +with (obj) function inFile1() { return "in file in with"; }; -} assertEq(inFile1(), "in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "inFile1"), true); assertEq(called, false); evaluate("function notInFile1() { return 'not in file'; }"); called = false; obj = { set notInFile1(v) { called = true; return "not in file 2"; } }; -with (obj) { +with (obj) function notInFile1() { return "not in file in with"; }; -} assertEq(notInFile1(), "not in file in with"); assertEq("set" in Object.getOwnPropertyDescriptor(obj, "notInFile1"), true); assertEq(called, false); function inFile2() { return "in file 1"; } called = false; obj = Object.defineProperty({}, "inFile2", { value: 42, configurable: false, enumerable: false }); -with (obj) { +with (obj) function inFile2() { return "in file 2"; }; -} assertEq(inFile2(), "in file 2"); assertEq(obj.inFile2, 42); /******************************************************************************/ if (typeof reportCompare === "function") reportCompare(true, true);
--- a/js/src/tests/ecma_5/extensions/strict-function-statements.js +++ b/js/src/tests/ecma_5/extensions/strict-function-statements.js @@ -4,91 +4,97 @@ */ // Ordinary function definitions should be unaffected. assertEq(testLenientAndStrict("function f() { }", parsesSuccessfully, parsesSuccessfully), true); +// Function statements within blocks are forbidden in strict mode code. +assertEq(testLenientAndStrict("{ function f() { } }", + parsesSuccessfully, + parseRaisesException(SyntaxError)), + true); + // Lambdas are always permitted within blocks. assertEq(testLenientAndStrict("{ (function f() { }) }", parsesSuccessfully, parsesSuccessfully), true); -// Function statements within unbraced blocks are forbidden in strict mode code. -// They are allowed only under if statements in sloppy mode. +// Function statements within any sort of statement are forbidden in strict mode code. assertEq(testLenientAndStrict("if (true) function f() { }", parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("while (true) function f() { }", - parseRaisesException(SyntaxError), + parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("do function f() { } while (true);", - parseRaisesException(SyntaxError), + parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(;;) function f() { }", - parseRaisesException(SyntaxError), + parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("for(x in []) function f() { }", - parseRaisesException(SyntaxError), + parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("with(o) function f() { }", - parseRaisesException(SyntaxError), + parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }", parsesSuccessfully, - parsesSuccessfully), + parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("x: function f() { }", parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }", parsesSuccessfully, - parsesSuccessfully), + parseRaisesException(SyntaxError)), true); // Lambdas are always permitted within any sort of statement. assertEq(testLenientAndStrict("if (true) (function f() { })", parsesSuccessfully, parsesSuccessfully), true); // Function statements are permitted in blocks within lenient functions. assertEq(parsesSuccessfully("function f() { function g() { } }"), true); -// Function statements are permitted in if statement within lenient functions. +// Function statements are permitted in any statement within lenient functions. assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"), true); assertEq(parseRaisesException(SyntaxError) ("function f() { 'use strict'; if (true) function g() { } }"), true); -assertEq(parsesSuccessfully("function f() { 'use strict'; { function g() { } } }"), +assertEq(parseRaisesException(SyntaxError) + ("function f() { 'use strict'; { function g() { } } }"), true); assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"), true); assertEq(parsesSuccessfully("function f() { 'use strict'; { (function g() { }) } }"), true); // Eval should behave the same way. (The parse-only tests use the Function constructor.) assertEq(testLenientAndStrict("function f() { }", completesNormally, completesNormally), true); assertEq(testLenientAndStrict("{ function f() { } }", completesNormally, - completesNormally), + raisesException(SyntaxError)), true); reportCompare(true, true);
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-eval.js +++ /dev/null @@ -1,38 +0,0 @@ -var log = ""; - -function f() { - log += g(); - function g() { return "outer-g"; } - - var o = { g: function () { return "with-g"; } }; - with (o) { - // Annex B.3.3.3 says g should be set on the nearest VariableEnvironment, - // and so should not change o.g. - eval(`{ - function g() { return "eval-g"; } - }`); - } - - log += g(); - log += o.g(); -} - -f(); - -function h() { - eval(` - // Should return true, as var bindings introduced by eval are configurable. - log += (delete q); - { - function q() { log += "q"; } - // Should return false, as lexical bindings introduced by eval are not - // configurable. - log += (delete q); - } - `); - return q; -} - -h()(); - -reportCompare(log, "outer-geval-gwith-gtruefalseq");
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-if.js +++ /dev/null @@ -1,42 +0,0 @@ -var log = ""; - -function f(x) { - if (x) - function g() { return "g0"; } - else - function g() { return "g1"; } - - log += g(); - - if (x) - function g() { return "g2"; } - else { - } - - log += g(); - - if (x) { - } else - function g() { return "g3"; } - - log += g(); - - if (x) - function g() { return "g4"; } - - log += g(); -} - -f(true); -f(false); - -try { - eval(` - if (1) - l: function foo() {} - `); -} catch (e) { - log += "e"; -} - -reportCompare(log, "g0g2g2g4g1g1g3g3e");
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-label.js +++ /dev/null @@ -1,43 +0,0 @@ -function expectSyntaxError(str) { - var threwSyntaxError; - try { - eval(str); - } catch (e) { - threwSyntaxError = e instanceof SyntaxError; - } - assertEq(threwSyntaxError, true); - - try { - eval('"use strict";' + str); - } catch (e) { - threwSyntaxError = e instanceof SyntaxError; - } - assertEq(threwSyntaxError, true); -} - -function expectSloppyPass(str) { - eval(str); - - try { - eval('"use strict";' + str); - } catch (e) { - threwSyntaxError = e instanceof SyntaxError; - } - assertEq(threwSyntaxError, true); -} - -expectSloppyPass(`l: function f1() {}`); -expectSloppyPass(`l0: l: function f1() {}`); -expectSloppyPass(`{ f1(); l: function f1() {} }`); -expectSloppyPass(`{ f1(); l0: l: function f1() {} }`); -expectSloppyPass(`{ f1(); l: function f1() { return 42; } } assertEq(f1(), 42);`); -expectSloppyPass(`eval("fe(); l: function fe() {}")`); -expectSyntaxError(`if (1) l: function f2() {}`); -expectSyntaxError(`if (1) {} else l: function f3() {}`); -expectSyntaxError(`do l: function f4() {} while (0)`); -expectSyntaxError(`while (0) l: function f5() {}`); -expectSyntaxError(`for (;;) l: function f6() {}`); -expectSloppyPass(`switch (1) { case 1: l: function f7() {} }`); -expectSloppyPass(`switch (1) { case 1: assertEq(f8(), 'f8'); case 2: l: function f8() { return 'f8'; } } assertEq(f8(), 'f8');`); - -reportCompare(0, 0);
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-property.js +++ /dev/null @@ -1,18 +0,0 @@ -// |reftest| skip-if(!xulRuntime.shell) - -// Define a global getter without a setter. -Object.defineProperty(this, "x", { - get: function () { return "get-x"; }, - configurable: true -}); - -// Simulate loading a 2nd script with evaluate, else we would DEFVAR the x and -// the above defineProperty would fail in trying to redefine a non-configurable -// property on the global. -evaluate(`{ - function x() { return "fun-x"; } -}`); - -// Annex B is supposed to be like an assignment. Should not blow away the -// existing setter-less getter. -reportCompare(x, "get-x");
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-same-name.js +++ /dev/null @@ -1,7 +0,0 @@ -{ - function f() { return "inner"; } -} - -function f() { return "outer"; } - -reportCompare(f(), "inner");
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b-with.js +++ /dev/null @@ -1,18 +0,0 @@ -var o = { f: "string-f" }; -with (o) { - var desc = Object.getOwnPropertyDescriptor(this, "f"); - assertEq(desc.value, undefined); - assertEq(desc.writable, true); - assertEq(desc.enumerable, true); - assertEq(desc.configurable, false); - function f() { - return "fun-f"; - } -} - -// Annex B explicitly assigns to the nearest VariableEnvironment, so the -// with-object "o" should have its property unchanged. -assertEq(o.f, "string-f"); -assertEq(f(), "fun-f"); - -reportCompare(true, true)
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-annex-b.js +++ /dev/null @@ -1,31 +0,0 @@ -var log = ""; - -log += typeof f; - -{ - log += f(); - - function f() { - return "f1"; - } -} - -log += f(); - -function g() { - log += typeof h; - - { - log += h(); - - function h() { - return "h1"; - } - } - - log += h(); -} - -g(); - -reportCompare(log, "undefinedf1f1undefinedh1h1");
deleted file mode 100644 --- a/js/src/tests/ecma_6/LexicalEnvironment/block-scoped-functions-strict.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict" - -var log = ""; - -function f() { - return "f0"; -} - -log += f(); - -{ - log += f(); - - function f() { - return "f1"; - } - - log += f(); -} - -log += f(); - -function g() { - function h() { - return "h0"; - } - - log += h(); - - { - log += h(); - - function h() { - return "h1"; - } - - log += h(); - } - - log += h(); -} - -g(); - -reportCompare(log, "f0f1f1f0h0h1h1h0");
--- a/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js +++ b/js/src/tests/ecma_6/extensions/for-loop-with-lexical-declaration-and-nested-function-statement.js @@ -13,40 +13,36 @@ var summary = "bindings at runtime"; print(BUGNUMBER + ": " + summary); /************** * BEGIN TEST * **************/ -for (let x = 0; x < 9; ++x) { +for (let x = 0; x < 9; ++x) function q1() {} -} { - for (let x = 0; x < 9; ++x) { + for (let x = 0; x < 9; ++x) function q2() {} - } } function f1() { - for (let x = 0; x < 9; ++x) { + for (let x = 0; x < 9; ++x) function q3() {} - } } f1(); function f2() { { - for (let x = 0; x < 9; ++x) { + for (let x = 0; x < 9; ++x) function q4() {} - } } } f2(); for (let x = 0; x < 9; ++x) { // deliberately inside a block statement function q5() {}
new file mode 100644 --- /dev/null +++ b/js/src/tests/js1_5/Regress/regress-326453.js @@ -0,0 +1,21 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: Blake Kaplan + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 326453; +var summary = 'Do not assert: while decompiling'; +var actual = 'No Crash'; +var expect = 'No Crash'; + +printBugNumber(BUGNUMBER); +printStatus (summary); + +function f() { with({})function g() { }; printStatus(); } + +printStatus(f.toString()); + +reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/extensions/regress-245795.js +++ b/js/src/tests/js1_5/extensions/regress-245795.js @@ -7,25 +7,28 @@ var BUGNUMBER = 245795; var summary = 'eval(uneval(function)) should be round-trippable'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); -function a() +if (typeof uneval != 'undefined') { - b = function() {}; -} + function a() + { + b = function() {}; + } -var r = "function a() { b = function() {}; }"; -eval(uneval(a)); + var r = "function a() { b = function() {}; }"; + eval(uneval(a)); -var v = a.toString().replace(/[ \n]+/g, ' '); -print(v) - -printStatus("[" + v + "]"); + var v = a.toString().replace(/[ \n]+/g, ' '); + print(v) + + printStatus("[" + v + "]"); -expect = r; -actual = v; + expect = r; + actual = v; -reportCompare(expect, actual, summary); + reportCompare(expect, actual, summary); +}
--- a/js/src/tests/js1_5/extensions/regress-406572.js +++ b/js/src/tests/js1_5/extensions/regress-406572.js @@ -17,24 +17,22 @@ if (typeof window != 'undefined') try { actual = "FAIL: Unexpected exception thrown"; var win = window; var windowString = String(window); window = 1; reportCompare(windowString, String(window), "window should be readonly"); + actual = ""; // We should reach this line, and throw an exception after it + if (1) function window() { return 1; } - // We should reach this line without throwing. Annex B means the - // block-scoped function above gets an assignment to 'window' in the - // nearest 'var' environment, but since 'window' is read-only, the - // assignment silently fails. - actual = ""; + actual = "FAIL: this line should never be reached"; // The test harness might rely on window having its original value: // restore it. window = win; } catch (e) { } } else
--- a/js/src/tests/js1_8_5/reflect-parse/declarations.js +++ b/js/src/tests/js1_8_5/reflect-parse/declarations.js @@ -1,15 +1,15 @@ // |reftest| skip-if(!xulRuntime.shell) function test() { // Bug 632056: constant-folding program([exprStmt(ident("f")), ifStmt(lit(1), - blockStmt([funDecl(ident("f"), [], blockStmt([]))]), + funDecl(ident("f"), [], blockStmt([])), null)]).assert(Reflect.parse("f; if (1) function f(){}")); // declarations assertDecl("var x = 1, y = 2, z = 3", varDecl([{ id: ident("x"), init: lit(1) }, { id: ident("y"), init: lit(2) }, { id: ident("z"), init: lit(3) }])); assertDecl("var x, y, z", @@ -81,17 +81,9 @@ assertStmt("function g(x) { var x }", funDecl(ident("g"), [ident("x")], blockStmt([varDecl[{ id: ident("x"), init: null }]]))); assertProg("f.p = 1; var f; f.p; function f(){}", [exprStmt(aExpr("=", dotExpr(ident("f"), ident("p")), lit(1))), varDecl([{ id: ident("f"), init: null }]), exprStmt(dotExpr(ident("f"), ident("p"))), funDecl(ident("f"), [], blockStmt([]))]); } -assertBlockStmt("{ function f(x) {} }", - blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])); - -// Annex B semantics should not change parse tree. -assertBlockStmt("{ let f; { function f(x) {} } }", - blockStmt([letDecl([{ id: ident("f"), init: null }]), - blockStmt([funDecl(ident("f"), [ident("x")], blockStmt([]))])])); - runtest(test);
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1748,16 +1748,17 @@ CASE(JSOP_UNUSED181) CASE(JSOP_UNUSED182) CASE(JSOP_UNUSED183) CASE(JSOP_UNUSED187) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_UNUSED212) +CASE(JSOP_UNUSED213) CASE(JSOP_UNUSED219) CASE(JSOP_UNUSED220) CASE(JSOP_UNUSED221) CASE(JSOP_UNUSED222) CASE(JSOP_UNUSED223) CASE(JSOP_CONDSWITCH) CASE(JSOP_TRY) { @@ -2083,22 +2084,16 @@ CASE(JSOP_BINDNAME) PUSH_OBJECT(*scope); static_assert(JSOP_BINDNAME_LENGTH == JSOP_BINDGNAME_LENGTH, "We're sharing the END_CASE so the lengths better match"); } END_CASE(JSOP_BINDNAME) -CASE(JSOP_BINDVAR) -{ - PUSH_OBJECT(REGS.fp()->varObj()); -} -END_CASE(JSOP_BINDVAR) - #define BITWISE_OP(OP) \ JS_BEGIN_MACRO \ int32_t i, j; \ if (!ToInt32(cx, REGS.stackHandleAt(-2), &i)) \ goto error; \ if (!ToInt32(cx, REGS.stackHandleAt(-1), &j)) \ goto error; \ i = i OP j; \
--- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2047,25 +2047,17 @@ 1234567890123456789012345678901234567890 * Operands: * Stack: => */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED212, 212, "unused212", NULL, 1, 0, 0, JOF_BYTE) \ - /* - * Pushes the nearest 'var' environment. - * - * Category: Variables and Scopes - * Type: Free Variables - * Operands: - * Stack: => scope - */ \ - macro(JSOP_BINDVAR, 213, "bindvar", NULL, 1, 0, 1, JOF_BYTE) \ + macro(JSOP_UNUSED213, 213, "unused213", NULL, 1, 0, 0, JOF_BYTE) \ /* * Pushes the global scope onto the stack if the script doesn't have a * non-syntactic global scope. Otherwise will act like JSOP_BINDNAME. * * 'nameIndex' is only used when acting like JSOP_BINDNAME. * Category: Variables and Scopes * Type: Free Variables * Operands: uint32_t nameIndex
--- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -28,17 +28,17 @@ namespace js { * this wiki page: * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 329; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 424, +static_assert(JSErr_Limit == 421, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " "expected JSErr_Limit value."); class XDRBuffer { public: explicit XDRBuffer(JSContext* cx)