mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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:
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -199,7 +199,6 @@ compartment.
|
||||
* "CC_WAITING"
|
||||
* "CC_FORCED"
|
||||
* "LOAD_END"
|
||||
* "POST_COMPARTMENT"
|
||||
* "PAGE_HIDE"
|
||||
* "NSJSCONTEXT_DESTROY"
|
||||
* "SET_NEW_DOCUMENT"
|
||||
|
||||
+485
-102
@@ -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, ¬eIndex))
|
||||
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, ¬eIndex))
|
||||
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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
if (!('oomTest' in this))
|
||||
quit();
|
||||
|
||||
oomTest(() => getBacktrace({
|
||||
locals: true,
|
||||
thisprops: true
|
||||
}));
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace jit {
|
||||
_(JSOP_DELNAME) \
|
||||
_(JSOP_GETIMPORT) \
|
||||
_(JSOP_GETINTRINSIC) \
|
||||
_(JSOP_BINDVAR) \
|
||||
_(JSOP_DEFVAR) \
|
||||
_(JSOP_DEFCONST) \
|
||||
_(JSOP_DEFLET) \
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -162,6 +162,7 @@ namespace jit {
|
||||
_(GetPropertyPolymorphic) \
|
||||
_(SetPropertyPolymorphic) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(GuardShape) \
|
||||
_(GuardReceiverPolymorphic) \
|
||||
_(GuardObjectGroup) \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
{}
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace jit {
|
||||
_(GetProp_DOMProxyShadowed) \
|
||||
_(GetProp_ArgumentsLength) \
|
||||
_(GetProp_ArgumentsCallee) \
|
||||
_(GetProp_ModuleNamespace) \
|
||||
_(GetProp_Generic) \
|
||||
\
|
||||
|
||||
|
||||
+15
-13
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(¬Osr);
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(¬Osr);
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
@@ -277,6 +277,7 @@
|
||||
_(GetPropertyPolymorphicV) \
|
||||
_(GetPropertyPolymorphicT) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(CallGetProperty) \
|
||||
_(GetNameCache) \
|
||||
_(CallGetIntrinsicValue) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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'")
|
||||
|
||||
@@ -69,6 +69,7 @@ enum State {
|
||||
D(JSFunction*) \
|
||||
D(ModuleObject*) \
|
||||
D(ModuleEnvironmentObject*) \
|
||||
D(ModuleNamespaceObject*) \
|
||||
D(NestedScopeObject*) \
|
||||
D(PlainObject*) \
|
||||
D(SavedFrame*) \
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
@@ -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
@@ -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");
|
||||
+8
-4
@@ -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);
|
||||
@@ -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
Reference in New Issue
Block a user