import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1071646 - Light refactoring of lexical binding helpers in Parser. (r=jorendorff) (08dd35a4aa)
- Bug 1230710 - Reenable direct eval and arrow functions in derived class constructors. (r=jorendorff, r=shu) (8767ca1400)
- Bug 1071646 - Make functions block-scoped in JS and implement Annex B semantics for compatibility. (r=jorendorff) (3a4b960615)
- Bug 1071646 - Support labelled function declarations in sloppy mode per Annex B.3.2. (r=jorendorff) (7688ce37ca)
- Bug 1071646 - Introduce JSOP_BINDVAR to support Annex B.3.3.3. (r=jorendorff) (6f19a9c7c9)
- Bug 1071646 - Cast ParseNode to Definition using as<T>. (r=jorendorff) (c96ca0203c)
- Bug 1071646 - Support JSOP_BINDVAR in Baseline. (r=jandem) (d7912d6cd7)
- Bug 1071646 - Support JSOP_BINDVAR in Ion. (r=jandem) (9c76c0f995)
- Bug 1227677 - Emit code for PNK_COMPREHENSIONFOR using separate code from that used for for-loops. r=shu (d565e9e21d)
- Bug 1227677 - Minor renaming. r=shu (ec3a477823)
- Bug 1227677 - Rename the misnomer |letDecl| variable associated with for-in/of loop variables to |letBlockScope|, as that's much closer to its actual meaning. (Notably, |for (let x of []);| does *not* mean |*letDecl|.) r=shu (c9feaa68c2)
- Bug 1227677 - Simplify code in light of the fact that for-in/of loops never have a PNK_LEXICALSCOPE declaration node and therefore never have a letBlockScope requiring pushing and popping. r=shu (19ff248532)
- Bug 1225298 - Use GCHashSet for SavedStack set of frames, r=terrence (9ed6c0f4c3)
- Bug 1225474: Ensure we only ever seed the js::SavedStacks PRNG state with valid states. r=fitzgen (99c858644b)
- Bug 1230162 - allocate less, when we can. r=luke (563337bc70)
- Bug 1225298 - Use GCHashSet for SymbolRegistry, r=terrence (4d0cfc2931)
- Bug 1227028: TraceLogger - Fix when to keep the payload of a TraceLogger event, r=bbouvier (cb3bea30fc)
- Bug 1228238 - "TraceLogger: don't enable tracelogger unless TLOPTIONS is set". r=hv1989 (c9d56ad367)
- Bug 1224809 - "TraceLogger: Document what are enabled in 'TLLOG=Default' and 'TLLOG=IonCompiler'". r=hv1989 (64b6ebceea)
- Bug 1223767 - "TraceLogger: Assertion failure: i < size_, at js/src/vm/TraceLoggingTypes.h:210". r=hv1989 (1b50e8acb7)
- Bug 1227914: TraceLogger - Limit the memory tracelogger can take, r=bbouvier (458da9b2f7)
- Bug 1259403 - Tracelogger: Only increase capacity by multiples of 2, r=bbouvier (1e4bf23eab)
- Bug 1225346 - Fix parseModule() error handling r=terrence (b100c793dc)
- bug 1224308 - Remove some Makefile cruft. r=gps (f094c44df9)
- Bug 1217911 - Use correct scope when bailing out a module script r=shu (c4f39cf294)
- Bug 1230039 - IonMonkey: MIPS: Implement cacheFlush for Loongson3. r=jandem (01c5af81f2)
- Bug 922406 - Ion-compile global scripts that use 'this'. r=shu (cb66effddf)
- Bug 1227567 - Add Ion cache for module namepsace imports r=shu (72f4a4e971)
- Bug 1216107 - Remove bogus assert. (r=arai) (bad84d1795)
- Bug 1225367 - Fix bogus asserts when storing symbols to typed arrays. r=h4writer (0220c4ca3b)
- Bug 1226188 - Define the DEFINED_ON_DISPATCH_RESULT macro such that it can be called with no arguments as well as with one argument. r=nbp (69138260f3)
- Bug 1227567 - Add baseline IC to optimise module namespace imports r=shu (692aaf3e8a)
- Bug 1226816: SharedStubs - Don't enable the call scripted get prop shared stub in ionmonkey yet, r=jandem (b71ff142f4)
- Bug 1229396: Templatize get{Float,Double,SimdData}; r=jandem (3b75e4e4ff)
- Bug 1229396: Templatize constants merging in the shared x86 masm; r=jandem (d32cdb2b1b)
- Bug 1229396: Templatize Float/Double/SimdData in MacroAssembler-x86-shared.h; r=jandem (0ae16c027a)
- Bug 1229396: Propagate OOM when pushing elements to the uses array; r=jandem (8ddb05d926)
- bits of  Backed out 2 changesets (bug 1229604) (1285191abd)
- Bug 1229821 - IonMonkey: MIPS: Fix merge macro assemblers. r=bbouvier (078b702c7d)
- Bug 1230404 - IonMonkey: MIPS32: Fix load32(wasm::SymbolicAddress, Register). r=bbouvier (c7861440e2)
- Bug 1213165 - IonMonkey: MIPS32: Fix ion/bug901086.js failed. r=nbp f=rankov (63d3f07b39)
- Bug 1230403 - IonMonkey: MIPS: Add add64 to mips32. r=arai (105e49c927)
- Bug 1208259 - ARM64: Handle an empty nursery in branchValueIsNurseryObject(). r=bhackett (95d6a15432)
- Bug 1207827 - Fix OOM error in ARM64 simulator. r=nbp (076d2a5a5e)
- Bug 1204700 - ARM: Use a different scratch register for store32. r=sstangl (b6e70c4d26)
- Bug 1221359 - Fix ARM assembler assertion that doesn't hold if we are OOM r=jolesen (7e79f2b4f9)
- No bug. Remove some long obsolete files. r=woof! (c95a1f341b)
This commit is contained in:
2023-05-29 11:32:23 +08:00
parent ed2747a092
commit 3931475e3b
119 changed files with 2125 additions and 858 deletions
-6
View File
@@ -238,12 +238,6 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
return false;
}
if (evalType == DIRECT_EVAL && caller.script()->isDerivedClassConstructor()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DISABLED_DERIVED_CLASS,
"direct eval");
return false;
}
// ES5 15.1.2.1 step 1.
if (args.length() < 1) {
args.rval().setUndefined();
+6
View File
@@ -2040,6 +2040,9 @@ 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);
@@ -2411,6 +2414,9 @@ 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);
+3 -1
View File
@@ -887,7 +887,9 @@ SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
if (!ToInt32(cx, args[0], &seed))
return false;
cx->compartment()->savedStacks().setRNGState(seed, seed * 33);
// Either one or the other of the seed arguments must be non-zero;
// make this true no matter what value 'seed' has.
cx->compartment()->savedStacks().setRNGState(seed, (seed + 1) * 33);
return true;
}
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
cd $SOURCE
exec env MOZCONFIG=$SOURCE/mozconfig-b2g-ra ./mach build
@@ -1,4 +0,0 @@
#!/bin/sh
cd $SOURCE
exec ./mach build
-7
View File
@@ -1,7 +0,0 @@
#!/bin/sh -x
mkdir $ANALYZED_OBJDIR
cd $ANALYZED_OBJDIR
$SOURCE/js/src/configure --enable-debug --enable-optimize --enable-root-analysis
# perl -i -pe 's!ccache ([gc])!$1!' "$TARGET_BUILDROOT/config/autoconf.mk"
make -j12 -s
@@ -8,5 +8,12 @@
"algorithm": "sha512",
"filename": "gcc.tar.xz",
"unpack": true
},
{
"size": 4431740,
"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
"algorithm": "sha512",
"filename": "gtk3.tar.xz",
"unpack": true
}
]
-1
View File
@@ -199,7 +199,6 @@ compartment.
* "CC_WAITING"
* "CC_FORCED"
* "LOAD_END"
* "POST_COMPARTMENT"
* "PAGE_HIDE"
* "NSJSCONTEXT_DESTROY"
* "SET_NEW_DOCUMENT"
+485 -102
View File
@@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn)
}
bool
BytecodeEmitter::atBodyLevel() const
BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) 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 = !innermostStmt()->enclosing;
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
->as<StaticBlockObject>()
.enclosingStaticScope() == sc->staticScope());
bool bl = !stmt->enclosing;
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, stmt->staticScope
->as<StaticBlockObject>()
.enclosingStaticScope() == sc->staticScope());
return bl;
}
return !innermostStmt() || sc->isModuleBox();
return !stmt || 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 = (Definition*) pn;
dn = &pn->as<Definition>();
} else {
return true;
}
@@ -2344,6 +2344,10 @@ 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;
@@ -3084,11 +3088,23 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
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();
@@ -3449,10 +3465,34 @@ BytecodeEmitter::emitSetThis(ParseNode* pn)
JSOp setOp = name->getOp();
// Handle the eval case. Only accept the strict variant, as eval in a
// derived class constructor must be strict.
if (setOp == JSOP_STRICTSETNAME) {
if (!emitAtomOp(name, JSOP_GETNAME))
return false;
if (!emit1(JSOP_CHECKTHISREINIT))
return false;
if (!emit1(JSOP_POP))
return false;
if (!emitAtomOp(name, JSOP_BINDNAME))
return false;
if (!emit1(JSOP_SWAP))
return false;
return emitAtomOp(name, setOp);
}
JSOp getOp;
switch (setOp) {
case JSOP_SETLOCAL: getOp = JSOP_GETLOCAL; break;
case JSOP_SETALIASEDVAR: getOp = JSOP_GETALIASEDVAR; break;
case JSOP_SETLOCAL:
getOp = JSOP_GETLOCAL;
setOp = JSOP_INITLEXICAL;
break;
case JSOP_SETALIASEDVAR:
getOp = JSOP_GETALIASEDVAR;
setOp = JSOP_INITALIASEDLEXICAL;
break;
default: MOZ_CRASH("Unexpected op");
}
@@ -4363,7 +4403,7 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption)
* i' to be hoisted out of the loop.
*/
MOZ_ASSERT(binding->isOp(JSOP_NOP));
MOZ_ASSERT(emitOption != DefineVars);
MOZ_ASSERT(emitOption != DefineVars && emitOption != AnnexB);
/*
* To allow the front end to rewrite var f = x; as f = x; when a
@@ -4424,13 +4464,18 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
op == JSOP_STRICTSETGNAME)
{
MOZ_ASSERT(emitOption != PushInitialValues);
JSOp bindOp;
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
bindOp = JSOP_BINDNAME;
else
bindOp = JSOP_BINDGNAME;
if (!emitIndex32(bindOp, atomIndex))
return false;
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;
}
}
bool oldEmittingForInit = emittingForInit;
@@ -4454,7 +4499,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) {
if (emitOption == InitializeVars || emitOption == AnnexB) {
MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr);
if (!binding->pn_scopecoord.isFree()) {
if (!emitVarOp(binding, op))
@@ -5292,6 +5337,28 @@ 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
@@ -5303,7 +5370,17 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn)
if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0))
return false;
if (!emitTree(pn->pn_expr))
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))
return false;
if (!leaveNestedScope(&stmtInfo))
@@ -5382,8 +5459,10 @@ BytecodeEmitter::emitIterator()
}
bool
BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl)
BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_LET));
// ES6 specifies that loop variables get a fresh binding in each iteration.
// This is currently implemented for C-style for(;;) loops, but not
// for-in/of loops, though a similar approach should work. See bug 449811.
@@ -5392,32 +5471,22 @@ BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn, bool* letDecl)
// scope containing an uninitialized `x`. If EXPR accesses `x`, we should
// get a ReferenceError due to the TDZ violation. This is not yet
// implemented. See bug 1069480.
*letDecl = pn->isKind(PNK_LEXICALSCOPE);
MOZ_ASSERT_IF(*letDecl, pn->isLexical());
// If the left part is 'var x', emit code to define x if necessary using a
// prologue opcode, but do not emit a pop. If it is 'let x', enterBlockScope
// will initialize let bindings in emitForOf and emitForIn with
// undefineds.
//
// Due to the horror of legacy comprehensions, there is a third case where
// we have PNK_LET without a lexical scope, because those expressions are
// parsed with single lexical scope for the entire comprehension. In this
// case we must initialize the lets to not trigger dead zone checks via
// InitializeVars.
if (!*letDecl) {
emittingForInit = true;
if (pn->isKind(PNK_VAR)) {
if (!emitVariables(pn, DefineVars))
return false;
} else {
MOZ_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_CONST));
if (!emitVariables(pn, InitializeVars))
return false;
}
emittingForInit = false;
// prologue opcode, but do not emit a pop. If it's 'let x', we initialize
// the lets to not trigger dead zone checks, via InitializeVars. (The
// frontend currently assumes use of a 'let', dominated by its declaration,
// needs no TDZ check, so we can't just fix the TDZ bug above by not
// initializing here.)
emittingForInit = true;
if (pn->isKind(PNK_VAR)) {
if (!emitVariables(pn, DefineVars))
return false;
} else {
MOZ_ASSERT(pn->isKind(PNK_LET) || pn->isKind(PNK_CONST));
if (!emitVariables(pn, InitializeVars))
return false;
}
emittingForInit = false;
return true;
}
@@ -5440,9 +5509,8 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn)
ParseNode* forHeadExpr = forHead ? forHead->pn_kid3 : nullptr;
ParseNode* forBody = pn ? pn->pn_right : nullptr;
ParseNode* pn1 = forHead ? forHead->pn_kid1 : nullptr;
bool letDecl = false;
if (pn1 && !emitForInOrOfVariables(pn1, &letDecl))
ParseNode* loopDecl = forHead ? forHead->pn_kid1 : nullptr;
if (loopDecl && !emitForInOrOfVariables(loopDecl))
return false;
if (type == StmtType::FOR_OF_LOOP) {
@@ -5460,15 +5528,6 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn)
return false;
}
// Enter the block before the loop body, after evaluating the obj.
// Initialize let bindings with undefined when entering, as the name
// assigned to is a plain assignment.
StmtInfoBCE letStmt(cx);
if (letDecl) {
if (!enterBlockScope(&letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
return false;
}
LoopStmtInfo stmtInfo(cx);
pushLoopStatement(&stmtInfo, type, top);
@@ -5566,11 +5625,6 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn)
if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top, offset()))
return false;
if (letDecl) {
if (!leaveNestedScope(&letStmt))
return false;
}
if (type == StmtType::SPREAD) {
if (!emit2(JSOP_PICK, 3)) // ARR I RESULT ITER
return false;
@@ -5587,9 +5641,8 @@ BytecodeEmitter::emitForIn(ParseNode* pn)
ParseNode* forHead = pn->pn_left;
ParseNode* forBody = pn->pn_right;
ParseNode* pn1 = forHead->pn_kid1;
bool letDecl = false;
if (pn1 && !emitForInOrOfVariables(pn1, &letDecl))
ParseNode* loopDecl = forHead->pn_kid1;
if (loopDecl && !emitForInOrOfVariables(loopDecl))
return false;
/* Compile the object expression to the right of 'in'. */
@@ -5610,15 +5663,6 @@ BytecodeEmitter::emitForIn(ParseNode* pn)
if (!emit1(JSOP_UNDEFINED))
return false;
// Enter the block before the loop body, after evaluating the obj.
// Initialize let bindings with undefined when entering, as the name
// assigned to is a plain assignment.
StmtInfoBCE letStmt(cx);
if (letDecl) {
if (!enterBlockScope(&letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0))
return false;
}
LoopStmtInfo stmtInfo(cx);
pushLoopStatement(&stmtInfo, StmtType::FOR_IN_LOOP, top);
@@ -5694,11 +5738,6 @@ BytecodeEmitter::emitForIn(ParseNode* pn)
if (!emit1(JSOP_ENDITER))
return false;
if (letDecl) {
if (!leaveNestedScope(&letStmt))
return false;
}
return true;
}
@@ -5881,6 +5920,8 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn)
bool
BytecodeEmitter::emitFor(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_FOR));
if (pn->pn_left->isKind(PNK_FORHEAD))
return emitCStyleFor(pn);
@@ -5894,9 +5935,323 @@ BytecodeEmitter::emitFor(ParseNode* pn)
return emitForOf(StmtType::FOR_OF_LOOP, pn);
}
bool
BytecodeEmitter::emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope)
{
// ES6 specifies that loop variables get a fresh binding in each iteration.
// This is currently implemented for C-style for(;;) loops, but not
// for-in/of loops, though a similar approach should work. See bug 449811.
//
// In `for (let x in/of EXPR)`, ES6 specifies that EXPR is evaluated in a
// scope containing an uninitialized `x`. If EXPR accesses `x`, we should
// get a ReferenceError due to the TDZ violation. This is not yet
// implemented. See bug 1069480.
*letBlockScope = pn->isKind(PNK_LEXICALSCOPE);
MOZ_ASSERT_IF(*letBlockScope, pn->isLexical());
// If the left part is 'var x', emit code to define x if necessary using a
// prologue opcode, but do not emit a pop. If it is 'let x', enterBlockScope
// will initialize let bindings in emitForOf and emitForIn with
// undefineds.
//
// Due to the horror of legacy comprehensions, there is a third case where
// we have PNK_LET without a lexical scope, because those expressions are
// parsed with single lexical scope for the entire comprehension. In this
// case we must initialize the lets to not trigger dead zone checks via
// InitializeVars.
if (!*letBlockScope) {
emittingForInit = true;
if (pn->isKind(PNK_VAR)) {
if (!emitVariables(pn, DefineVars))
return false;
} else {
MOZ_ASSERT(pn->isKind(PNK_LET));
if (!emitVariables(pn, InitializeVars))
return false;
}
emittingForInit = false;
}
return true;
}
bool
BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR));
ParseNode* forHead = pn->pn_left;
MOZ_ASSERT(forHead->isKind(PNK_FOROF));
ParseNode* forHeadExpr = forHead->pn_kid3;
ParseNode* forBody = pn->pn_right;
ptrdiff_t top = offset();
ParseNode* loopDecl = forHead->pn_kid1;
bool letBlockScope = false;
if (loopDecl && !emitComprehensionForInOrOfVariables(loopDecl, &letBlockScope))
return false;
// For-of loops run with two values on the stack: the iterator and the
// current result object.
// Compile the object expression to the right of 'of'.
if (!emitTree(forHeadExpr)) // EXPR
return false;
if (!emitIterator()) // ITER
return false;
// Push a dummy result so that we properly enter iteration midstream.
if (!emit1(JSOP_UNDEFINED)) // ITER RESULT
return false;
// Enter the block before the loop body, after evaluating the obj.
// Initialize let bindings with undefined when entering, as the name
// assigned to is a plain assignment.
StmtInfoBCE letStmt(cx);
if (letBlockScope) {
if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0))
return false;
}
LoopStmtInfo stmtInfo(cx);
pushLoopStatement(&stmtInfo, StmtType::FOR_OF_LOOP, top);
// Jump down to the loop condition to minimize overhead assuming at least
// one iteration, as the other loop forms do. Annotate so IonMonkey can
// find the loop-closing jump.
unsigned noteIndex;
if (!newSrcNote(SRC_FOR_OF, &noteIndex))
return false;
ptrdiff_t jmp;
if (!emitJump(JSOP_GOTO, 0, &jmp))
return false;
top = offset();
stmtInfo.setTop(top);
if (!emitLoopHead(nullptr))
return false;
#ifdef DEBUG
int loopDepth = this->stackDepth;
#endif
// Emit code to assign result.value to the iteration variable.
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER RESULT VALUE
return false;
if (!emitAssignment(forHead->pn_kid2, JSOP_NOP, nullptr)) // ITER RESULT VALUE
return false;
if (!emit1(JSOP_POP)) // ITER RESULT
return false;
// The stack should be balanced around the assignment opcode sequence.
MOZ_ASSERT(this->stackDepth == loopDepth);
// Emit code for the loop body.
if (!emitTree(forBody))
return false;
// Set loop and enclosing "update" offsets, for continue.
StmtInfoBCE* stmt = &stmtInfo;
do {
stmt->update = offset();
} while ((stmt = stmt->enclosing) != nullptr && stmt->type == StmtType::LABEL);
// COME FROM the beginning of the loop to here.
setJumpOffsetAt(jmp);
if (!emitLoopEntry(forHeadExpr))
return false;
if (!emit1(JSOP_POP)) // ITER
return false;
if (!emit1(JSOP_DUP)) // ITER ITER
return false;
if (!emitIteratorNext(forHead)) // ITER RESULT
return false;
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE?
return false;
ptrdiff_t beq;
if (!emitJump(JSOP_IFEQ, top - offset(), &beq)) // ITER RESULT
return false;
MOZ_ASSERT(this->stackDepth == loopDepth);
// Let Ion know where the closing jump of this loop is.
if (!setSrcNoteOffset(noteIndex, 0, beq - jmp))
return false;
// Fixup breaks and continues.
popStatement();
if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top, offset()))
return false;
if (letBlockScope) {
if (!leaveNestedScope(&letStmt))
return false;
}
// Pop the result and the iter.
return emitUint16Operand(JSOP_POPN, 2); //
}
bool
BytecodeEmitter::emitComprehensionForIn(ParseNode* pn)
{
MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR));
ptrdiff_t top = offset();
ParseNode* forHead = pn->pn_left;
MOZ_ASSERT(forHead->isKind(PNK_FORIN));
ParseNode* forBody = pn->pn_right;
ParseNode* loopDecl = forHead->pn_kid1;
bool letBlockScope = false;
if (loopDecl && !emitComprehensionForInOrOfVariables(loopDecl, &letBlockScope))
return false;
/* Compile the object expression to the right of 'in'. */
if (!emitTree(forHead->pn_kid3))
return false;
/*
* Emit a bytecode to convert top of stack value to the iterator
* object depending on the loop variant (for-in, for-each-in, or
* destructuring for-in).
*/
MOZ_ASSERT(pn->isOp(JSOP_ITER));
if (!emit2(JSOP_ITER, (uint8_t) pn->pn_iflags))
return false;
// For-in loops have both the iterator and the value on the stack. Push
// undefined to balance the stack.
if (!emit1(JSOP_UNDEFINED))
return false;
// Enter the block before the loop body, after evaluating the obj.
// Initialize let bindings with undefined when entering, as the name
// assigned to is a plain assignment.
StmtInfoBCE letStmt(cx);
if (letBlockScope) {
if (!enterBlockScope(&letStmt, loopDecl->pn_objbox, JSOP_UNDEFINED, 0))
return false;
}
LoopStmtInfo stmtInfo(cx);
pushLoopStatement(&stmtInfo, StmtType::FOR_IN_LOOP, top);
/* Annotate so IonMonkey can find the loop-closing jump. */
unsigned noteIndex;
if (!newSrcNote(SRC_FOR_IN, &noteIndex))
return false;
/*
* Jump down to the loop condition to minimize overhead assuming at
* least one iteration, as the other loop forms do.
*/
ptrdiff_t jmp;
if (!emitJump(JSOP_GOTO, 0, &jmp))
return false;
top = offset();
stmtInfo.setTop(top);
if (!emitLoopHead(nullptr))
return false;
#ifdef DEBUG
int loopDepth = this->stackDepth;
#endif
// Emit code to assign the enumeration value to the left hand side, but
// also leave it on the stack.
if (!emitAssignment(forHead->pn_kid2, JSOP_NOP, nullptr))
return false;
/* The stack should be balanced around the assignment opcode sequence. */
MOZ_ASSERT(this->stackDepth == loopDepth);
/* Emit code for the loop body. */
if (!emitTree(forBody))
return false;
/* Set loop and enclosing "update" offsets, for continue. */
StmtInfoBCE* stmt = &stmtInfo;
do {
stmt->update = offset();
} while ((stmt = stmt->enclosing) != nullptr && stmt->type == StmtType::LABEL);
/*
* Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
*/
setJumpOffsetAt(jmp);
if (!emitLoopEntry(nullptr))
return false;
if (!emit1(JSOP_POP))
return false;
if (!emit1(JSOP_MOREITER))
return false;
if (!emit1(JSOP_ISNOITER))
return false;
ptrdiff_t beq;
if (!emitJump(JSOP_IFEQ, top - offset(), &beq))
return false;
/* Set the srcnote offset so we can find the closing jump. */
if (!setSrcNoteOffset(noteIndex, 0, beq - jmp))
return false;
// Fix up breaks and continues.
popStatement();
// Pop the enumeration value.
if (!emit1(JSOP_POP))
return false;
if (!tryNoteList.append(JSTRY_FOR_IN, this->stackDepth, top, offset()))
return false;
if (!emit1(JSOP_ENDITER))
return false;
if (letBlockScope) {
if (!leaveNestedScope(&letStmt))
return false;
}
return true;
}
bool
BytecodeEmitter::emitComprehensionFor(ParseNode* compFor)
{
MOZ_ASSERT(compFor->pn_left->isKind(PNK_FORIN) ||
compFor->pn_left->isKind(PNK_FOROF));
if (!updateLineNumberNotes(compFor->pn_pos.begin))
return false;
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());
@@ -5907,9 +6262,25 @@ 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;
}
@@ -6017,10 +6388,28 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
* For modules, we record the function and instantiate the binding during
* ModuleDeclarationInstantiation(), before the script is run.
*/
if (sc->isGlobalContext()) {
// 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()) {
MOZ_ASSERT(pn->pn_scopecoord.isFree());
MOZ_ASSERT(pn->getOp() == JSOP_NOP);
MOZ_ASSERT(atBodyLevel());
switchToPrologue();
if (!emitIndex32(JSOP_DEFFUN, index))
return false;
@@ -7657,7 +8046,6 @@ 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;
@@ -7718,21 +8106,11 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn)
}
}
if (pnlast->pn_xflags & PNX_FUNCDEFS) {
// 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;
}
}
// 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;
}
return emitTree(pnlast);
}
@@ -7951,6 +8329,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
switch (pn->getKind()) {
case PNK_FUNCTION:
case PNK_ANNEXB_FUNCTION:
if (!emitFunction(pn))
return false;
break;
@@ -7981,11 +8360,15 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
break;
case PNK_FOR:
case PNK_COMPREHENSIONFOR:
if (!emitFor(pn))
return false;
break;
case PNK_COMPREHENSIONFOR:
if (!emitComprehensionFor(pn))
return false;
break;
case PNK_BREAK:
if (!emitBreak(pn->as<BreakStatement>().label()))
return false;
+19 -5
View File
@@ -121,7 +121,12 @@ 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
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,
};
struct BytecodeEmitter
@@ -249,7 +254,10 @@ struct BytecodeEmitter
return parser->blockScopes[dn->pn_blockid];
}
bool atBodyLevel() const;
bool atBodyLevel(StmtInfoBCE* stmt) const;
bool atBodyLevel() const {
return atBodyLevel(innermostStmt());
}
uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op);
@@ -464,6 +472,8 @@ 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
@@ -601,10 +611,15 @@ struct BytecodeEmitter
bool emitSelfHostedResumeGenerator(ParseNode* pn);
bool emitSelfHostedForceInterpreter(ParseNode* pn);
bool emitComprehensionFor(ParseNode* compFor);
bool emitComprehensionForIn(ParseNode* pn);
bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* letBlockScope);
bool emitComprehensionForOf(ParseNode* pn);
bool emitDo(ParseNode* pn);
bool emitFor(ParseNode* pn);
bool emitForIn(ParseNode* pn);
bool emitForInOrOfVariables(ParseNode* pn, bool* letDecl);
bool emitForInOrOfVariables(ParseNode* pn);
bool emitCStyleFor(ParseNode* pn);
bool emitWhile(ParseNode* pn);
@@ -627,8 +642,7 @@ struct BytecodeEmitter
bool emitSpread();
// If type is StmtType::FOR_OF_LOOP, emit bytecode for a for-of loop.
// pn should be PNK_FOR or PNK_COMPREHENSIONFOR, and pn->pn_left should be
// PNK_FOROF.
// pn should be PNK_FOR, and pn->pn_left should be PNK_FOROF.
//
// If type is StmtType::SPREAD, emit bytecode for spread operator.
// pn should be nullptr.
+8
View File
@@ -100,6 +100,11 @@ 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;
@@ -1783,6 +1788,9 @@ 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);
+36 -15
View File
@@ -444,24 +444,40 @@ 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));
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());
}
}
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;
}
}
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;
}
bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
@@ -656,6 +672,11 @@ 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);
}
@@ -727,7 +748,7 @@ class FullParseHandler
return node->isKind(PNK_SUPERBASE);
}
inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op);
inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init);
inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op);
void setBeginPosition(ParseNode* pn, ParseNode* oth) {
@@ -990,7 +1011,7 @@ FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseN
}
inline bool
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op)
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
{
if (pn->isUsed()) {
pn = makeAssignment(pn, init);
+6
View File
@@ -791,6 +791,12 @@ 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
+2 -1
View File
@@ -283,7 +283,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_NEWTARGET:
case PNK_SETTHIS:
case PNK_FOR:
case PNK_COMPREHENSIONFOR: {
case PNK_COMPREHENSIONFOR:
case PNK_ANNEXB_FUNCTION: {
MOZ_ASSERT(pn->isArity(PN_BINARY));
stack->push(pn->pn_left);
stack->push(pn->pn_right);
+10 -1
View File
@@ -165,6 +165,7 @@ class PackedScopeCoordinate
F(FORIN) \
F(FOROF) \
F(FORHEAD) \
F(ANNEXB_FUNCTION) \
F(ARGSBODY) \
F(SPREAD) \
F(MUTATEPROTO) \
@@ -271,6 +272,9 @@ 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
@@ -780,7 +784,8 @@ 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_GETARG) || // body-level function redeclaring formal
isOp(JSOP_INITLEXICAL)); // block-level function stmt
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
}
@@ -1607,6 +1612,8 @@ 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);
@@ -1615,6 +1622,8 @@ 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);
+383 -207
View File
@@ -104,9 +104,7 @@ ParseContext<FullParseHandler>::checkLocalsOverflow(TokenStream& ts)
static void
MarkUsesAsHoistedLexical(ParseNode* pn)
{
MOZ_ASSERT(pn->isDefn());
Definition* dn = (Definition*)pn;
Definition* dn = &pn->as<Definition>();
ParseNode** pnup = &dn->dn_uses;
ParseNode* pnu;
unsigned start = pn->pn_blockid;
@@ -128,8 +126,12 @@ SharedContext::computeAllowSyntax(JSObject* staticScope)
// Any function supports new.target.
allowNewTarget_ = true;
allowSuperProperty_ = it.fun().allowSuperProperty();
if (it.maybeFunctionBox())
if (it.maybeFunctionBox()) {
superScopeAlreadyNeedsHomeObject_ = it.maybeFunctionBox()->needsHomeObject();
allowSuperCall_ = it.maybeFunctionBox()->isDerivedClassConstructor();
} else {
allowSuperCall_ = it.fun().isDerivedClassConstructor();
}
break;
}
}
@@ -219,6 +221,8 @@ 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);
@@ -237,7 +241,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 = (Definition*) pn;
pnu->pn_lexdef = &pn->as<Definition>();
pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
pnup = &pnu->pn_link;
}
@@ -255,12 +259,11 @@ 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 = (Definition*)pn;
Definition* dn = &pn->as<Definition>();
switch (kind) {
case Definition::ARG:
MOZ_ASSERT(sc->isFunctionBox());
@@ -390,7 +393,7 @@ ParseContext<ParseHandler>::updateDecl(TokenStream& ts, JSAtom* atom, Node pn)
Definition* oldDecl = decls_.lookupFirst(atom);
pn->setDefn(true);
Definition* newDecl = (Definition*)pn;
Definition* newDecl = &pn->template as<Definition>();
decls_.updateFirst(atom, newDecl);
if (sc->isGlobalContext() || oldDecl->isDeoptimized()) {
@@ -1199,18 +1202,6 @@ 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;
@@ -1405,7 +1396,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 = (Definition*) pn;
pnu->pn_lexdef = &pn->as<Definition>();
pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
}
pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
@@ -1448,7 +1439,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
return false;
pn->dn_uses = lhs;
dn->pn_link = nullptr;
dn = (Definition*) lhs;
dn = &lhs->as<Definition>();
}
}
@@ -1459,7 +1450,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 = (Definition*) pn;
dn->pn_lexdef = &pn->as<Definition>();
dn->pn_scopecoord.makeFree();
dn->pn_dflags &= ~PND_BOUND;
return true;
@@ -1491,18 +1482,22 @@ struct BindData
void initLexical(VarContext varContext, JSOp op, StaticBlockObject* blockObj,
unsigned overflow)
{
init(LexicalBinding, op, op == JSOP_DEFCONST);
init(LexicalBinding, op, op == JSOP_DEFCONST, false);
letData_.varContext = varContext;
letData_.blockObj = blockObj;
letData_.overflow = overflow;
}
void initVar(JSOp op) {
init(VarBinding, op, false);
init(VarBinding, op, false, false);
}
void initAnnexBVar() {
init(VarBinding, JSOP_DEFVAR, false, true);
}
void initDestructuring(JSOp op) {
init(DestructuringBinding, op, false);
init(DestructuringBinding, op, false, false);
}
void setNameNode(typename ParseHandler::Node pn) {
@@ -1525,6 +1520,11 @@ struct BindData
return isConst_;
}
bool isAnnexB() {
MOZ_ASSERT(isInitialized());
return isAnnexB_;
}
const LetData& letData() {
MOZ_ASSERT(kind_ == LexicalBinding);
return letData_;
@@ -1561,17 +1561,19 @@ 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) {
void init(BindingKind kind, JSOp op, bool isConst, bool isAnnexB) {
MOZ_ASSERT(!isInitialized());
kind_ = kind;
op_ = op;
isConst_ = isConst;
isAnnexB_ = isAnnexB;
}
};
@@ -2249,138 +2251,215 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
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)
bool* pbodyProcessed,
ParseNode** assignmentForAnnexBOut)
{
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).
*/
if (Definition* dn = pc->decls().lookupFirst(funName)) {
MOZ_ASSERT(!dn->isUsed());
MOZ_ASSERT(dn->isDefn());
MOZ_ASSERT(assignmentForAnnexBOut);
*assignmentForAnnexBOut = nullptr;
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()))
{
// 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;
}
}
/*
* 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 {
if (!makeDefIntoUse(dn, pn, funName))
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);
}
} 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 (bodyLevel) {
MOZ_ASSERT(pn->functionIsHoisted());
MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree());
if (bodyLevelFunction) {
if (!bindBodyLevelFunctionName(funName, pn_))
return false;
} 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);
Definition* annexDef = nullptr;
Node synthesizedDeclarationList = null();
/*
* 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 (!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 (!pc->funcStmts->put(funName))
if (!bindLexicalFunctionName(funName, pn))
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();
}
if (annexDef) {
MOZ_ASSERT(!pc->sc->strict());
/* No further binding (in BindNameToSlot) is needed for functions. */
pn->pn_dflags |= PND_BOUND;
// 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))
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;
}
}
}
} else {
/* A function expression does not introduce any binding. */
pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
@@ -2489,7 +2568,8 @@ template <>
bool
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
Node* pn, FunctionSyntaxKind kind,
bool* pbodyProcessed)
bool* pbodyProcessed,
Node* assignmentForAnnexBOut)
{
*pbodyProcessed = false;
@@ -2497,10 +2577,18 @@ 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;
@@ -2511,16 +2599,13 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
return false;
}
}
} else if (bodyLevel) {
} else {
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) {
@@ -2601,7 +2686,8 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHandling,
HandlePropertyName funName, FunctionSyntaxKind kind,
GeneratorKind generatorKind, InvokedPrediction invoked)
GeneratorKind generatorKind, InvokedPrediction invoked,
Node* assignmentForAnnexBOut)
{
MOZ_ASSERT_IF(kind == Statement, funName);
@@ -2609,12 +2695,13 @@ 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))
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed, assignmentForAnnexBOut))
return null();
if (bodyProcessed)
@@ -2801,7 +2888,6 @@ 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);
@@ -2819,8 +2905,6 @@ 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
@@ -3061,6 +3145,24 @@ 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;
@@ -3088,12 +3190,35 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
return null();
}
/* We forbid function statements in strict mode code. */
if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
!report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT))
Node assignmentForAnnexB;
Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind,
PredictUninvoked, &assignmentForAnnexB);
if (!fun)
return null();
return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind);
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;
}
template <typename ParseHandler>
@@ -3697,19 +3822,24 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
StmtInfoPC* stmt = LexicalLookup(pc, name);
if (stmt && stmt->type == StmtType::WITH) {
parser->handler.setFlag(pn, PND_DEOPTIMIZED);
if (pc->sc->isFunctionBox()) {
FunctionBox* funbox = pc->sc->asFunctionBox();
funbox->setMightAliasLocals();
}
// 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();
// 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.
@@ -3844,23 +3974,27 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
template <>
bool
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn)
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, HandlePropertyName name,
ParseNode* 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;
return data->bind(name, this);
}
template <>
bool
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn)
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn)
{
if (!bindUninitialized(data, 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))
return false;
/*
@@ -3881,6 +4015,14 @@ Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, Pars
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)
@@ -4429,7 +4571,7 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
if (!bindBeforeInitializer && !data.bind(name, this))
return null();
if (!handler.finishInitializerAssignment(pn2, init, data.op()))
if (!handler.finishInitializerAssignment(pn2, init))
return null();
}
}
@@ -4450,28 +4592,45 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
template <>
bool
Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos)
Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat,
const TokenPos& errorPos)
{
/*
* 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.
* 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.
*
* 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.
*/
StmtInfoPC* stmt = pc->innermostStmt();
// 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();
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
isConst ? "const" : "lexical");
reportWithOffset(ParseError, false, errorPos.begin,
stmt->type == StmtType::LABEL
? JSMSG_LEXICAL_DECL_LABEL
: JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
prepareWhat == PrepareConst ? "const" : "lexical");
return false;
}
if (!stmt) {
MOZ_ASSERT(pc->atBodyLevel());
MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel());
/*
* Self-hosted code must be usable against *any* global object,
@@ -4482,7 +4641,7 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt();
if (options().selfHostingMode && isGlobal) {
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
isConst ? "'const'" : "'let'");
prepareWhat == PrepareConst ? "'const'" : "'let'");
return false;
}
return true;
@@ -4508,8 +4667,12 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
* catch block (catch is a lexical scope by definition).
*/
MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH);
pc->stmtStack.makeInnermostLexicalScope(*blockObj);
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);
@@ -4539,33 +4702,54 @@ CurrentLexicalStaticBlock(ParseContext<FullParseHandler>* pc)
}
template <>
ParseNode*
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst,
const TokenPos& pos)
bool
Parser<FullParseHandler>::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
PrepareLexicalKind prepareWhat,
ParseNode* pn,
const TokenPos& pos)
{
BindData<FullParseHandler> data(context);
if (!checkAndPrepareLexical(isConst, pos))
return null();
data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET,
if (!checkAndPrepareLexical(prepareWhat, pos))
return false;
data.initLexical(HoistVars, prepareWhat == PrepareConst ? 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 (!bindInitialized(&data, dn))
if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos))
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, pos()))
if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos()))
return null();
/*
@@ -5077,7 +5261,7 @@ Parser<FullParseHandler>::exportDeclaration()
default:
tokenStream.ungetToken();
RootedPropertyName name(context, context->names().starDefaultStar);
binding = makeInitializedLexicalBinding(name, true, pos());
binding = makeInitializedLexicalBinding(name, PrepareConst, pos());
if (!binding)
return null();
kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
@@ -5387,9 +5571,6 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
if (parseDecl) {
bool constDecl = tt == TOK_CONST;
isForDecl = true;
blockObj = StaticBlockObject::create(context);
if (!blockObj)
return null();
// Initialize the enclosing scope manually for the call to
// |variables| below.
@@ -5827,7 +6008,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
afterReturn = true;
}
}
handler.addList(body, stmt);
handler.addStatementToList(body, stmt, pc);
}
// In ES6, lexical bindings cannot be accessed until initialized. If
@@ -5846,7 +6027,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
if (!casepn)
return null();
handler.addList(caseList, casepn);
handler.addCaseStatementToList(caseList, casepn, pc);
}
/*
@@ -6672,7 +6853,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
ParseNode* nameNode = null();
ParseNode* methodsOrBlock = classMethods;
if (name) {
ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos);
ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos);
if (!innerBinding)
return null();
@@ -6683,7 +6864,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
ParseNode* outerBinding = null();
if (classContext == ClassStatement) {
outerBinding = makeInitializedLexicalBinding(name, false, namePos);
outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos);
if (!outerBinding)
return null();
}
@@ -7304,11 +7485,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
return null();
if (pc->sc->isFunctionBox() && pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
report(ParseError, false, null(), JSMSG_DISABLED_DERIVED_CLASS, "arrow functions");
return null();
}
Node arrowFunc = functionDef(inHandling, yieldHandling, nullptr, Arrow, NotGenerator);
if (!arrowFunc)
return null();
@@ -8716,7 +8892,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
tt == TOK_NO_SUBS_TEMPLATE)
{
if (handler.isSuperBase(lhs)) {
if (!pc->sc->isFunctionBox() || !pc->sc->asFunctionBox()->isDerivedClassConstructor()) {
if (!pc->sc->allowSuperCall()) {
report(ParseError, false, null(), JSMSG_BAD_SUPERCALL);
return null();
}
+30 -17
View File
@@ -53,7 +53,6 @@ struct StmtInfoPC : public StmtInfoBase
{}
};
typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> FuncStmtSet;
class SharedContext;
typedef Vector<Definition*, 16> DeclVector;
@@ -236,10 +235,6 @@ 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;
@@ -277,7 +272,6 @@ 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)
@@ -295,6 +289,7 @@ 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;
@@ -308,19 +303,23 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel() {
bool atBodyLevel(StmtInfoPC* stmt) {
// '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 = !innermostStmt()->enclosing;
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
->template as<StaticBlockObject>()
.enclosingStaticScope() == sc->staticScope());
bool bl = !stmt->enclosing;
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
MOZ_ASSERT_IF(bl, stmt->staticScope
->template as<StaticBlockObject>()
.enclosingStaticScope() == sc->staticScope());
return bl;
}
return !innermostStmt();
return !stmt;
}
bool atBodyLevel() {
return atBodyLevel(innermostStmt());
}
bool atGlobalLevel() {
@@ -728,7 +727,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
InvokedPrediction invoked = PredictUninvoked);
InvokedPrediction invoked = PredictUninvoked,
Node* assignmentForAnnexBOut = nullptr);
bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
FunctionSyntaxKind kind, GeneratorKind generatorKind,
Directives inheritedDirectives, Directives* newDirectives);
@@ -794,8 +794,10 @@ 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);
bool* pbodyProcessed, Node* assignmentForAnnexBOut);
bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
@@ -844,8 +846,17 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node objectLiteral(YieldHandling yieldHandling);
bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos);
Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos);
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);
Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
@@ -860,7 +871,9 @@ 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);
+9
View File
@@ -214,6 +214,7 @@ class SharedContext
bool allowNewTarget_;
bool allowSuperProperty_;
bool allowSuperCall_;
bool inWith_;
bool needsThisTDZChecks_;
bool superScopeAlreadyNeedsHomeObject_;
@@ -229,6 +230,7 @@ class SharedContext
thisBinding_(ThisBinding::Global),
allowNewTarget_(false),
allowSuperProperty_(false),
allowSuperCall_(false),
inWith_(false),
needsThisTDZChecks_(false),
superScopeAlreadyNeedsHomeObject_(false)
@@ -256,6 +258,7 @@ class SharedContext
bool allowNewTarget() const { return allowNewTarget_; }
bool allowSuperProperty() const { return allowSuperProperty_; }
bool allowSuperCall() const { return allowSuperCall_; }
bool inWith() const { return inWith_; }
bool needsThisTDZChecks() const { return needsThisTDZChecks_; }
@@ -596,6 +599,12 @@ 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;
+3 -1
View File
@@ -282,6 +282,7 @@ 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; }
@@ -325,6 +326,7 @@ 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) {
@@ -346,7 +348,7 @@ class SyntaxParseHandler
return NodeGeneric;
}
bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
bool finishInitializerAssignment(Node pn, Node init) { return true; }
void setLexicalDeclarationOp(Node pn, JSOp op) {}
void setBeginPosition(Node pn, Node oth) {}
+4
View File
@@ -197,6 +197,8 @@ class BaseShape;
class DebugScopeObject;
class GlobalObject;
class LazyScript;
class ModuleEnvironmentObject;
class ModuleNamespaceObject;
class NativeObject;
class NestedScopeObject;
class PlainObject;
@@ -909,6 +911,8 @@ typedef HeapPtr<JSLinearString*> HeapPtrLinearString;
typedef HeapPtr<JSObject*> HeapPtrObject;
typedef HeapPtr<JSScript*> HeapPtrScript;
typedef HeapPtr<JSString*> HeapPtrString;
typedef HeapPtr<ModuleEnvironmentObject*> HeapPtrModuleEnvironmentObject;
typedef HeapPtr<ModuleNamespaceObject*> HeapPtrModuleNamespaceObject;
typedef HeapPtr<PlainObject*> HeapPtrPlainObject;
typedef HeapPtr<PropertyName*> HeapPtrPropertyName;
typedef HeapPtr<Shape*> HeapPtrShape;
@@ -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);
@@ -1,18 +0,0 @@
// |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,2 +1,3 @@
for (var x in x)
for (var x in x) {
function x() {}
}
@@ -32,5 +32,5 @@ dbg.memory.trackingAllocationSites = true;
// probability is fine.
measure(0.0, 0);
measure(1.0, 100);
measure(0.1, 7);
measure(0.5, 44);
measure(0.1, 11);
measure(0.5, 49);
+7
View File
@@ -0,0 +1,7 @@
if (!('oomTest' in this))
quit();
oomTest(() => getBacktrace({
locals: true,
thisprops: true
}));
+5 -1
View File
@@ -8,7 +8,11 @@ try {
}
}(), function() {}))
} catch (e) {};
var log = "";
evaluate(`
try {
function x() {}
assertEq(String(b), "function () {}");
} catch (e) { throw (e); }
} catch (e) { log += "e"; }
`);
assertEq(log, "e");
+17
View File
@@ -0,0 +1,17 @@
function f() {
var hits = 0;
for (var T of [Float32Array, Float64Array, Float32Array]) {
var arr = new T(1);
try {
arr[0] = Symbol.iterator;
} catch(e) { hits++; }
}
for (var T of [Int32Array, Int16Array, Int8Array]) {
var arr = new T(1);
try {
arr[0] = Symbol.iterator;
} catch(e) { hits++; }
}
assertEq(hits, 6);
}
f();
+11
View File
@@ -0,0 +1,11 @@
// |jit-test| error: InternalError
x = 1;
x;
function g(y) {}
g(this);
x = /x/;
function f() {
f(x.flags);
}
f();
@@ -0,0 +1,2 @@
// |jit-test| error: Error: expected filename string, got number
parseModule("", 3);
+3
View File
@@ -0,0 +1,3 @@
// |jit-test| module; error:ReferenceError
foo = 1;
let foo;
@@ -0,0 +1,6 @@
// setSavedStacksRNGState shouldn't crash regardless of the seed value passed.
setSavedStacksRNGState(0);
setSavedStacksRNGState({});
setSavedStacksRNGState(false);
setSavedStacksRNGState(NaN);
+2
View File
@@ -706,6 +706,8 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
{
scopeChain = fun->environment();
}
} else if (script->module()) {
scopeChain = script->module()->environment();
} else {
// For global scripts without a non-syntactic scope the scope
// chain is the script's global lexical scope (Ion does not
+20
View File
@@ -2259,6 +2259,26 @@ 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()
{
+1
View File
@@ -141,6 +141,7 @@ namespace jit {
_(JSOP_DELNAME) \
_(JSOP_GETIMPORT) \
_(JSOP_GETINTRINSIC) \
_(JSOP_BINDVAR) \
_(JSOP_DEFVAR) \
_(JSOP_DEFCONST) \
_(JSOP_DEFLET) \
+1
View File
@@ -754,6 +754,7 @@ BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
case ICStub::GetProp_CallDOMProxyNative:
case ICStub::GetProp_CallDOMProxyWithGenerationNative:
case ICStub::GetProp_DOMProxyShadowed:
case ICStub::GetProp_ModuleNamespace:
case ICStub::GetElem_NativeSlotName:
case ICStub::GetElem_NativeSlotSymbol:
case ICStub::GetElem_NativePrototypeSlotName:
+1
View File
@@ -158,6 +158,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
case JSOP_GETNAME:
case JSOP_BINDNAME:
case JSOP_BINDVAR:
case JSOP_SETNAME:
case JSOP_STRICTSETNAME:
case JSOP_DELNAME:
+10
View File
@@ -8428,6 +8428,16 @@ 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);
+1
View File
@@ -329,6 +329,7 @@ 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);
+17 -1
View File
@@ -409,7 +409,23 @@ class ExecutableAllocator
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
static void cacheFlush(void* code, size_t size)
{
#if defined(__GNUC__)
#if defined(_MIPS_ARCH_LOONGSON3A)
// On Loongson3-CPUs, The cache flushed automatically
// by hardware. Just need to execute an instruction hazard.
uintptr_t tmp;
asm volatile (
".set push \n"
".set noreorder \n"
"move %[tmp], $ra \n"
"bal 1f \n"
"daddiu $ra, 8 \n"
"1: \n"
"jr.hb $ra \n"
"move $ra, %[tmp] \n"
".set pop\n"
:[tmp]"=&r"(tmp)
);
#elif defined(__GNUC__)
intptr_t end = reinterpret_cast<intptr_t>(code) + size;
__builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
#else
+2
View File
@@ -1263,6 +1263,8 @@ void
IonScript::Destroy(FreeOp* fop, IonScript* script)
{
script->unlinkFromRuntime(fop);
// Frees the potential event we have set.
script->traceLoggerScriptEvent_ = TraceLoggerEvent();
fop->free_(script);
}
+30
View File
@@ -1950,6 +1950,9 @@ 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;
@@ -2002,6 +2005,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_FUNCTIONTHIS:
return jsop_functionthis();
case JSOP_GLOBALTHIS:
return jsop_globalthis();
case JSOP_CALLEE: {
MDefinition* callee = getCallee();
current->push(callee);
@@ -8422,6 +8428,16 @@ 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)
{
@@ -12975,6 +12991,20 @@ IonBuilder::jsop_functionthis()
return resumeAfter(thisObj);
}
bool
IonBuilder::jsop_globalthis()
{
if (script()->hasNonSyntacticScope()) {
// Ion does not compile global scripts with a non-syntactic scope, but
// we can end up here when we're compiling an arrow function.
return abort("JSOP_GLOBALTHIS in script with non-syntactic scope");
}
ClonedBlockObject* globalLexical = &script()->global().lexicalScope();
pushConstant(globalLexical->thisValue());
return true;
}
bool
IonBuilder::jsop_typeof()
{
+2
View File
@@ -689,6 +689,7 @@ 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);
@@ -723,6 +724,7 @@ class IonBuilder
bool jsop_lambda(JSFunction* fun);
bool jsop_lambda_arrow(JSFunction* fun);
bool jsop_functionthis();
bool jsop_globalthis();
bool jsop_typeof();
bool jsop_toid();
bool jsop_iter(uint8_t flags);
+92 -2
View File
@@ -2036,6 +2036,93 @@ GetPropertyIC::tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript,
JS::TrackedOutcome::ICGetPropStub_ArgumentsLength);
}
static void
GenerateReadModuleNamespace(JSContext* cx, IonScript* ion, MacroAssembler& masm,
IonCache::StubAttacher& attacher, ModuleNamespaceObject* ns,
ModuleEnvironmentObject* env, Shape* shape, Register object,
TypedOrValueRegister output, Label* failures)
{
MOZ_ASSERT(ns);
MOZ_ASSERT(env);
// If we have multiple failure jumps but didn't get a label from the
// outside, make one ourselves.
Label failures_;
if (!failures)
failures = &failures_;
// Check for the specific namespace object.
attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, object, ImmGCPtr(ns), failures);
// If we need a scratch register, use either an output register or the
// object register.
bool restoreScratch = false;
Register scratchReg = InvalidReg; // Quell compiler warning.
if (output.hasValue()) {
scratchReg = output.valueReg().scratchReg();
} else if (output.type() == MIRType_Double) {
masm.push(object);
scratchReg = object;
restoreScratch = true;
} else {
scratchReg = output.typedReg().gpr();
}
// Slot access.
Register envReg = scratchReg;
masm.movePtr(ImmGCPtr(env), envReg);
EmitLoadSlot(masm, &env->as<NativeObject>(), shape, envReg, output, scratchReg);
// Restore scratch on success.
if (restoreScratch)
masm.pop(object);
attacher.jumpRejoin(masm);
masm.bind(failures);
attacher.jumpNextStub(masm);
}
bool
GetPropertyIC::tryAttachModuleNamespace(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, void* returnAddr,
bool* emitted)
{
MOZ_ASSERT(canAttachStub());
MOZ_ASSERT(!*emitted);
MOZ_ASSERT(outerScript->ionScript() == ion);
if (!obj->is<ModuleNamespaceObject>())
return true;
Rooted<ModuleNamespaceObject*> ns(cx, &obj->as<ModuleNamespaceObject>());
RootedModuleEnvironmentObject env(cx);
RootedShape shape(cx);
if (!ns->bindings().lookup(id, env.address(), shape.address()))
return true;
// Don't emit a stub until the target binding has been initialized.
if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
return true;
*emitted = true;
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
StubAttacher attacher(*this);
Label failures;
emitIdGuard(masm, id, &failures);
Label* maybeFailures = failures.used() ? &failures : nullptr;
GenerateReadModuleNamespace(cx, ion, masm, attacher, ns, env,
shape, object(), output(), maybeFailures);
return linkAndAttachStub(cx, masm, attacher, ion, "module namespace",
JS::TrackedOutcome::ICGetPropStub_ReadSlot);
}
static bool
ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id, bool* nameOrSymbol)
{
@@ -2082,6 +2169,9 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript*
void* returnAddr = GetReturnAddressToIonCode(cx);
if (!*emitted && !tryAttachModuleNamespace(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
if (!*emitted && !tryAttachProxy(cx, outerScript, ion, obj, id, returnAddr, emitted))
return false;
@@ -4239,9 +4329,9 @@ IsDenseElementSetInlineable(JSObject* obj, const Value& idval, ConstantOrRegiste
static bool
IsTypedArrayElementSetInlineable(JSObject* obj, const Value& idval, const Value& value)
{
// Don't bother attaching stubs for assigning strings and objects.
// Don't bother attaching stubs for assigning strings, objects or symbols.
return IsAnyTypedArray(obj) && idval.isInt32() &&
!value.isString() && !value.isObject();
!value.isString() && !value.isObject() && !value.isSymbol();
}
static void
+3
View File
@@ -553,6 +553,9 @@ class GetPropertyIC : public IonCache
IonScript* ion, HandleObject obj,
HandleValue idval, bool* emitted);
bool tryAttachModuleNamespace(JSContext* cx, HandleScript outerScript, IonScript* ion,
HandleObject obj, HandleId id, void* returnAddr, bool* emitted);
static bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
HandleObject obj, HandleValue id, MutableHandleValue vp);
};
+10 -2
View File
@@ -469,8 +469,6 @@ LIRGenerator::visitCall(MCall* call)
} else if (target) {
// Call known functions.
if (target->isNative()) {
MOZ_ASSERT(!target->isClassConstructor());
Register cxReg, numReg, vpReg, tmpReg;
GetTempRegForIntArg(0, 0, &cxReg);
GetTempRegForIntArg(1, 0, &numReg);
@@ -3364,6 +3362,16 @@ 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)
{
+1
View File
@@ -230,6 +230,7 @@ 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);
+33
View File
@@ -10752,6 +10752,39 @@ 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,
+1
View File
@@ -162,6 +162,7 @@ namespace jit {
_(GetPropertyPolymorphic) \
_(SetPropertyPolymorphic) \
_(BindNameCache) \
_(CallBindVar) \
_(GuardShape) \
_(GuardReceiverPolymorphic) \
_(GuardObjectGroup) \
+2 -2
View File
@@ -1725,7 +1725,7 @@ MacroAssembler::convertValueToFloatingPoint(JSContext* cx, const Value& v, Float
return true;
}
MOZ_ASSERT(v.isObject());
MOZ_ASSERT(v.isObject() || v.isSymbol());
jump(fail);
return true;
}
@@ -1948,7 +1948,7 @@ MacroAssembler::convertValueToInt(JSContext* cx, const Value& v, Register output
return true;
}
MOZ_ASSERT(v.isObject());
MOZ_ASSERT(v.isObject() || v.isSymbol());
jump(fail);
return true;
+5 -3
View File
@@ -151,8 +151,10 @@
# define DEFINED_ON_RESULT_define
# define DEFINED_ON_RESULT_ = delete
# define DEFINED_ON_DISPATCH_RESULT(Result) \
DEFINED_ON_RESULT_ ## Result
# define DEFINED_ON_DISPATCH_RESULT_2(Macro, Result) \
Macro ## Result
# define DEFINED_ON_DISPATCH_RESULT(...) \
DEFINED_ON_DISPATCH_RESULT_2(DEFINED_ON_RESULT_, __VA_ARGS__)
// We need to let the evaluation of MOZ_FOR_EACH terminates.
# define DEFINED_ON_EXPAND_ARCH_RESULTS_3(ParenResult) \
@@ -733,7 +735,7 @@ class MacroAssembler : public MacroAssemblerSpecific
inline void sub32(Register src, Register dest) PER_SHARED_ARCH;
inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH;
inline void add64(Register64 src, Register64 dest) DEFINED_ON(x86, x64, arm, arm64, mips64);
inline void add64(Register64 src, Register64 dest) PER_ARCH;
// ===============================================================
// Shift functions
+92 -2
View File
@@ -493,6 +493,12 @@ ICStub::trace(JSTracer* trc)
TraceEdge(trc, &callStub->getter(), "baseline-getpropcallnativeglobal-stub-getter");
break;
}
case ICStub::GetProp_ModuleNamespace: {
ICGetProp_ModuleNamespace* nsStub = toGetProp_ModuleNamespace();
TraceEdge(trc, &nsStub->getNamespace(), "baseline-getprop-modulenamespace-stub-namespace");
TraceEdge(trc, &nsStub->environment(), "baseline-getprop-modulenamespace-stub-environment");
break;
}
case ICStub::SetProp_Native: {
ICSetProp_Native* propStub = toSetProp_Native();
TraceEdge(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
@@ -2560,7 +2566,7 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod
isTemporarilyUnoptimizable);
// Try handling scripted getters.
if (cacheableCall && isScripted && !isDOMProxy) {
if (cacheableCall && isScripted && !isDOMProxy && engine == ICStubCompiler::Engine::Baseline) {
RootedFunction callee(cx, &shape->getterObject()->as<JSFunction>());
MOZ_ASSERT(callee->hasScript());
@@ -2573,7 +2579,7 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod
JitSpew(JitSpew_BaselineIC, " Generating GetProp(NativeObj/ScriptedGetter %s:%" PRIuSIZE ") stub",
callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
ICGetProp_CallScripted::Compiler compiler(cx, engine, monitorStub, obj, holder, callee,
ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee,
script->pcToOffset(pc));
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
@@ -2792,6 +2798,48 @@ TryAttachTypedObjectGetPropStub(JSContext* cx, HandleScript script, ICGetProp_Fa
return true;
}
static bool
TryAttachModuleNamespaceGetPropStub(JSContext* cx, HandleScript script, ICGetProp_Fallback* stub,
ICStubCompiler::Engine engine, HandlePropertyName name,
HandleValue val, bool* attached)
{
MOZ_ASSERT(!*attached);
if (!ModuleNamespaceObject::isInstance(val))
return true;
Rooted<ModuleNamespaceObject*> ns(cx, &val.toObject().as<ModuleNamespaceObject>());
RootedModuleEnvironmentObject env(cx);
RootedShape shape(cx);
if (!ns->bindings().lookup(NameToId(name), env.address(), shape.address()))
return true;
// Don't emit a stub until the target binding has been initialized.
if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
return true;
ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
bool isFixedSlot;
uint32_t offset;
GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
// Instantiate this property for singleton holders, for use during Ion compilation.
if (IsIonEnabled(cx))
EnsureTrackPropertyTypes(cx, env, shape->propid());
ICGetProp_ModuleNamespace::Compiler compiler(cx, engine, monitorStub,
ns, env, isFixedSlot, offset);
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
*attached = true;
return true;
}
static bool
TryAttachPrimitiveGetPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
ICGetProp_Fallback* stub, ICStubCompiler::Engine engine,
@@ -3066,6 +3114,11 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_
if (attached)
return true;
if (!TryAttachModuleNamespaceGetPropStub(cx, script, stub, engine, name, val, &attached))
return false;
if (attached)
return true;
if (val.isString() || val.isNumber() || val.isBoolean()) {
if (!TryAttachPrimitiveGetPropStub(cx, script, pc, stub, engine, name, val, res, &attached))
return false;
@@ -3515,6 +3568,8 @@ ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler& masm)
bool
ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(engine_ == Engine::Baseline);
Label failure;
Label failureLeaveStubFrame;
AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
@@ -4278,6 +4333,41 @@ ICGetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm)
return true;
}
bool
ICGetProp_ModuleNamespace::Compiler::generateStubCode(MacroAssembler& masm)
{
Label failure;
AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch = regs.takeAnyExcluding(ICTailCallReg);
// Guard on namespace object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
Register object = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfNamespace()), scratch);
masm.branchPtr(Assembler::NotEqual, object, scratch, &failure);
// Determine base pointer for load.
Register loadBase = regs.takeAnyExcluding(ICTailCallReg);
masm.loadPtr(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfEnvironment()), loadBase);
if (!isFixedSlot_)
masm.loadPtr(Address(loadBase, NativeObject::offsetOfSlots()), loadBase);
// Load the property.
masm.load32(Address(ICStubReg, ICGetProp_ModuleNamespace::offsetOfOffset()), scratch);
masm.loadValue(BaseIndex(loadBase, scratch, TimesOne), R0);
// Enter type monitor IC to type-check result.
EmitEnterTypeMonitorIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
void
BaselineScript::noteAccessedGetter(uint32_t pcOffset)
{
+69 -2
View File
@@ -2805,6 +2805,73 @@ class ICGetProp_TypedObject : public ICMonitoredStub
};
};
class ICGetProp_ModuleNamespace : public ICMonitoredStub
{
friend class ICStubSpace;
HeapPtrModuleNamespaceObject namespace_;
HeapPtrModuleEnvironmentObject environment_;
uint32_t offset_;
ICGetProp_ModuleNamespace(JitCode* stubCode, ICStub* firstMonitorStub,
HandleModuleNamespaceObject ns, HandleModuleEnvironmentObject env,
uint32_t offset)
: ICMonitoredStub(ICStub::GetProp_ModuleNamespace, stubCode, firstMonitorStub),
namespace_(ns), environment_(env), offset_(offset)
{
(void) offset_; // Silence clang warning.
}
public:
HeapPtrModuleNamespaceObject& getNamespace() {
return namespace_;
}
HeapPtrModuleEnvironmentObject& environment() {
return environment_;
}
static size_t offsetOfNamespace() {
return offsetof(ICGetProp_ModuleNamespace, namespace_);
}
static size_t offsetOfEnvironment() {
return offsetof(ICGetProp_ModuleNamespace, environment_);
}
static size_t offsetOfOffset() {
return offsetof(ICGetProp_ModuleNamespace, offset_);
}
class Compiler : public ICStubCompiler {
protected:
ICStub* firstMonitorStub_;
RootedModuleNamespaceObject namespace_;
RootedModuleEnvironmentObject environment_;
bool isFixedSlot_;
uint32_t offset_;
bool generateStubCode(MacroAssembler& masm);
virtual int32_t getKey() const {
return static_cast<int32_t>(engine_) |
(static_cast<int32_t>(kind) << 1) |
(static_cast<int32_t>(isFixedSlot_) << 17);
}
public:
Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub,
HandleModuleNamespaceObject ns, HandleModuleEnvironmentObject env, bool isFixedSlot,
uint32_t offset)
: ICStubCompiler(cx, ICStub::GetProp_ModuleNamespace, engine),
firstMonitorStub_(firstMonitorStub),
namespace_(cx, ns), environment_(cx, env), isFixedSlot_(isFixedSlot), offset_(offset)
{}
ICStub* getStub(ICStubSpace* space) {
return newStub<ICGetProp_ModuleNamespace>(space, getStubCode(), firstMonitorStub_,
namespace_, environment_, offset_);
}
};
};
class ICGetPropCallGetter : public ICMonitoredStub
{
friend class ICStubSpace;
@@ -2931,9 +2998,9 @@ class ICGetProp_CallScripted : public ICGetPropCallGetter
bool generateStubCode(MacroAssembler& masm);
public:
Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, HandleObject obj,
Compiler(JSContext* cx, ICStub* firstMonitorStub, HandleObject obj,
HandleObject holder, HandleFunction getter, uint32_t pcOffset)
: ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, engine,
: ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallScripted, Engine::Baseline,
firstMonitorStub, obj, holder,
getter, pcOffset, /* outerClass = */ nullptr)
{}
+1
View File
@@ -52,6 +52,7 @@ namespace jit {
_(GetProp_DOMProxyShadowed) \
_(GetProp_ArgumentsLength) \
_(GetProp_ArgumentsCallee) \
_(GetProp_ModuleNamespace) \
_(GetProp_Generic) \
\
+15 -13
View File
@@ -167,14 +167,21 @@ 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, scopeChain);
while (!obj->isQualifiedVarObj())
obj = obj->enclosingScope();
RootedObject obj(cx, BindVar(cx, scopeChain));
return DefVarOperation(cx, obj, dn, attrs);
}
@@ -185,10 +192,7 @@ DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject sc
Rooted<ClonedBlockObject*> lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain));
// Find the variables object.
RootedObject varObj(cx, scopeChain);
while (!varObj->isQualifiedVarObj())
varObj = varObj->enclosingScope();
RootedObject varObj(cx, BindVar(cx, scopeChain));
return DefLexicalOperation(cx, lexical, varObj, dn, attrs);
}
@@ -853,9 +857,8 @@ bool
InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
{
RootedScript script(cx, frame->script());
RootedObject varObj(cx, frame->scopeChain());
while (!varObj->isQualifiedVarObj())
varObj = varObj->enclosingScope();
RootedObject scopeChain(cx, frame->scopeChain());
RootedObject varObj(cx, BindVar(cx, scopeChain));
if (script->isForEval()) {
// Strict eval needs its own call object.
@@ -866,13 +869,12 @@ 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(frame->scopeChain()));
&NearestEnclosingExtensibleLexicalScope(scopeChain));
if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj))
return false;
}
+1
View File
@@ -588,6 +588,7 @@ 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);
+7 -7
View File
@@ -2371,9 +2371,9 @@ MacroAssemblerARMCompat::store32(Imm32 src, const Address& address)
void
MacroAssemblerARMCompat::store32(Imm32 imm, const BaseIndex& dest)
{
AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
ma_mov(imm, scratch2);
store32(scratch2, dest);
ScratchRegisterScope scratch(asMasm());
ma_mov(imm, scratch);
store32(scratch, dest);
}
void
@@ -2382,11 +2382,11 @@ MacroAssemblerARMCompat::store32(Register src, const BaseIndex& dest)
Register base = dest.base;
uint32_t scale = Imm32::ShiftOf(dest.scale).value;
ScratchRegisterScope scratch(asMasm());
AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
if (dest.offset != 0) {
ma_add(base, Imm32(dest.offset), scratch);
base = scratch;
ma_add(base, Imm32(dest.offset), scratch2);
base = scratch2;
}
ma_str(src, DTRAddr(base, DtrRegImmShift(dest.index, LSL, scale)));
}
@@ -5295,7 +5295,7 @@ MacroAssembler::pushFakeReturnAddress(Register scratch)
uint32_t pseudoReturnOffset = currentOffset();
leaveNoPool();
MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8);
return pseudoReturnOffset;
}
+6 -1
View File
@@ -238,8 +238,13 @@ MacroAssemblerCompat::branchValueIsNurseryObject(Condition cond, ValueOperand va
MOZ_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
MOZ_ASSERT(temp != ScratchReg && temp != ScratchReg2); // Both may be used internally.
// 'Value' representing the start of the nursery tagged as a JSObject
const Nursery& nursery = GetJitContext()->runtime->gcNursery();
// Avoid creating a bogus ObjectValue below.
if (!nursery.exists())
return;
// 'Value' representing the start of the nursery tagged as a JSObject
Value start = ObjectValue(*reinterpret_cast<JSObject*>(nursery.start()));
movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), temp);
+3 -4
View File
@@ -364,11 +364,10 @@ class Redirection
}
}
js::AutoEnterOOMUnsafeRegion oomUnsafe;
Redirection* redir = (Redirection*)js_malloc(sizeof(Redirection));
if (!redir) {
MOZ_ReportAssertionFailure("[unhandlable oom] Simulator redirection", __FILE__, __LINE__);
MOZ_CRASH();
}
if (!redir)
oomUnsafe.crash("Simulator redirection");
new(redir) Redirection(nativeFunction, type, sim);
return redir;
}
@@ -88,6 +88,18 @@ AssemblerMIPSShared::finish()
isFinished = true;
}
bool
AssemblerMIPSShared::asmMergeWith(const AssemblerMIPSShared& other)
{
if (!AssemblerShared::asmMergeWith(size(), other))
return false;
for (size_t i = 0; i < other.numLongJumps(); i++) {
size_t off = other.longJumps_[i];
addLongJump(BufferOffset(size() + off));
}
return m_buffer.appendBuffer(other.m_buffer);
}
uint32_t
AssemblerMIPSShared::actualIndex(uint32_t idx_) const
{
@@ -126,7 +138,7 @@ AssemblerMIPSShared::processCodeLabels(uint8_t* rawCode)
{
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.dest(), rawCode + label.src()->offset());
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
}
}
@@ -1350,6 +1362,28 @@ AssemblerMIPSShared::retarget(Label* label, Label* target)
label->reset();
}
void
AssemblerMIPSShared::retargetWithOffset(size_t baseOffset, const LabelBase* label, Label* target)
{
if (!label->used())
return;
MOZ_ASSERT(!target->bound());
int32_t next;
BufferOffset labelBranchOffset(label->offset() + baseOffset);
do {
Instruction* inst = editSrc(labelBranchOffset);
int32_t prev = target->use(labelBranchOffset.getOffset());
MOZ_RELEASE_ASSERT(prev == Label::INVALID_OFFSET || unsigned(prev) < size());
next = inst[1].encode();
inst[1].setData(prev);
labelBranchOffset = BufferOffset(next + baseOffset);
} while (next != LabelBase::INVALID_OFFSET);
}
void dbg_break() {}
void
AssemblerMIPSShared::as_break(uint32_t code)
+18 -7
View File
@@ -645,6 +645,18 @@ class MIPSBufferWithExecutableCopy : public MIPSBuffer
buffer += cur->length();
}
}
bool appendBuffer(const MIPSBufferWithExecutableCopy& other) {
if (this->oom())
return false;
for (Slice* cur = other.head; cur != nullptr; cur = cur->getNext()) {
this->putBytes(cur->length(), &cur->instructions);
if (this->oom())
return false;
}
return true;
}
};
class AssemblerMIPSShared : public AssemblerShared
@@ -796,9 +808,7 @@ class AssemblerMIPSShared : public AssemblerShared
bool isFinished;
public:
void finish();
bool asmMergeWith(const AssemblerMIPSShared& other) {
MOZ_CRASH("NYI");
}
bool asmMergeWith(const AssemblerMIPSShared& other);
void executableCopy(void* buffer);
void copyJumpRelocationTable(uint8_t* dest);
void copyDataRelocationTable(uint8_t* dest);
@@ -1033,14 +1043,15 @@ class AssemblerMIPSShared : public AssemblerShared
// label operations
void bind(Label* label, BufferOffset boff = BufferOffset());
virtual void bind(InstImm* inst, uintptr_t branch, uintptr_t target) = 0;
virtual void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address) = 0;
virtual void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) = 0;
void bind(CodeOffset* label) {
label->bind(currentOffset());
}
uint32_t currentOffset() {
return nextOffset().getOffset();
}
void retarget(Label* label, Label* target);
void retargetWithOffset(size_t offset, const LabelBase* label, Label* target) {
MOZ_CRASH("NYI");
}
void retargetWithOffset(size_t baseOffset, const LabelBase* label, Label* target);
// See Bind
size_t labelToPatchOffset(CodeOffset label) { return label.offset(); }
@@ -947,9 +947,9 @@ MacroAssembler::pushFakeReturnAddress(Register scratch)
{
CodeLabel cl;
ma_li(scratch, cl.dest());
ma_li(scratch, cl.patchAt());
Push(scratch);
bind(cl.src());
bind(cl.target());
uint32_t retAddr = currentOffset();
addCodeLabel(cl);
+5 -10
View File
@@ -240,18 +240,13 @@ Assembler::trace(JSTracer* trc)
}
void
Assembler::Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address)
Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
{
if (label->used()) {
int32_t src = label->offset();
do {
Instruction* inst = (Instruction*) (rawCode + src);
uint32_t next = Assembler::ExtractLuiOriValue(inst, inst->next());
Assembler::UpdateLuiOriValue(inst, inst->next(), (uint32_t)address);
src = next;
} while (src != AbsoluteLabel::INVALID_OFFSET);
if (label->bound()) {
intptr_t offset = label->offset();
Instruction* inst = (Instruction*) (rawCode + offset);
Assembler::UpdateLuiOriValue(inst, inst->next(), (uint32_t)address);
}
label->bind();
}
void
+1 -1
View File
@@ -124,7 +124,7 @@ class Assembler : public AssemblerMIPSShared
using AssemblerMIPSShared::bind;
void bind(RepatchLabel* label);
void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address);
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
+4 -4
View File
@@ -62,7 +62,7 @@ CodeGeneratorMIPS::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
MTableSwitch* mir = ool->mir();
masm.haltingAlign(sizeof(void*));
masm.bind(ool->jumpLabel()->src());
masm.bind(ool->jumpLabel()->target());
masm.addCodeLabel(*ool->jumpLabel());
for (size_t i = 0; i < mir->numCases(); i++) {
@@ -73,9 +73,9 @@ CodeGeneratorMIPS::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
// The entries of the jump table need to be absolute addresses and thus
// must be patched after codegen is finished.
CodeLabel cl;
masm.ma_li(ScratchRegister, cl.dest());
masm.ma_li(ScratchRegister, cl.patchAt());
masm.branch(ScratchRegister);
cl.src()->bind(caseoffset);
cl.target()->bind(caseoffset);
masm.addCodeLabel(cl);
}
}
@@ -101,7 +101,7 @@ CodeGeneratorMIPS::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
addOutOfLineCode(ool, mir);
// Compute the position where a pointer to the right case stands.
masm.ma_li(address, ool->jumpLabel()->dest());
masm.ma_li(address, ool->jumpLabel()->patchAt());
masm.lshiftPtr(Imm32(4), index);
masm.addPtr(index, address);
@@ -75,6 +75,18 @@ MacroAssembler::xorPtr(Imm32 imm, Register dest)
ma_xor(dest, imm);
}
// ===============================================================
// Arithmetic functions
void
MacroAssembler::add64(Register64 src, Register64 dest)
{
as_addu(dest.low, dest.low, src.low);
as_sltu(ScratchRegister, dest.low, src.low);
as_addu(dest.high, dest.high, src.high);
as_addu(dest.high, dest.high, ScratchRegister);
}
// ===============================================================
// Shift functions
+4 -7
View File
@@ -312,14 +312,11 @@ MacroAssemblerMIPSCompat::inc64(AbsoluteAddress dest)
}
void
MacroAssemblerMIPS::ma_li(Register dest, AbsoluteLabel* label)
MacroAssemblerMIPS::ma_li(Register dest, CodeOffset* label)
{
MOZ_ASSERT(!label->bound());
// Thread the patch list through the unpatched address word in the
// instruction stream.
BufferOffset bo = m_buffer.nextOffset();
ma_liPatchable(dest, Imm32(label->prev()));
label->setPrev(bo.getOffset());
ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
label->bind(bo.getOffset());
}
void
@@ -967,7 +964,7 @@ void
MacroAssemblerMIPSCompat::load32(wasm::SymbolicAddress address, Register dest)
{
movePtr(address, ScratchRegister);
load32(Address(ScratchRegister, 0), address);
load32(Address(ScratchRegister, 0), dest);
}
void
+1 -1
View File
@@ -54,7 +54,7 @@ class MacroAssemblerMIPS : public MacroAssemblerMIPSShared
using MacroAssemblerMIPSShared::ma_cmp_set;
using MacroAssemblerMIPSShared::ma_subTestOverflow;
void ma_li(Register dest, AbsoluteLabel* label);
void ma_li(Register dest, CodeOffset* label);
void ma_liPatchable(Register dest, Imm32 imm);
void ma_li(Register dest, ImmWord imm);
+7 -6
View File
@@ -204,12 +204,14 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.push(s4); // descriptor
CodeLabel returnLabel;
CodeLabel oomReturnLabel;
if (type == EnterJitBaseline) {
// Handle OSR.
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
regs.take(OsrFrameReg);
regs.take(BaselineFrameReg);
regs.take(reg_code);
regs.take(ReturnReg);
const Address slotNumStackValues(BaselineFrameReg, sizeof(EnterJITRegs) +
offsetof(EnterJITArgs, numStackValues));
@@ -226,7 +228,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
// Push return address.
masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
masm.ma_li(scratch, returnLabel.dest());
masm.ma_li(scratch, returnLabel.patchAt());
masm.storePtr(scratch, Address(StackPointer, 0));
// Push previous frame pointer.
@@ -265,10 +267,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
regs.add(OsrFrameReg);
regs.add(scratch);
regs.add(numStackValues);
regs.take(JSReturnOperand);
regs.take(ReturnReg);
Register jitcode = regs.takeAny();
masm.loadPtr(Address(StackPointer, 0), jitcode);
masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr);
@@ -300,7 +299,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.movePtr(framePtr, StackPointer);
masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
masm.ma_li(scratch, returnLabel.dest());
masm.ma_li(scratch, oomReturnLabel.patchAt());
masm.jump(scratch);
masm.bind(&notOsr);
@@ -318,8 +317,10 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
if (type == EnterJitBaseline) {
// Baseline OSR will return here.
masm.bind(returnLabel.src());
masm.bind(returnLabel.target());
masm.addCodeLabel(returnLabel);
masm.bind(oomReturnLabel.target());
masm.addCodeLabel(oomReturnLabel);
}
// Pop arguments off the stack.
+5 -10
View File
@@ -234,18 +234,13 @@ Assembler::trace(JSTracer* trc)
}
void
Assembler::Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address)
Assembler::Bind(uint8_t* rawCode, CodeOffset* label, const void* address)
{
if (label->used()) {
int64_t src = label->offset();
do {
Instruction* inst = (Instruction*) (rawCode + src);
uint64_t next = Assembler::ExtractLoad64Value(inst);
Assembler::UpdateLoad64Value(inst, (uint64_t)address);
src = next;
} while (src != AbsoluteLabel::INVALID_OFFSET);
if (label->bound()) {
intptr_t offset = label->offset();
Instruction* inst = (Instruction*) (rawCode + offset);
Assembler::UpdateLoad64Value(inst, (uint64_t)address);
}
label->bind();
}
void
+1 -1
View File
@@ -126,7 +126,7 @@ class Assembler : public AssemblerMIPSShared
using AssemblerMIPSShared::bind;
void bind(RepatchLabel* label);
void Bind(uint8_t* rawCode, AbsoluteLabel* label, const void* address);
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
+4 -4
View File
@@ -60,7 +60,7 @@ CodeGeneratorMIPS64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
MTableSwitch* mir = ool->mir();
masm.haltingAlign(sizeof(void*));
masm.bind(ool->jumpLabel()->src());
masm.bind(ool->jumpLabel()->target());
masm.addCodeLabel(*ool->jumpLabel());
for (size_t i = 0; i < mir->numCases(); i++) {
@@ -72,11 +72,11 @@ CodeGeneratorMIPS64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool)
// must be patched after codegen is finished. Each table entry uses 8
// instructions (4 for load address, 2 for branch, and 2 padding).
CodeLabel cl;
masm.ma_li(ScratchRegister, cl.dest());
masm.ma_li(ScratchRegister, cl.patchAt());
masm.branch(ScratchRegister);
masm.as_nop();
masm.as_nop();
cl.src()->bind(caseoffset);
cl.target()->bind(caseoffset);
masm.addCodeLabel(cl);
}
}
@@ -102,7 +102,7 @@ CodeGeneratorMIPS64::emitTableSwitchDispatch(MTableSwitch* mir, Register index,
addOutOfLineCode(ool, mir);
// Compute the position where a pointer to the right case stands.
masm.ma_li(address, ool->jumpLabel()->dest());
masm.ma_li(address, ool->jumpLabel()->patchAt());
// index = size of table entry * index.
// See CodeGeneratorMIPS64::visitOutOfLineTableSwitch
masm.lshiftPtr(Imm32(5), index);
+3 -6
View File
@@ -273,14 +273,11 @@ MacroAssemblerMIPS64Compat::movq(Register rs, Register rd)
}
void
MacroAssemblerMIPS64::ma_li(Register dest, AbsoluteLabel* label)
MacroAssemblerMIPS64::ma_li(Register dest, CodeOffset* label)
{
MOZ_ASSERT(!label->bound());
// Thread the patch list through the unpatched address word in the
// instruction stream.
BufferOffset bo = m_buffer.nextOffset();
ma_liPatchable(dest, ImmWord(label->prev()));
label->setPrev(bo.getOffset());
ma_liPatchable(dest, ImmWord(/* placeholder */ 0));
label->bind(bo.getOffset());
}
void
+1 -1
View File
@@ -59,7 +59,7 @@ class MacroAssemblerMIPS64 : public MacroAssemblerMIPSShared
using MacroAssemblerMIPSShared::ma_cmp_set;
using MacroAssemblerMIPSShared::ma_subTestOverflow;
void ma_li(Register dest, AbsoluteLabel* label);
void ma_li(Register dest, CodeOffset* label);
void ma_li(Register dest, ImmWord imm);
void ma_liPatchable(Register dest, ImmPtr imm);
void ma_liPatchable(Register dest, ImmWord imm, LiFlags flags = Li48);
+6 -3
View File
@@ -197,6 +197,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.push(s4); // descriptor
CodeLabel returnLabel;
CodeLabel oomReturnLabel;
if (type == EnterJitBaseline) {
// Handle OSR.
AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
@@ -215,7 +216,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
// Push return address.
masm.subPtr(Imm32(sizeof(uintptr_t)), StackPointer);
masm.ma_li(scratch, returnLabel.dest());
masm.ma_li(scratch, returnLabel.patchAt());
masm.storePtr(scratch, Address(StackPointer, 0));
// Push previous frame pointer.
@@ -285,7 +286,7 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
masm.movePtr(framePtr, StackPointer);
masm.addPtr(Imm32(2 * sizeof(uintptr_t)), StackPointer);
masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
masm.ma_li(scratch, returnLabel.dest());
masm.ma_li(scratch, oomReturnLabel.patchAt());
masm.jump(scratch);
masm.bind(&notOsr);
@@ -303,8 +304,10 @@ JitRuntime::generateEnterJIT(JSContext* cx, EnterJitType type)
if (type == EnterJitBaseline) {
// Baseline OSR will return here.
masm.bind(returnLabel.src());
masm.bind(returnLabel.target());
masm.addCodeLabel(returnLabel);
masm.bind(oomReturnLabel.target());
masm.addCodeLabel(oomReturnLabel);
}
// Pop arguments off the stack.
+16
View File
@@ -5775,6 +5775,22 @@ 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>
{
+1
View File
@@ -277,6 +277,7 @@
_(GetPropertyPolymorphicV) \
_(GetPropertyPolymorphicT) \
_(BindNameCache) \
_(CallBindVar) \
_(CallGetProperty) \
_(GetNameCache) \
_(CallGetIntrinsicValue) \
+8 -4
View File
@@ -106,9 +106,11 @@ static MOZ_CONSTEXPR_VAR Register IntArgReg1 = rdx;
static MOZ_CONSTEXPR_VAR Register IntArgReg2 = r8;
static MOZ_CONSTEXPR_VAR Register IntArgReg3 = r9;
static MOZ_CONSTEXPR_VAR uint32_t NumIntArgRegs = 4;
static MOZ_CONSTEXPR_VAR Register IntArgRegs[NumIntArgRegs] = { rcx, rdx, r8, r9 };
// Use "const" instead of MOZ_CONSTEXPR_VAR here to work around a bug
// of VS2015 Update 1. See bug 1229604.
static const Register IntArgRegs[NumIntArgRegs] = { rcx, rdx, r8, r9 };
static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { rax, rdi, rbx, rsi };
static const Register CallTempNonArgRegs[] = { rax, rdi, rbx, rsi };
static const uint32_t NumCallTempNonArgRegs =
mozilla::ArrayLength(CallTempNonArgRegs);
@@ -126,9 +128,11 @@ static MOZ_CONSTEXPR_VAR Register IntArgReg3 = rcx;
static MOZ_CONSTEXPR_VAR Register IntArgReg4 = r8;
static MOZ_CONSTEXPR_VAR Register IntArgReg5 = r9;
static MOZ_CONSTEXPR_VAR uint32_t NumIntArgRegs = 6;
static MOZ_CONSTEXPR_VAR Register IntArgRegs[NumIntArgRegs] = { rdi, rsi, rdx, rcx, r8, r9 };
static const Register IntArgRegs[NumIntArgRegs] = { rdi, rsi, rdx, rcx, r8, r9 };
static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { rax, rbx };
// Use "const" instead of MOZ_CONSTEXPR_VAR here to work around a bug
// of VS2015 Update 1. See bug 1229604.
static const Register CallTempNonArgRegs[] = { rax, rbx };
static const uint32_t NumCallTempNonArgRegs =
mozilla::ArrayLength(CallTempNonArgRegs);
+4 -4
View File
@@ -32,7 +32,7 @@ MacroAssemblerX64::loadConstantDouble(double d, FloatRegister dest)
// PC-relative addressing. Use "jump" label support code, because we need
// the same PC-relative address patching that jumps use.
JmpSrc j = masm.vmovsd_ripr(dest.encoding());
dbl->uses.append(CodeOffset(j.offset()));
propagateOOM(dbl->uses.append(CodeOffset(j.offset())));
}
void
@@ -45,7 +45,7 @@ MacroAssemblerX64::loadConstantFloat32(float f, FloatRegister dest)
return;
// See comment in loadConstantDouble
JmpSrc j = masm.vmovss_ripr(dest.encoding());
flt->uses.append(CodeOffset(j.offset()));
propagateOOM(flt->uses.append(CodeOffset(j.offset())));
}
void
@@ -59,7 +59,7 @@ MacroAssemblerX64::loadConstantInt32x4(const SimdConstant& v, FloatRegister dest
return;
MOZ_ASSERT(val->type() == SimdConstant::Int32x4);
JmpSrc j = masm.vmovdqa_ripr(dest.encoding());
val->uses.append(CodeOffset(j.offset()));
propagateOOM(val->uses.append(CodeOffset(j.offset())));
}
void
@@ -73,7 +73,7 @@ MacroAssemblerX64::loadConstantFloat32x4(const SimdConstant&v, FloatRegister des
return;
MOZ_ASSERT(val->type() == SimdConstant::Float32x4);
JmpSrc j = masm.vmovaps_ripr(dest.encoding());
val->uses.append(CodeOffset(j.offset()));
propagateOOM(val->uses.append(CodeOffset(j.offset())));
}
void
@@ -232,84 +232,76 @@ template void
MacroAssemblerX86Shared::atomicExchangeToTypedIntArray(Scalar::Type arrayType, const BaseIndex& mem,
Register value, Register temp, AnyRegister output);
template<class T, class Map>
T*
MacroAssemblerX86Shared::getConstant(const typename T::Pod& value, Map& map,
Vector<T, 0, SystemAllocPolicy>& vec)
{
typedef typename Map::AddPtr AddPtr;
if (!map.initialized()) {
enoughMemory_ &= map.init();
if (!enoughMemory_)
return nullptr;
}
size_t index;
if (AddPtr p = map.lookupForAdd(value)) {
index = p->value();
} else {
index = vec.length();
enoughMemory_ &= vec.append(T(value));
if (!enoughMemory_)
return nullptr;
enoughMemory_ &= map.add(p, value, index);
if (!enoughMemory_)
return nullptr;
}
return &vec[index];
}
MacroAssemblerX86Shared::Float*
MacroAssemblerX86Shared::getFloat(float f)
{
if (!floatMap_.initialized()) {
enoughMemory_ &= floatMap_.init();
if (!enoughMemory_)
return nullptr;
}
size_t floatIndex;
if (FloatMap::AddPtr p = floatMap_.lookupForAdd(f)) {
floatIndex = p->value();
} else {
floatIndex = floats_.length();
enoughMemory_ &= floats_.append(Float(f));
if (!enoughMemory_)
return nullptr;
enoughMemory_ &= floatMap_.add(p, f, floatIndex);
if (!enoughMemory_)
return nullptr;
}
return &floats_[floatIndex];
return getConstant<Float, FloatMap>(f, floatMap_, floats_);
}
MacroAssemblerX86Shared::Double*
MacroAssemblerX86Shared::getDouble(double d)
{
if (!doubleMap_.initialized()) {
enoughMemory_ &= doubleMap_.init();
if (!enoughMemory_)
return nullptr;
}
size_t doubleIndex;
if (DoubleMap::AddPtr p = doubleMap_.lookupForAdd(d)) {
doubleIndex = p->value();
} else {
doubleIndex = doubles_.length();
enoughMemory_ &= doubles_.append(Double(d));
if (!enoughMemory_)
return nullptr;
enoughMemory_ &= doubleMap_.add(p, d, doubleIndex);
if (!enoughMemory_)
return nullptr;
}
return &doubles_[doubleIndex];
return getConstant<Double, DoubleMap>(d, doubleMap_, doubles_);
}
MacroAssemblerX86Shared::SimdData*
MacroAssemblerX86Shared::getSimdData(const SimdConstant& v)
{
if (!simdMap_.initialized()) {
enoughMemory_ &= simdMap_.init();
if (!enoughMemory_)
return nullptr;
}
size_t index;
if (SimdMap::AddPtr p = simdMap_.lookupForAdd(v)) {
index = p->value();
} else {
index = simds_.length();
enoughMemory_ &= simds_.append(SimdData(v));
if (!enoughMemory_)
return nullptr;
enoughMemory_ &= simdMap_.add(p, v, index);
if (!enoughMemory_)
return nullptr;
}
return &simds_[index];
return getConstant<SimdData, SimdMap>(v, simdMap_, simds_);
}
template<class T, class Map>
static bool
AppendShiftedUses(const MacroAssemblerX86Shared::UsesVector& old, size_t delta,
MacroAssemblerX86Shared::UsesVector* vec)
MergeConstants(size_t delta, const Vector<T, 0, SystemAllocPolicy>& other,
Map& map, Vector<T, 0, SystemAllocPolicy>& vec)
{
for (CodeOffset use : old) {
use.offsetBy(delta);
if (!vec->append(use))
return false;
typedef typename Map::AddPtr AddPtr;
if (!map.initialized() && !map.init())
return false;
for (const T& c : other) {
size_t index;
if (AddPtr p = map.lookupForAdd(c.value)) {
index = p->value();
} else {
index = vec.length();
if (!vec.append(T(c.value)) || !map.add(p, c.value, index))
return false;
}
MacroAssemblerX86Shared::UsesVector& uses = vec[index].uses;
for (CodeOffset use : c.uses) {
use.offsetBy(delta);
if (!uses.append(use))
return false;
}
}
return true;
}
@@ -317,56 +309,14 @@ bool
MacroAssemblerX86Shared::asmMergeWith(const MacroAssemblerX86Shared& other)
{
size_t sizeBefore = masm.size();
if (!Assembler::asmMergeWith(other))
return false;
if (!doubleMap_.initialized() && !doubleMap_.init())
if (!MergeConstants<Double, DoubleMap>(sizeBefore, other.doubles_, doubleMap_, doubles_))
return false;
if (!floatMap_.initialized() && !floatMap_.init())
if (!MergeConstants<Float, FloatMap>(sizeBefore, other.floats_, floatMap_, floats_))
return false;
if (!simdMap_.initialized() && !simdMap_.init())
if (!MergeConstants<SimdData, SimdMap>(sizeBefore, other.simds_, simdMap_, simds_))
return false;
for (const Double& d : other.doubles_) {
size_t index;
if (DoubleMap::AddPtr p = doubleMap_.lookupForAdd(d.value)) {
index = p->value();
} else {
index = doubles_.length();
if (!doubles_.append(Double(d.value)) || !doubleMap_.add(p, d.value, index))
return false;
}
if (!AppendShiftedUses(d.uses, sizeBefore, &doubles_[index].uses))
return false;
}
for (const Float& f : other.floats_) {
size_t index;
if (FloatMap::AddPtr p = floatMap_.lookupForAdd(f.value)) {
index = p->value();
} else {
index = floats_.length();
if (!floats_.append(Float(f.value)) || !floatMap_.add(p, f.value, index))
return false;
}
if (!AppendShiftedUses(f.uses, sizeBefore, &floats_[index].uses))
return false;
}
for (const SimdData& s : other.simds_) {
size_t index;
if (SimdMap::AddPtr p = simdMap_.lookupForAdd(s.value)) {
index = p->value();
} else {
index = simds_.length();
if (!simds_.append(SimdData(s.value)) || !simdMap_.add(p, s.value, index))
return false;
}
if (!AppendShiftedUses(s.uses, sizeBefore, &simds_[index].uses))
return false;
}
return true;
}
@@ -49,40 +49,37 @@ class MacroAssemblerX86Shared : public Assembler
typedef Vector<CodeOffset, 0, SystemAllocPolicy> UsesVector;
protected:
// For Double, Float and SimdData, make the move ctors explicit so that MSVC
// knows what to use instead of copying these data structures.
struct Double {
double value;
template<class T>
struct Constant {
typedef T Pod;
T value;
UsesVector uses;
explicit Double(double value) : value(value) {}
Double(Double&& other) : value(other.value), uses(mozilla::Move(other.uses)) {}
explicit Double(const Double&) = delete;
explicit Constant(const T& value) : value(value) {}
Constant(Constant<T>&& other) : value(other.value), uses(mozilla::Move(other.uses)) {}
explicit Constant(const Constant<T>&) = delete;
};
// These use SystemAllocPolicy since asm.js releases memory after each
// Containers use SystemAllocPolicy since asm.js releases memory after each
// function is compiled, and these need to live until after all functions
// are compiled.
using Double = Constant<double>;
Vector<Double, 0, SystemAllocPolicy> doubles_;
typedef HashMap<double, size_t, DefaultHasher<double>, SystemAllocPolicy> DoubleMap;
DoubleMap doubleMap_;
struct Float {
float value;
UsesVector uses;
explicit Float(float value) : value(value) {}
Float(Float&& other) : value(other.value), uses(mozilla::Move(other.uses)) {}
explicit Float(const Float&) = delete;
};
using Float = Constant<float>;
Vector<Float, 0, SystemAllocPolicy> floats_;
typedef HashMap<float, size_t, DefaultHasher<float>, SystemAllocPolicy> FloatMap;
FloatMap floatMap_;
struct SimdData {
SimdConstant value;
UsesVector uses;
explicit SimdData(const SimdConstant& v) : value(v) {}
SimdData(SimdData&& other) : value(other.value), uses(mozilla::Move(other.uses)) {}
struct SimdData : public Constant<SimdConstant> {
explicit SimdData(SimdConstant d) : Constant<SimdConstant>(d) {}
SimdData(SimdData&& d) : Constant<SimdConstant>(mozilla::Move(d)) {}
explicit SimdData(const SimdData&) = delete;
SimdConstant::Type type() const { return value.type(); }
};
@@ -91,6 +88,9 @@ class MacroAssemblerX86Shared : public Assembler
typedef HashMap<SimdConstant, size_t, SimdConstant, SystemAllocPolicy> SimdMap;
SimdMap simdMap_;
template<class T, class Map>
T* getConstant(const typename T::Pod& value, Map& map, Vector<T, 0, SystemAllocPolicy>& vec);
Float* getFloat(float f);
Double* getDouble(double d);
SimdData* getSimdData(const SimdConstant& v);
+3 -1
View File
@@ -61,7 +61,9 @@ static MOZ_CONSTEXPR_VAR Register CallTempReg4 = esi;
static MOZ_CONSTEXPR_VAR Register CallTempReg5 = edx;
// We have no arg regs, so our NonArgRegs are just our CallTempReg*
static MOZ_CONSTEXPR_VAR Register CallTempNonArgRegs[] = { edi, eax, ebx, ecx, esi, edx };
// Use "const" instead of MOZ_CONSTEXPR_VAR here to work around a bug
// of VS2015 Update 1. See bug 1229604.
static const Register CallTempNonArgRegs[] = { edi, eax, ebx, ecx, esi, edx };
static const uint32_t NumCallTempNonArgRegs =
mozilla::ArrayLength(CallTempNonArgRegs);
+6 -6
View File
@@ -99,7 +99,7 @@ MacroAssemblerX86::loadConstantDouble(double d, FloatRegister dest)
if (!dbl)
return;
masm.vmovsd_mr(nullptr, dest.encoding());
dbl->uses.append(CodeOffset(masm.size()));
propagateOOM(dbl->uses.append(CodeOffset(masm.size())));
}
void
@@ -109,7 +109,7 @@ MacroAssemblerX86::addConstantDouble(double d, FloatRegister dest)
if (!dbl)
return;
masm.vaddsd_mr(nullptr, dest.encoding(), dest.encoding());
dbl->uses.append(CodeOffset(masm.size()));
propagateOOM(dbl->uses.append(CodeOffset(masm.size())));
}
void
@@ -121,7 +121,7 @@ MacroAssemblerX86::loadConstantFloat32(float f, FloatRegister dest)
if (!flt)
return;
masm.vmovss_mr(nullptr, dest.encoding());
flt->uses.append(CodeOffset(masm.size()));
propagateOOM(flt->uses.append(CodeOffset(masm.size())));
}
void
@@ -131,7 +131,7 @@ MacroAssemblerX86::addConstantFloat32(float f, FloatRegister dest)
if (!flt)
return;
masm.vaddss_mr(nullptr, dest.encoding(), dest.encoding());
flt->uses.append(CodeOffset(masm.size()));
propagateOOM(flt->uses.append(CodeOffset(masm.size())));
}
void
@@ -145,7 +145,7 @@ MacroAssemblerX86::loadConstantInt32x4(const SimdConstant& v, FloatRegister dest
return;
MOZ_ASSERT(i4->type() == SimdConstant::Int32x4);
masm.vmovdqa_mr(nullptr, dest.encoding());
i4->uses.append(CodeOffset(masm.size()));
propagateOOM(i4->uses.append(CodeOffset(masm.size())));
}
void
@@ -159,7 +159,7 @@ MacroAssemblerX86::loadConstantFloat32x4(const SimdConstant& v, FloatRegister de
return;
MOZ_ASSERT(f4->type() == SimdConstant::Float32x4);
masm.vmovaps_mr(nullptr, dest.encoding());
f4->uses.append(CodeOffset(masm.size()));
propagateOOM(f4->uses.append(CodeOffset(masm.size())));
}
void
+3 -1
View File
@@ -105,7 +105,6 @@ MSG_DEF(JSMSG_INVALID_ARG_TYPE, 3, JSEXN_TYPEERR, "Invalid type: {0} can'
MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS, 1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors")
MSG_DEF(JSMSG_UNINITIALIZED_THIS, 1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
MSG_DEF(JSMSG_BAD_DERIVED_RETURN, 1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
@@ -270,6 +269,9 @@ 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'")
+1
View File
@@ -69,6 +69,7 @@ enum State {
D(JSFunction*) \
D(ModuleObject*) \
D(ModuleEnvironmentObject*) \
D(ModuleNamespaceObject*) \
D(NestedScopeObject*) \
D(PlainObject*) \
D(SavedFrame*) \
+2 -1
View File
@@ -2232,11 +2232,12 @@ js::LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject s
// See note above RuntimeLexicalErrorObject.
if (pobj == scope) {
if (IsUninitializedLexicalSlot(scope, shape)) {
if (name != cx->names().dotThis && IsUninitializedLexicalSlot(scope, shape)) {
scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_UNINITIALIZED_LEXICAL);
if (!scope)
return false;
} else if (scope->is<ScopeObject>() && !scope->is<DeclEnvObject>() && !shape->writable()) {
MOZ_ASSERT(name != cx->names().dotThis);
scope = RuntimeLexicalErrorObject::create(cx, scope, JSMSG_BAD_CONST_ASSIGN);
if (!scope)
return false;
-6
View File
@@ -4,12 +4,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
ifeq ($(OS_ARCH),Darwin)
ifeq ($(TARGET_CPU),x86_64)
DARWIN_EXE_LDFLAGS += -pagezero_size 10000 -image_base 100000000
endif
endif
ifdef QEMU_EXE
MOZ_POST_PROGRAM_COMMAND = $(topsrcdir)/build/qemu-wrap --qemu $(QEMU_EXE) --libdir $(CROSS_LIB)
endif
+2 -2
View File
@@ -3314,8 +3314,8 @@ ParseModule(JSContext* cx, unsigned argc, Value* vp)
CompileOptions options(cx);
if (args.length() > 1) {
if (!args[1].isString()) {
JS_ReportError(cx, "expected filename string, got %s",
JS_TypeOfValue(cx, args[1]));
const char* typeName = InformalValueTypeName(args[1]);
JS_ReportError(cx, "expected filename string, got %s", typeName);
return false;
}
@@ -19,8 +19,9 @@ 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);
@@ -28,8 +29,9 @@ 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);
@@ -39,8 +41,9 @@ 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,46 +9,41 @@ 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 any sort of statement are forbidden in strict mode code.
// Function statements within unbraced blocks are forbidden in strict mode code.
// They are allowed only under if statements in sloppy mode.
assertEq(testLenientAndStrict("if (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("while (true) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError),
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("do function f() { } while (true);",
parsesSuccessfully,
parseRaisesException(SyntaxError),
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(;;) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError),
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("for(x in []) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError),
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("with(o) function f() { }",
parsesSuccessfully,
parseRaisesException(SyntaxError),
parseRaisesException(SyntaxError)),
true);
assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
parsesSuccessfully),
true);
assertEq(testLenientAndStrict("x: function f() { }",
parsesSuccessfully,
@@ -56,7 +51,7 @@ assertEq(testLenientAndStrict("x: function f() { }",
true);
assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
parsesSuccessfully,
parseRaisesException(SyntaxError)),
parsesSuccessfully),
true);
// Lambdas are always permitted within any sort of statement.
@@ -69,7 +64,7 @@ assertEq(testLenientAndStrict("if (true) (function f() { })",
assertEq(parsesSuccessfully("function f() { function g() { } }"),
true);
// Function statements are permitted in any statement within lenient functions.
// Function statements are permitted in if statement within lenient functions.
assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"),
true);
@@ -77,8 +72,7 @@ assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; if (true) function g() { } }"),
true);
assertEq(parseRaisesException(SyntaxError)
("function f() { 'use strict'; { function g() { } } }"),
assertEq(parsesSuccessfully("function f() { 'use strict'; { function g() { } } }"),
true);
assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"),
@@ -94,7 +88,7 @@ assertEq(testLenientAndStrict("function f() { }",
true);
assertEq(testLenientAndStrict("{ function f() { } }",
completesNormally,
raisesException(SyntaxError)),
completesNormally),
true);
reportCompare(true, true);
@@ -0,0 +1,19 @@
// Make sure it doesn't matter when we make the arrow function
var test = `
new class extends class { } {
constructor() {
let arrow = () => this;
assertThrowsInstanceOf(arrow, ReferenceError);
super();
assertEq(arrow(), this);
}
}();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,18 @@
var test = `
new class extends class { } {
constructor() {
let a1 = () => this;
let a2 = (() => super());
assertThrowsInstanceOf(a1, ReferenceError);
assertEq(a2(), a1());
}
}();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,20 @@
var test = `
let arrow;
class foo extends class { } {
constructor() {
arrow = () => this;
super();
}
}
assertEq(new foo(), arrow());
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,45 @@
var test = `
let superArrow;
let thisArrow;
let thisStash;
class base {
constructor() {
// We run this constructor twice as part of the double init check
if (!thisStash)
thisStash = {prop:45};
return thisStash;
}
}
class foo extends base {
constructor() {
superArrow = (()=>super());
thisArrow = ()=>this;
}
}
// Populate the arrow function saves. Since we never invoke super(), we throw
assertThrowsInstanceOf(()=>new foo(), ReferenceError);
// No |this| binding in the closure, yet
assertThrowsInstanceOf(thisArrow, ReferenceError);
// call super()
superArrow();
// Can't call it twice
assertThrowsInstanceOf(superArrow, ReferenceError);
// Oh look, |this| is populated, now.
assertEq(thisArrow(), thisStash);
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,17 @@
var test = `
new class extends class { } {
constructor() {
super();
assertEq(this, (()=>this)());
assertEq(this, eval("this"));
}
}();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,41 @@
var test = `
new class extends class { } {
constructor() {
(()=>eval("super()"))();
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
new class extends class { } {
constructor() {
(()=>(()=>super())())();
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
new class extends class { } {
constructor() {
eval("(()=>super())()");
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
new class extends class { } {
constructor() {
eval("eval('super()')");
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -0,0 +1,25 @@
var test = `
new class extends class { } {
constructor() {
assertEq(eval("super(); this"), this);
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
new class extends class { } {
constructor() {
(()=>super())();
assertEq(this, eval("this"));
assertEq(this, (()=>this)());
}
}();
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -1,38 +0,0 @@
// |reftest| skip-if(!xulRuntime.shell)
var test = `
class base {
constructor() {
eval('');
(()=>0)();
}
}
class derived extends base {
constructor() {
eval('');
}
}
// Make sure eval and arrows are still valid in non-derived constructors.
new base();
// Eval throws in derived class constructors, in both class expressions and
// statements.
assertThrowsInstanceOf((() => new derived()), InternalError);
assertThrowsInstanceOf((() => new class extends base { constructor() { eval('') } }()), InternalError);
var g = newGlobal();
var dbg = Debugger(g);
dbg.onDebuggerStatement = function(frame) { assertThrowsInstanceOf(()=>frame.eval(''), InternalError); };
g.eval("new class foo extends null { constructor() { debugger; return {}; } }()");
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === 'function')
reportCompare(0,0,"OK");
@@ -18,26 +18,30 @@ 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();
@@ -1,21 +0,0 @@
/* -*- 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);
+15 -18
View File
@@ -12,23 +12,20 @@ var expect = '';
printBugNumber(BUGNUMBER);
printStatus (summary);
if (typeof uneval != 'undefined')
function a()
{
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);
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);

Some files were not shown because too many files have changed in this diff Show More