mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Backout bug 1071646, bug 1231758 for breaking the web. (r=woe)
This commit is contained in:
parent
44fade98a9
commit
07d8830040
@ -2040,9 +2040,6 @@ ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst)
|
||||
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);
|
||||
|
||||
@ -2414,9 +2411,6 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
||||
case PNK_VAR:
|
||||
return declaration(pn, dst);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
return declaration(pn->pn_left, dst);
|
||||
|
||||
case PNK_LETBLOCK:
|
||||
return letBlock(pn, dst);
|
||||
|
||||
|
@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn)
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
@ -1738,7 +1738,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
|
||||
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;
|
||||
}
|
||||
@ -2344,10 +2344,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
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;
|
||||
@ -3098,23 +3094,11 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
|
||||
if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0))
|
||||
return false;
|
||||
|
||||
// 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();
|
||||
|
||||
// Advance |cases| to refer to the switch case list.
|
||||
cases = cases->expr();
|
||||
} else {
|
||||
MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
|
||||
top = offset();
|
||||
@ -4414,24 +4398,12 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption)
|
||||
*/
|
||||
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;
|
||||
@ -4486,18 +4458,13 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
|
||||
op == JSOP_STRICTSETGNAME)
|
||||
{
|
||||
MOZ_ASSERT(emitOption != PushInitialValues);
|
||||
if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) {
|
||||
if (!emitIndex32(JSOP_BINDGNAME, atomIndex))
|
||||
JSOp bindOp;
|
||||
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
|
||||
bindOp = JSOP_BINDNAME;
|
||||
else
|
||||
bindOp = JSOP_BINDGNAME;
|
||||
if (!emitIndex32(bindOp, 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool oldEmittingForInit = emittingForInit;
|
||||
@ -4521,7 +4488,7 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
|
||||
|
||||
// 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))
|
||||
@ -5359,28 +5326,6 @@ BytecodeEmitter::emitLetBlock(ParseNode* pnLet)
|
||||
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
|
||||
@ -5392,17 +5337,7 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn)
|
||||
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))
|
||||
@ -6263,12 +6198,6 @@ BytecodeEmitter::emitComprehensionFor(ParseNode* 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());
|
||||
@ -6279,25 +6208,9 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
* 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;
|
||||
}
|
||||
|
||||
@ -6405,28 +6318,10 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
* 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;
|
||||
@ -8063,6 +7958,7 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn)
|
||||
// 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;
|
||||
@ -8123,12 +8019,22 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn)
|
||||
}
|
||||
}
|
||||
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))
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -8346,7 +8252,6 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
||||
|
||||
switch (pn->getKind()) {
|
||||
case PNK_FUNCTION:
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
if (!emitFunction(pn))
|
||||
return false;
|
||||
break;
|
||||
|
@ -121,12 +121,7 @@ enum VarEmitOption {
|
||||
// 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
|
||||
@ -254,10 +249,7 @@ struct BytecodeEmitter
|
||||
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);
|
||||
@ -472,8 +464,6 @@ struct BytecodeEmitter
|
||||
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
|
||||
|
@ -100,11 +100,6 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
*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;
|
||||
@ -1788,9 +1783,6 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
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);
|
||||
|
||||
|
@ -444,40 +444,24 @@ class FullParseHandler
|
||||
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)) {
|
||||
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) {
|
||||
@ -672,11 +656,6 @@ class FullParseHandler
|
||||
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);
|
||||
}
|
||||
@ -748,7 +727,7 @@ class FullParseHandler
|
||||
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) {
|
||||
@ -1011,7 +990,7 @@ FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseN
|
||||
}
|
||||
|
||||
inline bool
|
||||
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
|
||||
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op)
|
||||
{
|
||||
if (pn->isUsed()) {
|
||||
pn = makeAssignment(pn, init);
|
||||
|
@ -791,12 +791,6 @@ class NameResolver
|
||||
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
|
||||
|
@ -283,8 +283,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
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);
|
||||
|
@ -165,7 +165,6 @@ class PackedScopeCoordinate
|
||||
F(FORIN) \
|
||||
F(FOROF) \
|
||||
F(FORHEAD) \
|
||||
F(ANNEXB_FUNCTION) \
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
F(MUTATEPROTO) \
|
||||
@ -272,9 +271,6 @@ IsDeleteKind(ParseNodeKind kind)
|
||||
* 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
|
||||
@ -784,8 +780,7 @@ class ParseNode
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1612,8 +1607,6 @@ struct Definition : public ParseNode
|
||||
IMPORT
|
||||
};
|
||||
|
||||
static bool test(const ParseNode& pn) { return pn.isDefn(); }
|
||||
|
||||
bool canHaveInitializer() { return int(kind()) <= int(ARG); }
|
||||
|
||||
static const char* kindString(Kind kind);
|
||||
@ -1622,8 +1615,6 @@ struct Definition : public ParseNode
|
||||
if (getKind() == PNK_FUNCTION) {
|
||||
if (isOp(JSOP_GETARG))
|
||||
return ARG;
|
||||
if (isOp(JSOP_INITLEXICAL))
|
||||
return LET;
|
||||
return VAR;
|
||||
}
|
||||
MOZ_ASSERT(getKind() == PNK_NAME);
|
||||
|
@ -103,7 +103,9 @@ ParseContext<FullParseHandler>::checkLocalsOverflow(TokenStream& ts)
|
||||
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;
|
||||
@ -220,8 +222,6 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
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);
|
||||
@ -240,7 +240,7 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
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;
|
||||
}
|
||||
@ -258,11 +258,12 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
}
|
||||
|
||||
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());
|
||||
@ -392,7 +393,7 @@ 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()) {
|
||||
@ -1198,6 +1199,18 @@ 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;
|
||||
|
||||
@ -1392,7 +1405,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
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;
|
||||
@ -1435,7 +1448,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
return false;
|
||||
pn->dn_uses = lhs;
|
||||
dn->pn_link = nullptr;
|
||||
dn = &lhs->as<Definition>();
|
||||
dn = (Definition*) lhs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1446,7 +1459,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
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;
|
||||
@ -1478,22 +1491,18 @@ struct BindData
|
||||
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) {
|
||||
@ -1516,11 +1525,6 @@ struct BindData
|
||||
return isConst_;
|
||||
}
|
||||
|
||||
bool isAnnexB() {
|
||||
MOZ_ASSERT(isInitialized());
|
||||
return isAnnexB_;
|
||||
}
|
||||
|
||||
const LetData& letData() {
|
||||
MOZ_ASSERT(kind_ == LexicalBinding);
|
||||
return letData_;
|
||||
@ -1557,19 +1561,17 @@ struct BindData
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2235,13 +2237,17 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
ParseNode** pn_)
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
ParseNode** pn_, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed)
|
||||
{
|
||||
MOZ_ASSERT(pc->atBodyLevel() || !pc->sc->strict());
|
||||
|
||||
ParseNode*& pn = *pn_;
|
||||
*pbodyProcessed = false;
|
||||
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
* function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
|
||||
@ -2250,8 +2256,20 @@ Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
MOZ_ASSERT(!dn->isUsed());
|
||||
MOZ_ASSERT(dn->isDefn());
|
||||
|
||||
if (dn->kind() == Definition::CONSTANT || dn->kind() == Definition::LET)
|
||||
return reportRedeclaration(nullptr, Definition::VAR, funName);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Body-level function statements are effectively variable
|
||||
@ -2261,6 +2279,7 @@ Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
* 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
|
||||
@ -2276,7 +2295,8 @@ Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
if (!makeDefIntoUse(dn, pn, funName))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
} else if (bodyLevel) {
|
||||
/*
|
||||
* If this function was used before it was defined, claim the
|
||||
* pre-created definition node for this function that primaryExpr
|
||||
@ -2301,147 +2321,52 @@ Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* No further binding (in BindNameToSlot) is needed for functions. */
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
|
||||
if (bodyLevel) {
|
||||
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)
|
||||
{
|
||||
ParseNode*& pn = *pn_;
|
||||
*pbodyProcessed = false;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
// Synthesize a new 'var' binding if one does not exist.
|
||||
ParseNode* varNode = newBindingNode(funName, /* functionScope = */ true);
|
||||
if (!varNode)
|
||||
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))
|
||||
return false;
|
||||
|
||||
if (annexDef) {
|
||||
/*
|
||||
* 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);
|
||||
|
||||
// Synthesize an assignment assigning the lexical name to the
|
||||
// 'var' name for Annex B.
|
||||
|
||||
ParseNode* rhs = newName(funName);
|
||||
if (!rhs)
|
||||
/*
|
||||
* 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 (!noteNameUse(funName, rhs))
|
||||
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);
|
||||
@ -2550,8 +2475,7 @@ template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
Node* pn, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed,
|
||||
Node* assignmentForAnnexBOut)
|
||||
bool* pbodyProcessed)
|
||||
{
|
||||
*pbodyProcessed = false;
|
||||
|
||||
@ -2559,18 +2483,10 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
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;
|
||||
@ -2581,13 +2497,16 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
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) {
|
||||
@ -2668,8 +2587,7 @@ 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);
|
||||
|
||||
@ -2677,13 +2595,12 @@ Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHand
|
||||
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)
|
||||
@ -2870,6 +2787,7 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
||||
if (!addFreeVariablesFromLazyFunction(fun, pc))
|
||||
return false;
|
||||
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
PropagateTransitiveParseFlags(funbox, outerpc->sc);
|
||||
return true;
|
||||
} while (false);
|
||||
@ -2887,6 +2805,8 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
||||
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
|
||||
@ -3128,24 +3048,6 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, 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;
|
||||
@ -3173,35 +3075,12 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
|
||||
return null();
|
||||
}
|
||||
|
||||
Node assignmentForAnnexB;
|
||||
Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind,
|
||||
PredictUninvoked, &assignmentForAnnexB);
|
||||
if (!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();
|
||||
|
||||
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;
|
||||
return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -3805,24 +3684,19 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
|
||||
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.
|
||||
/*
|
||||
* 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.
|
||||
@ -3955,29 +3829,25 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, HandlePropertyName name,
|
||||
ParseNode* pn)
|
||||
{
|
||||
data->setNameNode(pn);
|
||||
return data->bind(name, this);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
{
|
||||
RootedPropertyName name(context, pn->name());
|
||||
return bindUninitialized(data, name, pn);
|
||||
MOZ_ASSERT(pn->isKind(PNK_NAME));
|
||||
|
||||
RootedPropertyName name(context, pn->pn_atom->asPropertyName());
|
||||
|
||||
data->setNameNode(pn);
|
||||
if (!data->bind(name, this))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, HandlePropertyName name,
|
||||
ParseNode* pn)
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
{
|
||||
if (!bindUninitialized(data, name, pn))
|
||||
if (!bindUninitialized(data, pn))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -3998,14 +3868,6 @@ Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, Hand
|
||||
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)
|
||||
@ -4554,7 +4416,7 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
|
||||
if (!bindBeforeInitializer && !data.bind(name, this))
|
||||
return null();
|
||||
|
||||
if (!handler.finishInitializerAssignment(pn2, init))
|
||||
if (!handler.finishInitializerAssignment(pn2, init, data.op()))
|
||||
return null();
|
||||
}
|
||||
}
|
||||
@ -4575,45 +4437,28 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
|
||||
|
||||
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,
|
||||
@ -4624,7 +4469,7 @@ Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat,
|
||||
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;
|
||||
@ -4650,12 +4495,8 @@ Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat,
|
||||
* 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);
|
||||
}
|
||||
MOZ_ASSERT(!blockScopes[stmt->blockid]);
|
||||
blockScopes[stmt->blockid].set(blockObj);
|
||||
|
||||
@ -4685,54 +4526,33 @@ CurrentLexicalStaticBlock(ParseContext<FullParseHandler>* pc)
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
ParseNode* pn,
|
||||
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();
|
||||
|
||||
/*
|
||||
@ -5244,7 +5064,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
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);
|
||||
@ -5991,7 +5811,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
afterReturn = true;
|
||||
}
|
||||
}
|
||||
handler.addStatementToList(body, stmt, pc);
|
||||
handler.addList(body, stmt);
|
||||
}
|
||||
|
||||
// In ES6, lexical bindings cannot be accessed until initialized. If
|
||||
@ -6010,7 +5830,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
|
||||
if (!casepn)
|
||||
return null();
|
||||
handler.addCaseStatementToList(caseList, casepn, pc);
|
||||
handler.addList(caseList, casepn);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6837,7 +6657,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
ParseNode* nameNode = null();
|
||||
ParseNode* methodsOrBlock = classMethods;
|
||||
if (name) {
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos);
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
|
||||
@ -6848,7 +6668,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
|
||||
ParseNode* outerBinding = null();
|
||||
if (classContext == ClassStatement) {
|
||||
outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos);
|
||||
outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ struct StmtInfoPC : public StmtInfoBase
|
||||
{}
|
||||
};
|
||||
|
||||
typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> FuncStmtSet;
|
||||
class SharedContext;
|
||||
|
||||
typedef Vector<Definition*, 16> DeclVector;
|
||||
@ -235,6 +236,10 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
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;
|
||||
|
||||
@ -272,6 +277,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
parserPC(&prs->pc),
|
||||
oldpc(prs->pc),
|
||||
lexdeps(prs->context),
|
||||
funcStmts(nullptr),
|
||||
innerFunctions(prs->context, TraceableVector<JSFunction*>(prs->context)),
|
||||
newDirectives(newDirectives),
|
||||
inDeclDestructuring(false)
|
||||
@ -289,7 +295,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
|
||||
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;
|
||||
@ -303,23 +308,19 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
// 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
|
||||
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() {
|
||||
@ -727,8 +728,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
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);
|
||||
@ -794,10 +794,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
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);
|
||||
|
||||
@ -846,17 +844,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
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);
|
||||
|
||||
@ -871,9 +860,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
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);
|
||||
|
@ -583,12 +583,6 @@ class MOZ_STACK_CLASS StmtInfoStack
|
||||
|
||||
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;
|
||||
|
@ -290,7 +290,6 @@ class SyntaxParseHandler
|
||||
|
||||
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; }
|
||||
|
||||
@ -334,7 +333,6 @@ class SyntaxParseHandler
|
||||
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) {
|
||||
@ -356,7 +354,7 @@ class SyntaxParseHandler
|
||||
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) {}
|
||||
|
@ -4,6 +4,6 @@
|
||||
// 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);
|
||||
|
18
js/src/jit-test/tests/baseline/bug1081850.js
Normal file
18
js/src/jit-test/tests/baseline/bug1081850.js
Normal file
@ -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");
|
@ -1,3 +1,2 @@
|
||||
for (var x in x) {
|
||||
for (var x in x)
|
||||
function x() {}
|
||||
}
|
||||
|
@ -8,11 +8,7 @@ try {
|
||||
}
|
||||
}(), function() {}))
|
||||
} catch (e) {};
|
||||
var log = "";
|
||||
evaluate(`
|
||||
try {
|
||||
function x() {}
|
||||
assertEq(String(b), "function () {}");
|
||||
} catch (e) { log += "e"; }
|
||||
`);
|
||||
assertEq(log, "e");
|
||||
} catch (e) { throw (e); }
|
||||
|
@ -2259,26 +2259,6 @@ BaselineCompiler::emit_JSOP_BINDGNAME()
|
||||
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()
|
||||
{
|
||||
|
@ -141,7 +141,6 @@ namespace jit {
|
||||
_(JSOP_DELNAME) \
|
||||
_(JSOP_GETIMPORT) \
|
||||
_(JSOP_GETINTRINSIC) \
|
||||
_(JSOP_BINDVAR) \
|
||||
_(JSOP_DEFVAR) \
|
||||
_(JSOP_DEFCONST) \
|
||||
_(JSOP_DEFLET) \
|
||||
|
@ -158,7 +158,6 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
|
||||
|
||||
case JSOP_GETNAME:
|
||||
case JSOP_BINDNAME:
|
||||
case JSOP_BINDVAR:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_STRICTSETNAME:
|
||||
case JSOP_DELNAME:
|
||||
|
@ -8428,16 +8428,6 @@ CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool
|
||||
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);
|
||||
|
||||
|
@ -329,7 +329,6 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
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);
|
||||
|
@ -1943,9 +1943,6 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
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;
|
||||
@ -8421,16 +8418,6 @@ IonBuilder::jsop_bindname(PropertyName* name)
|
||||
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)
|
||||
{
|
||||
|
@ -689,7 +689,6 @@ class IonBuilder
|
||||
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);
|
||||
|
@ -3361,16 +3361,6 @@ LIRGenerator::visitBindNameCache(MBindNameCache* 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)
|
||||
{
|
||||
|
@ -230,7 +230,6 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
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);
|
||||
|
@ -10758,39 +10758,6 @@ class MBindNameCache
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
|
@ -162,7 +162,6 @@ namespace jit {
|
||||
_(GetPropertyPolymorphic) \
|
||||
_(SetPropertyPolymorphic) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(GuardShape) \
|
||||
_(GuardReceiverPolymorphic) \
|
||||
_(GuardObjectGroup) \
|
||||
|
@ -167,21 +167,14 @@ CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -192,7 +185,10 @@ DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject sc
|
||||
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);
|
||||
}
|
||||
|
||||
@ -857,8 +853,9 @@ 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.
|
||||
@ -869,12 +866,13 @@ InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
|
||||
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;
|
||||
}
|
||||
|
@ -588,7 +588,6 @@ 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);
|
||||
|
@ -5774,22 +5774,6 @@ class LBindNameCache : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
{
|
||||
|
@ -277,7 +277,6 @@
|
||||
_(GetPropertyPolymorphicV) \
|
||||
_(GetPropertyPolymorphicT) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(CallGetProperty) \
|
||||
_(GetNameCache) \
|
||||
_(CallGetIntrinsicValue) \
|
||||
|
@ -269,9 +269,6 @@ 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'")
|
||||
|
@ -19,9 +19,8 @@ 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);
|
||||
@ -29,9 +28,8 @@ 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);
|
||||
@ -41,9 +39,8 @@ 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);
|
||||
|
||||
|
@ -9,41 +9,46 @@ assertEq(testLenientAndStrict("function f() { }",
|
||||
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,
|
||||
@ -51,7 +56,7 @@ assertEq(testLenientAndStrict("x: function f() { }",
|
||||
true);
|
||||
assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
// Lambdas are always permitted within any sort of statement.
|
||||
@ -64,7 +69,7 @@ assertEq(testLenientAndStrict("if (true) (function f() { })",
|
||||
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);
|
||||
|
||||
@ -72,7 +77,8 @@ 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() { }) }"),
|
||||
@ -88,7 +94,7 @@ assertEq(testLenientAndStrict("function f() { }",
|
||||
true);
|
||||
assertEq(testLenientAndStrict("{ function f() { } }",
|
||||
completesNormally,
|
||||
completesNormally),
|
||||
raisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -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");
|
@ -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");
|
@ -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);
|
@ -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");
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
function f() { return "inner"; }
|
||||
}
|
||||
|
||||
function f() { return "outer"; }
|
||||
|
||||
reportCompare(f(), "inner");
|
@ -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)
|
@ -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");
|
@ -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");
|
@ -18,31 +18,27 @@ 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();
|
||||
|
||||
|
21
js/src/tests/js1_5/Regress/regress-326453.js
Normal file
21
js/src/tests/js1_5/Regress/regress-326453.js
Normal file
@ -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);
|
@ -12,20 +12,23 @@ var expect = '';
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
function a()
|
||||
if (typeof uneval != 'undefined')
|
||||
{
|
||||
function a()
|
||||
{
|
||||
b = function() {};
|
||||
}
|
||||
|
||||
var r = "function a() { b = function() {}; }";
|
||||
eval(uneval(a));
|
||||
|
||||
var v = a.toString().replace(/[ \n]+/g, ' ');
|
||||
print(v)
|
||||
|
||||
printStatus("[" + v + "]");
|
||||
|
||||
expect = r;
|
||||
actual = v;
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
}
|
||||
|
||||
var r = "function a() { b = function() {}; }";
|
||||
eval(uneval(a));
|
||||
|
||||
var v = a.toString().replace(/[ \n]+/g, ' ');
|
||||
print(v)
|
||||
|
||||
printStatus("[" + v + "]");
|
||||
|
||||
expect = r;
|
||||
actual = v;
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
@ -22,14 +22,12 @@ if (typeof window != 'undefined')
|
||||
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.
|
||||
|
@ -4,7 +4,7 @@ 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
|
||||
|
||||
@ -86,12 +86,4 @@ assertProg("f.p = 1; var f; f.p; function f(){}",
|
||||
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);
|
||||
|
@ -1753,6 +1753,7 @@ CASE(JSOP_UNUSED209)
|
||||
CASE(JSOP_UNUSED210)
|
||||
CASE(JSOP_UNUSED211)
|
||||
CASE(JSOP_UNUSED212)
|
||||
CASE(JSOP_UNUSED213)
|
||||
CASE(JSOP_UNUSED219)
|
||||
CASE(JSOP_UNUSED220)
|
||||
CASE(JSOP_UNUSED221)
|
||||
@ -2088,12 +2089,6 @@ CASE(JSOP_BINDNAME)
|
||||
}
|
||||
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; \
|
||||
|
@ -2052,15 +2052,7 @@
|
||||
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.
|
||||
|
@ -33,7 +33,7 @@ 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 "
|
||||
|
Loading…
Reference in New Issue
Block a user