diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index a7faf3b98b6..5c2180c97c9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3972,9 +3972,9 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, VarEmitOption emitOptio } bool -BytecodeEmitter::emitIteratorNext(ParseNode* pn) +BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted) { - MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting, + MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " "can run user-modifiable iteration code"); @@ -5514,7 +5514,7 @@ BytecodeEmitter::emitForInOrOfVariables(ParseNode* pn) } bool -BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) +BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted) { MOZ_ASSERT(type == StmtType::FOR_OF_LOOP || type == StmtType::SPREAD); #ifdef DEBUG @@ -5623,7 +5623,7 @@ BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn) if (!emitDupAt(2)) // ITER ARR I ITER return false; } - if (!emitIteratorNext(forHead)) // ... RESULT + if (!emitIteratorNext(forHead, allowSelfHosted)) // ... RESULT return false; if (!emit1(JSOP_DUP)) // ... RESULT RESULT return false; @@ -7269,6 +7269,18 @@ BytecodeEmitter::emitSelfHostedForceInterpreter(ParseNode* pn) return true; } +bool +BytecodeEmitter::emitSelfHostedAllowContentSpread(ParseNode* pn) +{ + if (pn->pn_count != 2) { + reportError(pn, JSMSG_MORE_ARGS_NEEDED, "allowContentSpread", "1", ""); + return false; + } + + // We're just here as a sentinel. Pass the value through directly. + return emitTree(pn->pn_head->pn_next); +} + bool BytecodeEmitter::emitCallOrNew(ParseNode* pn) { @@ -7317,6 +7329,8 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return emitSelfHostedResumeGenerator(pn); if (pn2->name() == cx->names().forceInterpreter) return emitSelfHostedForceInterpreter(pn); + if (pn2->name() == cx->names().allowContentSpread) + return emitSelfHostedAllowContentSpread(pn); // Fall through. } if (!emitNameOp(pn2, callop)) @@ -7932,9 +7946,9 @@ BytecodeEmitter::emitArrayComp(ParseNode* pn) } bool -BytecodeEmitter::emitSpread() +BytecodeEmitter::emitSpread(bool allowSelfHosted) { - return emitForOf(StmtType::SPREAD, nullptr); + return emitForOf(StmtType::SPREAD, nullptr, allowSelfHosted); } bool @@ -8024,11 +8038,25 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) } if (!updateSourceCoordNotes(pn2->pn_pos.begin)) return false; + + bool allowSelfHostedSpread = false; if (pn2->isKind(PNK_ELISION)) { if (!emit1(JSOP_HOLE)) return false; } else { - ParseNode* expr = pn2->isKind(PNK_SPREAD) ? pn2->pn_kid : pn2; + ParseNode* expr; + if (pn2->isKind(PNK_SPREAD)) { + expr = pn2->pn_kid; + + if (emitterMode == BytecodeEmitter::SelfHosting && + expr->isKind(PNK_CALL) && + expr->pn_head->name() == cx->names().allowContentSpread) + { + allowSelfHostedSpread = true; + } + } else { + expr = pn2; + } if (!emitTree(expr)) // ARRAY INDEX? VALUE return false; } @@ -8039,7 +8067,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) return false; if (!emit2(JSOP_PICK, 2)) // ITER ARRAY INDEX return false; - if (!emitSpread()) // ARRAY INDEX + if (!emitSpread(allowSelfHostedSpread)) // ARRAY INDEX return false; } else if (afterSpread) { if (!emit1(JSOP_INITELEM_INC)) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 8596c43153a..ffd688e6546 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -577,7 +577,7 @@ struct BytecodeEmitter // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. - bool emitIteratorNext(ParseNode* pn); + bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); // Check if the value on top of the stack is "undefined". If so, replace // that value on the stack with the value defined by |defaultExpr|. @@ -614,6 +614,7 @@ struct BytecodeEmitter bool emitSelfHostedCallFunction(ParseNode* pn); bool emitSelfHostedResumeGenerator(ParseNode* pn); bool emitSelfHostedForceInterpreter(ParseNode* pn); + bool emitSelfHostedAllowContentSpread(ParseNode* pn); bool emitComprehensionFor(ParseNode* compFor); bool emitComprehensionForIn(ParseNode* pn); @@ -643,7 +644,7 @@ struct BytecodeEmitter // |.next()| and put the results into the I-th element of array with // incrementing I, then push the result I (it will be original I + // iteration count). The stack after iteration will look like |ARRAY INDEX|. - bool emitSpread(); + bool emitSpread(bool allowSelfHosted = false); // If type is StmtType::FOR_OF_LOOP, emit bytecode for a for-of loop. // pn should be PNK_FOR, and pn->pn_left should be PNK_FOROF. @@ -653,7 +654,7 @@ struct BytecodeEmitter // // Please refer the comment above emitSpread for additional information about // stack convention. - bool emitForOf(StmtType type, ParseNode* pn); + bool emitForOf(StmtType type, ParseNode* pn, bool allowSelfHosted = false); bool emitClass(ParseNode* pn); bool emitSuperPropLHS(ParseNode* superBase, bool isCall = false); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index b6cf006254f..51f773a6c06 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -13,6 +13,7 @@ #define FOR_EACH_COMMON_PROPERTYNAME(macro) \ macro(add, add, "add") \ + macro(allowContentSpread, allowContentSpread, "allowContentSpread") \ macro(anonymous, anonymous, "anonymous") \ macro(Any, Any, "Any") \ macro(apply, apply, "apply") \