forked from mirrors/gecko-dev
Bug 1414340 part 2 - Remove array/generator comprehensions from SpiderMonkey. r=evilpie
This commit is contained in:
parent
296194e65f
commit
d6b8e1b490
42 changed files with 135 additions and 2155 deletions
|
|
@ -603,17 +603,6 @@ class NodeBuilder
|
|||
MOZ_MUST_USE bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos* pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach,
|
||||
bool isForOf, TokenPos* pos, MutableHandleValue dst);
|
||||
MOZ_MUST_USE bool comprehensionIf(HandleValue test, TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool comprehensionExpression(HandleValue body, NodeVector& blocks,
|
||||
HandleValue filter, bool isLegacy, TokenPos* pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
|
||||
bool isLegacy, TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
MOZ_MUST_USE bool metaProperty(HandleValue meta, HandleValue property, TokenPos* pos,
|
||||
MutableHandleValue dst);
|
||||
|
||||
|
|
@ -1331,85 +1320,6 @@ NodeBuilder::yieldExpression(HandleValue arg, YieldKind kind, TokenPos* pos, Mut
|
|||
return newNode(AST_YIELD_EXPR, pos, "argument", arg, "delegate", delegateVal, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos* pos,
|
||||
MutableHandleValue dst)
|
||||
{
|
||||
RootedValue isForEachVal(cx, BooleanValue(isForEach));
|
||||
RootedValue isForOfVal(cx, BooleanValue(isForOf));
|
||||
|
||||
RootedValue cb(cx, callbacks[AST_COMP_BLOCK]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, patt, src, isForEachVal, isForOfVal, pos, dst);
|
||||
|
||||
return newNode(AST_COMP_BLOCK, pos,
|
||||
"left", patt,
|
||||
"right", src,
|
||||
"each", isForEachVal,
|
||||
"of", isForOfVal,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::comprehensionIf(HandleValue test, TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_COMP_IF]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, test, pos, dst);
|
||||
|
||||
return newNode(AST_COMP_IF, pos,
|
||||
"test", test,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::comprehensionExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
|
||||
bool isLegacy, TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue array(cx);
|
||||
if (!newArray(blocks, &array))
|
||||
return false;
|
||||
|
||||
RootedValue style(cx);
|
||||
if (!atomValue(isLegacy ? "legacy" : "modern", &style))
|
||||
return false;
|
||||
|
||||
RootedValue cb(cx, callbacks[AST_COMP_EXPR]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, body, array, opt(filter), style, pos, dst);
|
||||
|
||||
return newNode(AST_COMP_EXPR, pos,
|
||||
"body", body,
|
||||
"blocks", array,
|
||||
"filter", filter,
|
||||
"style", style,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
|
||||
bool isLegacy, TokenPos* pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue array(cx);
|
||||
if (!newArray(blocks, &array))
|
||||
return false;
|
||||
|
||||
RootedValue style(cx);
|
||||
if (!atomValue(isLegacy ? "legacy" : "modern", &style))
|
||||
return false;
|
||||
|
||||
RootedValue cb(cx, callbacks[AST_GENERATOR_EXPR]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, body, array, opt(filter), style, pos, dst);
|
||||
|
||||
return newNode(AST_GENERATOR_EXPR, pos,
|
||||
"body", body,
|
||||
"blocks", array,
|
||||
"filter", filter,
|
||||
"style", style,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::importDeclaration(NodeVector& elts, HandleValue moduleSpec, TokenPos* pos,
|
||||
MutableHandleValue dst)
|
||||
|
|
@ -1810,11 +1720,6 @@ class ASTSerializer
|
|||
MutableHandleValue body, MutableHandleValue rest);
|
||||
bool functionBody(ParseNode* pn, TokenPos* pos, MutableHandleValue dst);
|
||||
|
||||
bool comprehensionBlock(ParseNode* pn, MutableHandleValue dst);
|
||||
bool comprehensionIf(ParseNode* pn, MutableHandleValue dst);
|
||||
bool comprehension(ParseNode* pn, MutableHandleValue dst);
|
||||
bool generatorExpression(ParseNode* pn, MutableHandleValue dst);
|
||||
|
||||
public:
|
||||
ASTSerializer(JSContext* c, bool l, char const* src, uint32_t ln)
|
||||
: cx(c)
|
||||
|
|
@ -2455,7 +2360,6 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
|||
}
|
||||
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
{
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
|
||||
|
|
@ -2685,129 +2589,6 @@ ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::comprehensionBlock(ParseNode* pn, MutableHandleValue dst)
|
||||
{
|
||||
LOCAL_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
||||
ParseNode* in = pn->pn_left;
|
||||
|
||||
LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF)));
|
||||
|
||||
bool isForEach = in->isKind(PNK_FORIN) && (pn->pn_iflags & JSITER_FOREACH);
|
||||
bool isForOf = in->isKind(PNK_FOROF);
|
||||
|
||||
ParseNode* decl = in->pn_kid1;
|
||||
if (decl->isKind(PNK_LEXICALSCOPE))
|
||||
decl = decl->pn_expr;
|
||||
MOZ_ASSERT(decl->pn_count == 1);
|
||||
|
||||
RootedValue patt(cx), src(cx);
|
||||
return pattern(decl->pn_head, &patt) &&
|
||||
expression(in->pn_kid3, &src) &&
|
||||
builder.comprehensionBlock(patt, src, isForEach, isForOf, &in->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::comprehensionIf(ParseNode* pn, MutableHandleValue dst)
|
||||
{
|
||||
LOCAL_ASSERT(pn->isKind(PNK_IF));
|
||||
LOCAL_ASSERT(!pn->pn_kid3);
|
||||
|
||||
RootedValue patt(cx);
|
||||
return pattern(pn->pn_kid1, &patt) &&
|
||||
builder.comprehensionIf(patt, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::comprehension(ParseNode* pn, MutableHandleValue dst)
|
||||
{
|
||||
// There are two array comprehension flavors.
|
||||
// 1. The kind that was in ES4 for a while: [z for (x in y)]
|
||||
// 2. The kind that was in ES6 for a while: [for (x of y) z]
|
||||
// They have slightly different parse trees and scoping.
|
||||
bool isLegacy = pn->isKind(PNK_LEXICALSCOPE);
|
||||
ParseNode* next = isLegacy ? pn->pn_expr : pn;
|
||||
LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR));
|
||||
|
||||
NodeVector blocks(cx);
|
||||
RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE));
|
||||
while (true) {
|
||||
if (next->isKind(PNK_COMPREHENSIONFOR)) {
|
||||
RootedValue block(cx);
|
||||
if (!comprehensionBlock(next, &block) || !blocks.append(block))
|
||||
return false;
|
||||
next = next->pn_right;
|
||||
} else if (next->isKind(PNK_IF)) {
|
||||
if (isLegacy) {
|
||||
MOZ_ASSERT(filter.isMagic(JS_SERIALIZE_NO_NODE));
|
||||
if (!optExpression(next->pn_kid1, &filter))
|
||||
return false;
|
||||
} else {
|
||||
// ES7 comprehension can contain multiple ComprehensionIfs.
|
||||
RootedValue compif(cx);
|
||||
if (!comprehensionIf(next, &compif) || !blocks.append(compif))
|
||||
return false;
|
||||
}
|
||||
next = next->pn_kid2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL_ASSERT(next->isKind(PNK_ARRAYPUSH));
|
||||
|
||||
RootedValue body(cx);
|
||||
|
||||
return expression(next->pn_kid, &body) &&
|
||||
builder.comprehensionExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst)
|
||||
{
|
||||
// Just as there are two kinds of array comprehension (see
|
||||
// ASTSerializer::comprehension), there are legacy and modern generator
|
||||
// expression.
|
||||
bool isLegacy = pn->isKind(PNK_LEXICALSCOPE);
|
||||
ParseNode* next = isLegacy ? pn->pn_expr : pn;
|
||||
LOCAL_ASSERT(next->isKind(PNK_COMPREHENSIONFOR));
|
||||
|
||||
NodeVector blocks(cx);
|
||||
RootedValue filter(cx, MagicValue(JS_SERIALIZE_NO_NODE));
|
||||
while (true) {
|
||||
if (next->isKind(PNK_COMPREHENSIONFOR)) {
|
||||
RootedValue block(cx);
|
||||
if (!comprehensionBlock(next, &block) || !blocks.append(block))
|
||||
return false;
|
||||
next = next->pn_right;
|
||||
} else if (next->isKind(PNK_IF)) {
|
||||
if (isLegacy) {
|
||||
MOZ_ASSERT(filter.isMagic(JS_SERIALIZE_NO_NODE));
|
||||
if (!optExpression(next->pn_kid1, &filter))
|
||||
return false;
|
||||
} else {
|
||||
// ES7 comprehension can contain multiple ComprehensionIfs.
|
||||
RootedValue compif(cx);
|
||||
if (!comprehensionIf(next, &compif) || !blocks.append(compif))
|
||||
return false;
|
||||
}
|
||||
next = next->pn_kid2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL_ASSERT(next->isKind(PNK_SEMI) &&
|
||||
next->pn_kid->isKind(PNK_YIELD) &&
|
||||
next->pn_kid->pn_kid);
|
||||
|
||||
RootedValue body(cx);
|
||||
|
||||
return expression(next->pn_kid->pn_kid, &body) &&
|
||||
builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
||||
{
|
||||
|
|
@ -2943,9 +2724,6 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
builder.unaryExpression(op, expr, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_GENEXP:
|
||||
return generatorExpression(pn->generatorExpr(), dst);
|
||||
|
||||
case PNK_NEW:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
case PNK_CALL:
|
||||
|
|
@ -3165,13 +2943,6 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos));
|
||||
|
||||
/* NB: it's no longer the case that pn_count could be 2. */
|
||||
LOCAL_ASSERT(pn->pn_count == 1);
|
||||
return comprehension(pn->pn_head, dst);
|
||||
|
||||
case PNK_CLASS:
|
||||
return classDefinition(pn, true, dst);
|
||||
|
||||
|
|
|
|||
|
|
@ -2195,7 +2195,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
|
|||
maxFixedSlots(0),
|
||||
maxStackDepth(0),
|
||||
stackDepth(0),
|
||||
arrayCompDepth(0),
|
||||
emitLevel(0),
|
||||
bodyScopeIndex(UINT32_MAX),
|
||||
varEmitterScope(nullptr),
|
||||
|
|
@ -3177,7 +3176,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
// and an array comprehension containing no other effectful operations
|
||||
// only produce a value, without affecting anything else.
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_ARRAYPUSH:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
return checkSideEffects(pn->pn_kid, answer);
|
||||
|
||||
|
|
@ -3348,7 +3346,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
case PNK_DOWHILE:
|
||||
case PNK_WHILE:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
*answer = true;
|
||||
return true;
|
||||
|
|
@ -3441,12 +3438,6 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
*answer = false;
|
||||
return true;
|
||||
|
||||
// Generator expressions have no side effects on their own.
|
||||
case PNK_GENEXP:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
*answer = false;
|
||||
return true;
|
||||
|
||||
case PNK_TRY:
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
if (!checkSideEffects(pn->pn_kid1, answer))
|
||||
|
|
@ -3506,19 +3497,14 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
*answer = pn->pn_count > 1;
|
||||
return true;
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pn->pn_count == 1);
|
||||
return checkSideEffects(pn->pn_head, answer);
|
||||
|
||||
// This should be unreachable but is left as-is for now.
|
||||
case PNK_PARAMSBODY:
|
||||
*answer = true;
|
||||
return true;
|
||||
|
||||
case PNK_FORIN: // by PNK_FOR/PNK_COMPREHENSIONFOR
|
||||
case PNK_FOROF: // by PNK_FOR/PNK_COMPREHENSIONFOR
|
||||
case PNK_FORHEAD: // by PNK_FOR/PNK_COMPREHENSIONFOR
|
||||
case PNK_FORIN: // by PNK_FOR
|
||||
case PNK_FOROF: // by PNK_FOR
|
||||
case PNK_FORHEAD: // by PNK_FOR
|
||||
case PNK_CLASSMETHOD: // by PNK_CLASS
|
||||
case PNK_CLASSNAMES: // by PNK_CLASS
|
||||
case PNK_CLASSMETHODLIST: // by PNK_CLASS
|
||||
|
|
@ -7788,326 +7774,6 @@ BytecodeEmitter::emitFor(ParseNode* pn, EmitterScope* headLexicalEmitterScope)
|
|||
return emitForOf(pn, headLexicalEmitterScope);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope)
|
||||
{
|
||||
// ES6 specifies that lexical for-loop variables get a fresh binding each
|
||||
// iteration, and that evaluation of the expression looped over occurs with
|
||||
// these variables dead zoned. But these rules only apply to *standard*
|
||||
// for-in/of loops, and we haven't extended these requirements to
|
||||
// comprehension syntax.
|
||||
|
||||
*lexicalScope = pn->isKind(PNK_LEXICALSCOPE);
|
||||
if (*lexicalScope) {
|
||||
// This is initially-ES7-tracked syntax, now with considerably murkier
|
||||
// outlook. The scope work is done by the caller by instantiating an
|
||||
// EmitterScope. There's nothing to do here.
|
||||
} else {
|
||||
// This is legacy comprehension syntax. We'll have PNK_LET here, using
|
||||
// a lexical scope provided by/for the entire comprehension. Name
|
||||
// analysis assumes declarations initialize lets, but as we're handling
|
||||
// this declaration manually, we must also initialize manually to avoid
|
||||
// triggering dead zone checks.
|
||||
MOZ_ASSERT(pn->isKind(PNK_LET));
|
||||
MOZ_ASSERT(pn->pn_count == 1);
|
||||
|
||||
if (!emitDeclarationList(pn))
|
||||
return 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;
|
||||
|
||||
ParseNode* loopDecl = forHead->pn_kid1;
|
||||
bool lexicalScope = false;
|
||||
if (!emitComprehensionForInOrOfVariables(loopDecl, &lexicalScope))
|
||||
return false;
|
||||
|
||||
// For-of loops run with two values on the stack: the iterator and the
|
||||
// current result object.
|
||||
|
||||
// Evaluate the 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 VALUE
|
||||
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.
|
||||
TDZCheckCache tdzCache(this);
|
||||
Maybe<EmitterScope> emitterScope;
|
||||
ParseNode* loopVariableName;
|
||||
if (lexicalScope) {
|
||||
loopVariableName = parser.singleBindingFromDeclaration(loopDecl->pn_expr);
|
||||
emitterScope.emplace(this);
|
||||
if (!emitterScope->enterComprehensionFor(this, loopDecl->scopeBindings()))
|
||||
return false;
|
||||
} else {
|
||||
loopVariableName = parser.singleBindingFromDeclaration(loopDecl);
|
||||
}
|
||||
|
||||
LoopControl loopInfo(this, StatementKind::ForOfLoop);
|
||||
|
||||
// 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;
|
||||
JumpList jmp;
|
||||
if (!emitJump(JSOP_GOTO, &jmp))
|
||||
return false;
|
||||
|
||||
JumpTarget top{ -1 };
|
||||
if (!emitLoopHead(nullptr, &top))
|
||||
return false;
|
||||
|
||||
#ifdef DEBUG
|
||||
int loopDepth = this->stackDepth;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
IfThenElseEmitter ifDone(this);
|
||||
|
||||
if (!ifDone.emitIf()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Remove RESULT from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// If the iteration is done, leave loop here, instead of the branch at
|
||||
// the end of the loop.
|
||||
if (!loopInfo.emitSpecialBreakForDone(this)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
if (!ifDone.emitEnd()) // ITER RESULT
|
||||
return false;
|
||||
|
||||
// Emit code to assign result.value to the iteration variable.
|
||||
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
// Notice: Comprehension for-of doesn't perform IteratorClose, since it's
|
||||
// not in the spec.
|
||||
if (!emitAssignment(loopVariableName, PNK_ASSIGN, nullptr)) // ITER VALUE
|
||||
return false;
|
||||
|
||||
// Remove VALUE from the stack to release it.
|
||||
if (!emit1(JSOP_POP)) // ITER
|
||||
return false;
|
||||
if (!emit1(JSOP_UNDEFINED)) // ITER UNDEF
|
||||
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)) // ITER UNDEF
|
||||
return false;
|
||||
|
||||
// The stack should be balanced around the assignment opcode sequence.
|
||||
MOZ_ASSERT(this->stackDepth == loopDepth);
|
||||
|
||||
// Set offset for continues.
|
||||
loopInfo.continueTarget = { offset() };
|
||||
|
||||
if (!emitLoopEntry(forHeadExpr, jmp))
|
||||
return false;
|
||||
|
||||
if (!emit1(JSOP_FALSE)) // ITER VALUE FALSE
|
||||
return false;
|
||||
|
||||
JumpList beq;
|
||||
JumpTarget breakTarget{ -1 };
|
||||
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
|
||||
return false; // ITER VALUE
|
||||
|
||||
MOZ_ASSERT(this->stackDepth == loopDepth);
|
||||
|
||||
// Let Ion know where the closing jump of this loop is.
|
||||
if (!setSrcNoteOffset(noteIndex, 0, beq.offset - jmp.offset))
|
||||
return false;
|
||||
|
||||
if (!loopInfo.patchBreaksAndContinues(this))
|
||||
return false;
|
||||
|
||||
if (!tryNoteList.append(JSTRY_FOR_OF, stackDepth, top.offset, breakTarget.offset))
|
||||
return false;
|
||||
|
||||
if (emitterScope) {
|
||||
if (!emitterScope->leave(this))
|
||||
return false;
|
||||
emitterScope.reset();
|
||||
}
|
||||
|
||||
// Pop the result and the iter.
|
||||
return emitPopN(2); //
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitComprehensionForIn(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_COMPREHENSIONFOR));
|
||||
|
||||
ParseNode* forHead = pn->pn_left;
|
||||
MOZ_ASSERT(forHead->isKind(PNK_FORIN));
|
||||
|
||||
ParseNode* forBody = pn->pn_right;
|
||||
|
||||
ParseNode* loopDecl = forHead->pn_kid1;
|
||||
bool lexicalScope = false;
|
||||
if (loopDecl && !emitComprehensionForInOrOfVariables(loopDecl, &lexicalScope))
|
||||
return false;
|
||||
|
||||
// Evaluate the 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.
|
||||
TDZCheckCache tdzCache(this);
|
||||
Maybe<EmitterScope> emitterScope;
|
||||
if (lexicalScope) {
|
||||
emitterScope.emplace(this);
|
||||
if (!emitterScope->enterComprehensionFor(this, loopDecl->scopeBindings()))
|
||||
return false;
|
||||
}
|
||||
|
||||
LoopControl loopInfo(this, StatementKind::ForInLoop);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
JumpList jmp;
|
||||
if (!emitJump(JSOP_GOTO, &jmp))
|
||||
return false;
|
||||
|
||||
JumpTarget top{ -1 };
|
||||
if (!emitLoopHead(nullptr, &top))
|
||||
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, PNK_ASSIGN, 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 offset for continues.
|
||||
loopInfo.continueTarget = { offset() };
|
||||
|
||||
if (!emitLoopEntry(nullptr, jmp))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
if (!emit1(JSOP_MOREITER))
|
||||
return false;
|
||||
if (!emit1(JSOP_ISNOITER))
|
||||
return false;
|
||||
JumpList beq;
|
||||
JumpTarget breakTarget{ -1 };
|
||||
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
|
||||
return false;
|
||||
|
||||
/* Set the srcnote offset so we can find the closing jump. */
|
||||
if (!setSrcNoteOffset(noteIndex, 0, beq.offset - jmp.offset))
|
||||
return false;
|
||||
|
||||
if (!loopInfo.patchBreaksAndContinues(this))
|
||||
return false;
|
||||
|
||||
// Pop the enumeration value.
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
|
||||
JumpTarget endIter{ offset() };
|
||||
if (!tryNoteList.append(JSTRY_FOR_IN, this->stackDepth, top.offset, endIter.offset))
|
||||
return false;
|
||||
if (!emit1(JSOP_ENDITER))
|
||||
return false;
|
||||
|
||||
if (emitterScope) {
|
||||
if (!emitterScope->leave(this))
|
||||
return false;
|
||||
emitterScope.reset();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -10216,27 +9882,6 @@ BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitArrayComp(ParseNode* pn)
|
||||
{
|
||||
if (!emitNewInit(JSProto_Array))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Pass the new array's stack index to the PNK_ARRAYPUSH case via
|
||||
* arrayCompDepth, then simply traverse the PNK_FOR node and
|
||||
* its kids under pn2 to generate this comprehension.
|
||||
*/
|
||||
MOZ_ASSERT(stackDepth > 0);
|
||||
uint32_t saveDepth = arrayCompDepth;
|
||||
arrayCompDepth = (uint32_t) (stackDepth - 1);
|
||||
if (!emitTree(pn->pn_head))
|
||||
return false;
|
||||
arrayCompDepth = saveDepth;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitArrayLiteral(ParseNode* pn)
|
||||
{
|
||||
|
|
@ -10532,7 +10177,6 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn)
|
|||
// Left-hand sides are either simple names or destructuring patterns.
|
||||
MOZ_ASSERT(bindingElement->isKind(PNK_NAME) ||
|
||||
bindingElement->isKind(PNK_ARRAY) ||
|
||||
bindingElement->isKind(PNK_ARRAYCOMP) ||
|
||||
bindingElement->isKind(PNK_OBJECT));
|
||||
|
||||
// The rest parameter doesn't have an initializer.
|
||||
|
|
@ -11016,11 +10660,6 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
return false;
|
||||
break;
|
||||
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
if (!emitComprehensionFor(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_BREAK:
|
||||
if (!emitBreak(pn->as<BreakStatement>().label()))
|
||||
return false;
|
||||
|
|
@ -11235,7 +10874,6 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
case PNK_NEW:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
case PNK_CALL:
|
||||
case PNK_GENEXP:
|
||||
case PNK_SUPERCALL:
|
||||
if (!emitCallOrNew(pn, valueUsage))
|
||||
return false;
|
||||
|
|
@ -11280,20 +10918,6 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
MOZ_ASSERT(sc->isModuleContext());
|
||||
break;
|
||||
|
||||
case PNK_ARRAYPUSH:
|
||||
/*
|
||||
* The array object's stack index is in arrayCompDepth. See below
|
||||
* under the array initialiser code generator for array comprehension
|
||||
* special casing.
|
||||
*/
|
||||
if (!emitTree(pn->pn_kid))
|
||||
return false;
|
||||
if (!emitDupAt(this->stackDepth - 1 - arrayCompDepth))
|
||||
return false;
|
||||
if (!emit1(JSOP_ARRAYPUSH))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_CALLSITEOBJ:
|
||||
if (!emitCallSiteObject(pn))
|
||||
return false;
|
||||
|
|
@ -11304,11 +10928,6 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
return false;
|
||||
break;
|
||||
|
||||
case PNK_ARRAYCOMP:
|
||||
if (!emitArrayComp(pn))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_OBJECT:
|
||||
if (!emitObject(pn))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -224,8 +224,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
|
||||
int32_t stackDepth; /* current stack depth in script frame */
|
||||
|
||||
uint32_t arrayCompDepth; /* stack depth of array in comprehension */
|
||||
|
||||
unsigned emitLevel; /* emitTree recursion level */
|
||||
|
||||
uint32_t bodyScopeIndex; /* index into scopeList of the body scope */
|
||||
|
|
@ -555,7 +553,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
|
||||
MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count);
|
||||
MOZ_MUST_USE bool emitArrayComp(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op);
|
||||
MOZ_MUST_USE bool emitInternedObjectOp(uint32_t index, JSOp op);
|
||||
|
|
@ -798,11 +795,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor);
|
||||
MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope);
|
||||
MOZ_MUST_USE bool emitComprehensionForOf(ParseNode* pn);
|
||||
|
||||
MOZ_MUST_USE bool emitDo(ParseNode* pn);
|
||||
MOZ_MUST_USE bool emitWhile(ParseNode* pn);
|
||||
|
||||
|
|
|
|||
|
|
@ -175,22 +175,6 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Legacy array and generator comprehensions use PNK_IF to represent
|
||||
// conditions specified in the comprehension tail: for example,
|
||||
// [x for (x in obj) if (x)]. The consequent of such PNK_IF nodes is
|
||||
// either PNK_YIELD in a PNK_SEMI statement (generator comprehensions) or
|
||||
// PNK_ARRAYPUSH (array comprehensions) . The first case is consistent
|
||||
// with normal if-statement structure with consequent/alternative as
|
||||
// statements. The second case is abnormal and requires that we not
|
||||
// banish PNK_ARRAYPUSH to the unreachable list, handling it explicitly.
|
||||
//
|
||||
// We could require that this one weird PNK_ARRAYPUSH case be packaged in
|
||||
// a PNK_SEMI, for consistency. That requires careful bytecode emitter
|
||||
// adjustment that seems unwarranted for a deprecated feature.
|
||||
case PNK_ARRAYPUSH:
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
// try-statements have statements to execute, and one or both of a
|
||||
// catch-list and a finally-block.
|
||||
case PNK_TRY: {
|
||||
|
|
@ -239,8 +223,7 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_CASE:
|
||||
return ContainsHoistedDeclaration(cx, node->as<CaseClause>().statementList(), result);
|
||||
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR: {
|
||||
case PNK_FOR: {
|
||||
MOZ_ASSERT(node->isArity(PN_BINARY));
|
||||
|
||||
ParseNode* loopHead = node->pn_left;
|
||||
|
|
@ -386,8 +369,6 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_NUMBER:
|
||||
case PNK_NEW:
|
||||
case PNK_GENERATOR:
|
||||
case PNK_GENEXP:
|
||||
case PNK_ARRAYCOMP:
|
||||
case PNK_PARAMSBODY:
|
||||
case PNK_CATCHLIST:
|
||||
case PNK_CATCH:
|
||||
|
|
@ -477,8 +458,7 @@ IsEffectless(ParseNode* node)
|
|||
node->isKind(PNK_NUMBER) ||
|
||||
node->isKind(PNK_NULL) ||
|
||||
node->isKind(PNK_RAW_UNDEFINED) ||
|
||||
node->isKind(PNK_FUNCTION) ||
|
||||
node->isKind(PNK_GENEXP);
|
||||
node->isKind(PNK_FUNCTION);
|
||||
}
|
||||
|
||||
enum Truthiness { Truthy, Falsy, Unknown };
|
||||
|
|
@ -496,7 +476,6 @@ Boolish(ParseNode* pn)
|
|||
|
||||
case PNK_TRUE:
|
||||
case PNK_FUNCTION:
|
||||
case PNK_GENEXP:
|
||||
return Truthy;
|
||||
|
||||
case PNK_FALSE:
|
||||
|
|
@ -524,15 +503,13 @@ Boolish(ParseNode* pn)
|
|||
}
|
||||
|
||||
static bool
|
||||
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda);
|
||||
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser);
|
||||
|
||||
static bool
|
||||
FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
// Conditions fold like any other expression...
|
||||
if (!Fold(cx, nodePtr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, nodePtr, parser))
|
||||
return false;
|
||||
|
||||
// ...but then they sometimes can be further folded to constants.
|
||||
|
|
@ -559,14 +536,13 @@ FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char1
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid;
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
// Constant-fold the entire |typeof| if given a constant with known type.
|
||||
|
|
@ -595,14 +571,13 @@ FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid;
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
// Expression deletion evaluates the expression, then evaluates to true.
|
||||
|
|
@ -618,15 +593,14 @@ FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM));
|
||||
|
||||
ParseNode*& expr = node->pn_kid;
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
// If we're deleting an element, but constant-folding converted our
|
||||
|
|
@ -643,8 +617,7 @@ FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char1
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
|
@ -655,7 +628,7 @@ FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char
|
|||
ParseNodeKind oldKind = expr->getKind();
|
||||
#endif
|
||||
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(expr->isKind(oldKind),
|
||||
|
|
@ -665,14 +638,13 @@ FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_NOT));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid;
|
||||
if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
|
||||
if (!FoldCondition(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
if (expr->isKind(PNK_NUMBER)) {
|
||||
|
|
@ -700,15 +672,14 @@ FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& pars
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
|
||||
"need a different method for this node kind");
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid;
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
if (expr->isKind(PNK_NUMBER) || expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
|
||||
|
|
@ -734,8 +705,7 @@ FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, cha
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
|
||||
node->isKind(PNK_POSTINCREMENT) ||
|
||||
|
|
@ -746,7 +716,7 @@ FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler,
|
|||
ParseNode*& target = node->pn_kid;
|
||||
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
|
||||
|
||||
if (!Fold(cx, &target, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &target, parser))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
|
||||
|
|
@ -755,8 +725,7 @@ FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler,
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
ParseNode* node = *nodePtr;
|
||||
|
||||
|
|
@ -766,7 +735,7 @@ FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>
|
|||
bool isOrNode = node->isKind(PNK_OR);
|
||||
ParseNode** elem = &node->pn_head;
|
||||
do {
|
||||
if (!Fold(cx, elem, parser, inGenexpLambda))
|
||||
if (!Fold(cx, elem, parser))
|
||||
return false;
|
||||
|
||||
Truthiness t = Boolish(*elem);
|
||||
|
|
@ -838,8 +807,7 @@ FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
ParseNode** nextNode = nodePtr;
|
||||
|
||||
|
|
@ -855,11 +823,11 @@ FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, cha
|
|||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid1;
|
||||
if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
|
||||
if (!FoldCondition(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
ParseNode*& ifTruthy = node->pn_kid2;
|
||||
if (!Fold(cx, &ifTruthy, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &ifTruthy, parser))
|
||||
return false;
|
||||
|
||||
ParseNode*& ifFalsy = node->pn_kid3;
|
||||
|
|
@ -874,7 +842,7 @@ FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, cha
|
|||
if (ifFalsy->isKind(PNK_CONDITIONAL)) {
|
||||
nextNode = &ifFalsy;
|
||||
} else {
|
||||
if (!Fold(cx, &ifFalsy, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &ifFalsy, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -908,8 +876,7 @@ FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, cha
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
ParseNode** nextNode = nodePtr;
|
||||
|
||||
|
|
@ -924,11 +891,11 @@ FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& p
|
|||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
ParseNode*& expr = node->pn_kid1;
|
||||
if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
|
||||
if (!FoldCondition(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
ParseNode*& consequent = node->pn_kid2;
|
||||
if (!Fold(cx, &consequent, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &consequent, parser))
|
||||
return false;
|
||||
|
||||
ParseNode*& alternative = node->pn_kid3;
|
||||
|
|
@ -941,17 +908,15 @@ FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& p
|
|||
if (alternative->isKind(PNK_IF)) {
|
||||
nextNode = &alternative;
|
||||
} else {
|
||||
if (!Fold(cx, &alternative, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &alternative, parser))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminate the consequent or alternative if the condition has
|
||||
// constant truthiness. Don't eliminate if we have an |if (0)| in
|
||||
// trailing position in a generator expression, as this is a special
|
||||
// form we can't fold away.
|
||||
// constant truthiness.
|
||||
Truthiness t = Boolish(expr);
|
||||
if (t == Unknown || inGenexpLambda)
|
||||
if (t == Unknown)
|
||||
continue;
|
||||
|
||||
// Careful! Either of these can be null: |replacement| in |if (0) T;|,
|
||||
|
|
@ -1012,8 +977,7 @@ FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& p
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_FUNCTION));
|
||||
MOZ_ASSERT(node->isArity(PN_CODE));
|
||||
|
|
@ -1025,7 +989,7 @@ FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>&
|
|||
|
||||
// Note: pn_body is null for lazily-parsed functions.
|
||||
if (ParseNode*& functionBody = node->pn_body) {
|
||||
if (!Fold(cx, &functionBody, parser, node->pn_funbox->isGenexpLambda))
|
||||
if (!Fold(cx, &functionBody, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1082,12 +1046,11 @@ FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& p
|
|||
|
||||
ParseNode*& moduleBody = node->pn_body;
|
||||
MOZ_ASSERT(moduleBody);
|
||||
return Fold(cx, &moduleBody, parser, false);
|
||||
return Fold(cx, &moduleBody, parser);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_SUB) ||
|
||||
node->isKind(PNK_STAR) ||
|
||||
|
|
@ -1102,7 +1065,7 @@ FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, ch
|
|||
// Fold each operand, ideally into a number.
|
||||
ParseNode** listp = &node->pn_head;
|
||||
for (; *listp; listp = &(*listp)->pn_next) {
|
||||
if (!Fold(cx, listp, parser, inGenexpLambda))
|
||||
if (!Fold(cx, listp, parser))
|
||||
return false;
|
||||
|
||||
if (!FoldType(cx, *listp, PNK_NUMBER))
|
||||
|
|
@ -1158,8 +1121,7 @@ FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, ch
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_POW));
|
||||
MOZ_ASSERT(node->isArity(PN_LIST));
|
||||
|
|
@ -1168,7 +1130,7 @@ FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char
|
|||
// Fold each operand, ideally into a number.
|
||||
ParseNode** listp = &node->pn_head;
|
||||
for (; *listp; listp = &(*listp)->pn_next) {
|
||||
if (!Fold(cx, listp, parser, inGenexpLambda))
|
||||
if (!Fold(cx, listp, parser))
|
||||
return false;
|
||||
|
||||
if (!FoldType(cx, *listp, PNK_NUMBER))
|
||||
|
|
@ -1202,14 +1164,13 @@ FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(list->isArity(PN_LIST));
|
||||
|
||||
ParseNode** elem = &list->pn_head;
|
||||
for (; *elem; elem = &(*elem)->pn_next) {
|
||||
if (!Fold(cx, elem, parser, inGenexpLambda))
|
||||
if (!Fold(cx, elem, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1222,14 +1183,13 @@ FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& par
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_RETURN));
|
||||
MOZ_ASSERT(node->isArity(PN_UNARY));
|
||||
|
||||
if (ParseNode*& expr = node->pn_kid) {
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1237,23 +1197,22 @@ FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& p
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_TRY));
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
ParseNode*& statements = node->pn_kid1;
|
||||
if (!Fold(cx, &statements, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &statements, parser))
|
||||
return false;
|
||||
|
||||
if (ParseNode*& catchList = node->pn_kid2) {
|
||||
if (!Fold(cx, &catchList, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &catchList, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseNode*& finally = node->pn_kid3) {
|
||||
if (!Fold(cx, &finally, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &finally, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1261,23 +1220,22 @@ FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& pars
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_CATCH));
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
ParseNode*& declPattern = node->pn_kid1;
|
||||
if (!Fold(cx, &declPattern, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &declPattern, parser))
|
||||
return false;
|
||||
|
||||
if (ParseNode*& cond = node->pn_kid2) {
|
||||
if (!FoldCondition(cx, &cond, parser, inGenexpLambda))
|
||||
if (!FoldCondition(cx, &cond, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseNode*& statements = node->pn_kid3) {
|
||||
if (!Fold(cx, &statements, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &statements, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1285,29 +1243,27 @@ FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& pa
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_CLASS));
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
if (ParseNode*& classNames = node->pn_kid1) {
|
||||
if (!Fold(cx, &classNames, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &classNames, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseNode*& heritage = node->pn_kid2) {
|
||||
if (!Fold(cx, &heritage, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &heritage, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseNode*& body = node->pn_kid3;
|
||||
return Fold(cx, &body, parser, inGenexpLambda);
|
||||
return Fold(cx, &body, parser);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
ParseNode* node = *nodePtr;
|
||||
|
||||
|
|
@ -1315,11 +1271,11 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_
|
|||
MOZ_ASSERT(node->isArity(PN_BINARY));
|
||||
|
||||
ParseNode*& expr = node->pn_left;
|
||||
if (!Fold(cx, &expr, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &expr, parser))
|
||||
return false;
|
||||
|
||||
ParseNode*& key = node->pn_right;
|
||||
if (!Fold(cx, &key, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &key, parser))
|
||||
return false;
|
||||
|
||||
PropertyName* name = nullptr;
|
||||
|
|
@ -1375,8 +1331,7 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
ParseNode* node = *nodePtr;
|
||||
|
||||
|
|
@ -1385,7 +1340,7 @@ FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>&
|
|||
MOZ_ASSERT(node->pn_count >= 2);
|
||||
|
||||
// Generically fold all operands first.
|
||||
if (!FoldList(cx, node, parser, inGenexpLambda))
|
||||
if (!FoldList(cx, node, parser))
|
||||
return false;
|
||||
|
||||
// Fold leading numeric operands together:
|
||||
|
|
@ -1526,8 +1481,7 @@ FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>&
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
|
||||
node->isKind(PNK_TAGGED_TEMPLATE));
|
||||
|
|
@ -1549,7 +1503,7 @@ FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& par
|
|||
listp = &(*listp)->pn_next;
|
||||
|
||||
for (; *listp; listp = &(*listp)->pn_next) {
|
||||
if (!Fold(cx, listp, parser, inGenexpLambda))
|
||||
if (!Fold(cx, listp, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1561,31 +1515,29 @@ FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& par
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
MOZ_ASSERT(!node->pn_kid2);
|
||||
|
||||
return Fold(cx, &node->pn_kid1, parser, inGenexpLambda) &&
|
||||
Fold(cx, &node->pn_kid3, parser, inGenexpLambda);
|
||||
return Fold(cx, &node->pn_kid1, parser) &&
|
||||
Fold(cx, &node->pn_kid3, parser);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_FORHEAD));
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
|
||||
if (ParseNode*& init = node->pn_kid1) {
|
||||
if (!Fold(cx, &init, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &init, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ParseNode*& test = node->pn_kid2) {
|
||||
if (!FoldCondition(cx, &test, parser, inGenexpLambda))
|
||||
if (!FoldCondition(cx, &test, parser))
|
||||
return false;
|
||||
|
||||
if (test->isKind(PNK_TRUE)) {
|
||||
|
|
@ -1595,7 +1547,7 @@ FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>&
|
|||
}
|
||||
|
||||
if (ParseNode*& update = node->pn_kid3) {
|
||||
if (!Fold(cx, &update, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &update, parser))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1603,8 +1555,7 @@ FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>&
|
|||
}
|
||||
|
||||
static bool
|
||||
FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_DOT));
|
||||
MOZ_ASSERT(node->isArity(PN_NAME));
|
||||
|
|
@ -1617,12 +1568,11 @@ FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char
|
|||
nested = &(*nested)->pn_expr;
|
||||
}
|
||||
|
||||
return Fold(cx, nested, parser, inGenexpLambda);
|
||||
return Fold(cx, nested, parser);
|
||||
}
|
||||
|
||||
static bool
|
||||
FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
MOZ_ASSERT(node->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(node->isArity(PN_NAME));
|
||||
|
|
@ -1630,12 +1580,11 @@ FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& par
|
|||
if (!node->pn_expr)
|
||||
return true;
|
||||
|
||||
return Fold(cx, &node->pn_expr, parser, inGenexpLambda);
|
||||
return Fold(cx, &node->pn_expr, parser);
|
||||
}
|
||||
|
||||
bool
|
||||
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
||||
bool inGenexpLambda)
|
||||
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser)
|
||||
{
|
||||
if (!CheckRecursionLimit(cx))
|
||||
return false;
|
||||
|
|
@ -1671,7 +1620,7 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
return true;
|
||||
|
||||
case PNK_TYPEOFEXPR:
|
||||
return FoldTypeOfExpr(cx, pn, parser, inGenexpLambda);
|
||||
return FoldTypeOfExpr(cx, pn, parser);
|
||||
|
||||
case PNK_DELETENAME: {
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
|
|
@ -1680,53 +1629,52 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
}
|
||||
|
||||
case PNK_DELETEEXPR:
|
||||
return FoldDeleteExpr(cx, pn, parser, inGenexpLambda);
|
||||
return FoldDeleteExpr(cx, pn, parser);
|
||||
|
||||
case PNK_DELETEELEM:
|
||||
return FoldDeleteElement(cx, pn, parser, inGenexpLambda);
|
||||
return FoldDeleteElement(cx, pn, parser);
|
||||
|
||||
case PNK_DELETEPROP:
|
||||
return FoldDeleteProperty(cx, pn, parser, inGenexpLambda);
|
||||
return FoldDeleteProperty(cx, pn, parser);
|
||||
|
||||
case PNK_CONDITIONAL:
|
||||
return FoldConditional(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldConditional(cx, pnp, parser);
|
||||
|
||||
case PNK_IF:
|
||||
return FoldIf(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldIf(cx, pnp, parser);
|
||||
|
||||
case PNK_NOT:
|
||||
return FoldNot(cx, pn, parser, inGenexpLambda);
|
||||
return FoldNot(cx, pn, parser);
|
||||
|
||||
case PNK_BITNOT:
|
||||
case PNK_POS:
|
||||
case PNK_NEG:
|
||||
return FoldUnaryArithmetic(cx, pn, parser, inGenexpLambda);
|
||||
return FoldUnaryArithmetic(cx, pn, parser);
|
||||
|
||||
case PNK_PREINCREMENT:
|
||||
case PNK_POSTINCREMENT:
|
||||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
return FoldIncrementDecrement(cx, pn, parser, inGenexpLambda);
|
||||
return FoldIncrementDecrement(cx, pn, parser);
|
||||
|
||||
case PNK_THROW:
|
||||
case PNK_ARRAYPUSH:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_COMPUTED_NAME:
|
||||
case PNK_SPREAD:
|
||||
case PNK_EXPORT:
|
||||
case PNK_VOID:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_kid, parser);
|
||||
|
||||
case PNK_EXPORT_DEFAULT:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_left, parser);
|
||||
|
||||
case PNK_SEMI:
|
||||
case PNK_THIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
if (ParseNode*& expr = pn->pn_kid)
|
||||
return Fold(cx, &expr, parser, inGenexpLambda);
|
||||
return Fold(cx, &expr, parser);
|
||||
return true;
|
||||
|
||||
case PNK_PIPELINE:
|
||||
|
|
@ -1734,10 +1682,10 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
|
||||
case PNK_AND:
|
||||
case PNK_OR:
|
||||
return FoldAndOr(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldAndOr(cx, pnp, parser);
|
||||
|
||||
case PNK_FUNCTION:
|
||||
return FoldFunction(cx, pn, parser, inGenexpLambda);
|
||||
return FoldFunction(cx, pn, parser);
|
||||
|
||||
case PNK_MODULE:
|
||||
return FoldModule(cx, pn, parser);
|
||||
|
|
@ -1749,10 +1697,10 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
case PNK_URSH:
|
||||
case PNK_DIV:
|
||||
case PNK_MOD:
|
||||
return FoldBinaryArithmetic(cx, pn, parser, inGenexpLambda);
|
||||
return FoldBinaryArithmetic(cx, pn, parser);
|
||||
|
||||
case PNK_POW:
|
||||
return FoldExponentiation(cx, pn, parser, inGenexpLambda);
|
||||
return FoldExponentiation(cx, pn, parser);
|
||||
|
||||
// Various list nodes not requiring care to minimally fold. Some of
|
||||
// these could be further folded/optimized, but we don't make the effort.
|
||||
|
|
@ -1773,7 +1721,6 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
case PNK_NEW:
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
case PNK_ARRAYCOMP:
|
||||
case PNK_STATEMENTLIST:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CATCHLIST:
|
||||
|
|
@ -1785,8 +1732,7 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
case PNK_CALLSITEOBJ:
|
||||
case PNK_EXPORT_SPEC_LIST:
|
||||
case PNK_IMPORT_SPEC_LIST:
|
||||
case PNK_GENEXP:
|
||||
return FoldList(cx, pn, parser, inGenexpLambda);
|
||||
return FoldList(cx, pn, parser);
|
||||
|
||||
case PNK_INITIALYIELD:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
|
|
@ -1797,37 +1743,37 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
|
||||
case PNK_YIELD_STAR:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_kid, parser);
|
||||
|
||||
case PNK_YIELD:
|
||||
case PNK_AWAIT:
|
||||
MOZ_ASSERT(pn->isArity(PN_UNARY));
|
||||
if (!pn->pn_kid)
|
||||
return true;
|
||||
return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_kid, parser);
|
||||
|
||||
case PNK_RETURN:
|
||||
return FoldReturn(cx, pn, parser, inGenexpLambda);
|
||||
return FoldReturn(cx, pn, parser);
|
||||
|
||||
case PNK_TRY:
|
||||
return FoldTry(cx, pn, parser, inGenexpLambda);
|
||||
return FoldTry(cx, pn, parser);
|
||||
|
||||
case PNK_CATCH:
|
||||
return FoldCatch(cx, pn, parser, inGenexpLambda);
|
||||
return FoldCatch(cx, pn, parser);
|
||||
|
||||
case PNK_CLASS:
|
||||
return FoldClass(cx, pn, parser, inGenexpLambda);
|
||||
return FoldClass(cx, pn, parser);
|
||||
|
||||
case PNK_ELEM:
|
||||
return FoldElement(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldElement(cx, pnp, parser);
|
||||
|
||||
case PNK_ADD:
|
||||
return FoldAdd(cx, pnp, parser, inGenexpLambda);
|
||||
return FoldAdd(cx, pnp, parser);
|
||||
|
||||
case PNK_CALL:
|
||||
case PNK_SUPERCALL:
|
||||
case PNK_TAGGED_TEMPLATE:
|
||||
return FoldCall(cx, pn, parser, inGenexpLambda);
|
||||
return FoldCall(cx, pn, parser);
|
||||
|
||||
case PNK_SWITCH:
|
||||
case PNK_COLON:
|
||||
|
|
@ -1848,14 +1794,13 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
case PNK_EXPORT_FROM:
|
||||
case PNK_SHORTHAND:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_IMPORT_SPEC:
|
||||
case PNK_EXPORT_SPEC:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
|
||||
Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_left, parser) &&
|
||||
Fold(cx, &pn->pn_right, parser);
|
||||
|
||||
case PNK_NEWTARGET:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
|
@ -1866,59 +1811,59 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
|
|||
case PNK_CLASSNAMES:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
if (ParseNode*& outerBinding = pn->pn_left) {
|
||||
if (!Fold(cx, &outerBinding, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &outerBinding, parser))
|
||||
return false;
|
||||
}
|
||||
return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_right, parser);
|
||||
|
||||
case PNK_DOWHILE:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
|
||||
FoldCondition(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_left, parser) &&
|
||||
FoldCondition(cx, &pn->pn_right, parser);
|
||||
|
||||
case PNK_WHILE:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return FoldCondition(cx, &pn->pn_left, parser, inGenexpLambda) &&
|
||||
Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return FoldCondition(cx, &pn->pn_left, parser) &&
|
||||
Fold(cx, &pn->pn_right, parser);
|
||||
|
||||
case PNK_CASE: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
|
||||
// pn_left is null for DefaultClauses.
|
||||
if (pn->pn_left) {
|
||||
if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda))
|
||||
if (!Fold(cx, &pn->pn_left, parser))
|
||||
return false;
|
||||
}
|
||||
return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_right, parser);
|
||||
}
|
||||
|
||||
case PNK_WITH:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
|
||||
Fold(cx, &pn->pn_right, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_left, parser) &&
|
||||
Fold(cx, &pn->pn_right, parser);
|
||||
|
||||
case PNK_FORIN:
|
||||
case PNK_FOROF:
|
||||
return FoldForInOrOf(cx, pn, parser, inGenexpLambda);
|
||||
return FoldForInOrOf(cx, pn, parser);
|
||||
|
||||
case PNK_FORHEAD:
|
||||
return FoldForHead(cx, pn, parser, inGenexpLambda);
|
||||
return FoldForHead(cx, pn, parser);
|
||||
|
||||
case PNK_LABEL:
|
||||
MOZ_ASSERT(pn->isArity(PN_NAME));
|
||||
return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_expr, parser);
|
||||
|
||||
case PNK_DOT:
|
||||
return FoldDottedProperty(cx, pn, parser, inGenexpLambda);
|
||||
return FoldDottedProperty(cx, pn, parser);
|
||||
|
||||
case PNK_LEXICALSCOPE:
|
||||
MOZ_ASSERT(pn->isArity(PN_SCOPE));
|
||||
if (!pn->scopeBody())
|
||||
return true;
|
||||
return Fold(cx, &pn->pn_u.scope.body, parser, inGenexpLambda);
|
||||
return Fold(cx, &pn->pn_u.scope.body, parser);
|
||||
|
||||
case PNK_NAME:
|
||||
return FoldName(cx, pn, parser, inGenexpLambda);
|
||||
return FoldName(cx, pn, parser);
|
||||
|
||||
case PNK_LIMIT: // invalid sentinel value
|
||||
MOZ_CRASH("invalid node kind");
|
||||
|
|
@ -1938,7 +1883,7 @@ frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler,
|
|||
return true;
|
||||
|
||||
AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants);
|
||||
return Fold(cx, pnp, *parser, false);
|
||||
return Fold(cx, pnp, *parser);
|
||||
}
|
||||
|
||||
template bool
|
||||
|
|
|
|||
|
|
@ -212,11 +212,6 @@ class FullParseHandler
|
|||
return new_<UnaryNode>(PNK_SPREAD, pos, kid);
|
||||
}
|
||||
|
||||
ParseNode* newArrayPush(uint32_t begin, ParseNode* kid) {
|
||||
TokenPos pos(begin, kid->pn_pos.end);
|
||||
return new_<UnaryNode>(PNK_ARRAYPUSH, pos, kid);
|
||||
}
|
||||
|
||||
private:
|
||||
ParseNode* newBinary(ParseNodeKind kind, ParseNode* left, ParseNode* right,
|
||||
JSOp op = JSOP_NOP)
|
||||
|
|
@ -234,26 +229,6 @@ class FullParseHandler
|
|||
|
||||
// Expressions
|
||||
|
||||
ParseNode* newGeneratorComprehension(ParseNode* genfn, const TokenPos& pos) {
|
||||
MOZ_ASSERT(pos.begin <= genfn->pn_pos.begin);
|
||||
MOZ_ASSERT(genfn->pn_pos.end <= pos.end);
|
||||
ParseNode* result = new_<ListNode>(PNK_GENEXP, JSOP_CALL, pos);
|
||||
if (!result)
|
||||
return null();
|
||||
result->append(genfn);
|
||||
return result;
|
||||
}
|
||||
|
||||
ParseNode* newArrayComprehension(ParseNode* body, const TokenPos& pos) {
|
||||
MOZ_ASSERT(pos.begin <= body->pn_pos.begin);
|
||||
MOZ_ASSERT(body->pn_pos.end <= pos.end);
|
||||
ParseNode* pn = new_<ListNode>(PNK_ARRAYCOMP, pos);
|
||||
if (!pn)
|
||||
return nullptr;
|
||||
pn->append(body);
|
||||
return pn;
|
||||
}
|
||||
|
||||
ParseNode* newArrayLiteral(uint32_t begin) {
|
||||
return new_<ListNode>(PNK_ARRAY, TokenPos(begin, begin + 1));
|
||||
}
|
||||
|
|
@ -577,24 +552,6 @@ class FullParseHandler
|
|||
return pn;
|
||||
}
|
||||
|
||||
ParseNode* newComprehensionFor(uint32_t begin, ParseNode* forHead, ParseNode* body) {
|
||||
// A PNK_COMPREHENSIONFOR node is binary: left is loop control, right
|
||||
// is the body.
|
||||
MOZ_ASSERT(forHead->isKind(PNK_FORIN) || forHead->isKind(PNK_FOROF));
|
||||
JSOp op = forHead->isKind(PNK_FORIN) ? JSOP_ITER : JSOP_NOP;
|
||||
BinaryNode* pn = new_<BinaryNode>(PNK_COMPREHENSIONFOR, op,
|
||||
TokenPos(begin, body->pn_pos.end), forHead, body);
|
||||
if (!pn)
|
||||
return null();
|
||||
pn->pn_iflags = JSOP_ITER;
|
||||
return pn;
|
||||
}
|
||||
|
||||
ParseNode* newComprehensionBinding(ParseNode* kid) {
|
||||
MOZ_ASSERT(kid->isKind(PNK_NAME));
|
||||
return new_<ListNode>(PNK_LET, JSOP_NOP, kid);
|
||||
}
|
||||
|
||||
ParseNode* newForHead(ParseNode* init, ParseNode* test, ParseNode* update,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
|
|
@ -691,14 +648,6 @@ class FullParseHandler
|
|||
return new_<CodeNode>(PNK_FUNCTION, JSOP_LAMBDA_ARROW, pos);
|
||||
}
|
||||
|
||||
bool setComprehensionLambdaBody(ParseNode* pn, ParseNode* body) {
|
||||
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
|
||||
ParseNode* paramsBody = newList(PNK_PARAMSBODY, body);
|
||||
if (!paramsBody)
|
||||
return false;
|
||||
setFunctionFormalParametersAndBody(pn, paramsBody);
|
||||
return true;
|
||||
}
|
||||
void setFunctionFormalParametersAndBody(ParseNode* pn, ParseNode* kid) {
|
||||
MOZ_ASSERT_IF(kid, kid->isKind(PNK_PARAMSBODY));
|
||||
pn->pn_body = kid;
|
||||
|
|
|
|||
|
|
@ -427,7 +427,6 @@ class NameResolver
|
|||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
case PNK_COMPUTED_NAME:
|
||||
case PNK_ARRAYPUSH:
|
||||
case PNK_SPREAD:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_EXPORT:
|
||||
|
|
@ -466,7 +465,6 @@ class NameResolver
|
|||
case PNK_WHILE:
|
||||
case PNK_SWITCH:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_SETTHIS:
|
||||
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||
|
|
@ -692,7 +690,6 @@ class NameResolver
|
|||
case PNK_NEW:
|
||||
case PNK_CALL:
|
||||
case PNK_SUPERCALL:
|
||||
case PNK_GENEXP:
|
||||
case PNK_ARRAY:
|
||||
case PNK_STATEMENTLIST:
|
||||
case PNK_PARAMSBODY:
|
||||
|
|
@ -708,19 +705,6 @@ class NameResolver
|
|||
}
|
||||
break;
|
||||
|
||||
// Array comprehension nodes are lists with a single child:
|
||||
// PNK_COMPREHENSIONFOR for comprehensions, PNK_LEXICALSCOPE for
|
||||
// legacy comprehensions. Probably this should be a non-list
|
||||
// eventually.
|
||||
case PNK_ARRAYCOMP:
|
||||
MOZ_ASSERT(cur->isArity(PN_LIST));
|
||||
MOZ_ASSERT(cur->pn_count == 1);
|
||||
MOZ_ASSERT(cur->pn_head->isKind(PNK_LEXICALSCOPE) ||
|
||||
cur->pn_head->isKind(PNK_COMPREHENSIONFOR));
|
||||
if (!resolve(cur->pn_head, prefix))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_OBJECT:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
MOZ_ASSERT(cur->isArity(PN_LIST));
|
||||
|
|
|
|||
|
|
@ -221,7 +221,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_PREDECREMENT:
|
||||
case PNK_POSTDECREMENT:
|
||||
case PNK_COMPUTED_NAME:
|
||||
case PNK_ARRAYPUSH:
|
||||
case PNK_SPREAD:
|
||||
case PNK_MUTATEPROTO:
|
||||
case PNK_EXPORT:
|
||||
|
|
@ -266,7 +265,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_NEWTARGET:
|
||||
case PNK_SETTHIS:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
case PNK_WITH: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
stack->push(pn->pn_left);
|
||||
|
|
@ -452,7 +450,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_NEW:
|
||||
case PNK_CALL:
|
||||
case PNK_SUPERCALL:
|
||||
case PNK_GENEXP:
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
case PNK_TEMPLATE_STRING_LIST:
|
||||
|
|
@ -469,20 +466,6 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_CLASSMETHODLIST:
|
||||
return PushListNodeChildren(pn, stack);
|
||||
|
||||
// Array comprehension nodes are lists with a single child:
|
||||
// PNK_COMPREHENSIONFOR for comprehensions, PNK_LEXICALSCOPE for legacy
|
||||
// comprehensions. Probably this should be a non-list eventually.
|
||||
case PNK_ARRAYCOMP: {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(pn->isKind(PNK_ARRAYCOMP));
|
||||
MOZ_ASSERT(pn->isArity(PN_LIST));
|
||||
MOZ_ASSERT(pn->pn_count == 1);
|
||||
MOZ_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE) ||
|
||||
pn->pn_head->isKind(PNK_COMPREHENSIONFOR));
|
||||
#endif
|
||||
return PushListNodeChildren(pn, stack);
|
||||
}
|
||||
|
||||
case PNK_LABEL:
|
||||
case PNK_DOT:
|
||||
case PNK_NAME:
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ class ObjectBox;
|
|||
F(WHILE) \
|
||||
F(DOWHILE) \
|
||||
F(FOR) \
|
||||
F(COMPREHENSIONFOR) \
|
||||
F(BREAK) \
|
||||
F(CONTINUE) \
|
||||
F(VAR) \
|
||||
|
|
@ -87,9 +86,6 @@ class ObjectBox;
|
|||
F(INITIALYIELD) \
|
||||
F(YIELD) \
|
||||
F(YIELD_STAR) \
|
||||
F(GENEXP) \
|
||||
F(ARRAYCOMP) \
|
||||
F(ARRAYPUSH) \
|
||||
F(LEXICALSCOPE) \
|
||||
F(LET) \
|
||||
F(IMPORT) \
|
||||
|
|
@ -237,10 +233,6 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* <Statements>
|
||||
* PNK_STATEMENTLIST list pn_head: list of pn_count statements
|
||||
* PNK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
|
||||
* In body of a comprehension or desugared generator
|
||||
* expression, pn_kid2 is PNK_YIELD, PNK_ARRAYPUSH,
|
||||
* or (if the push was optimized away) empty
|
||||
* PNK_STATEMENTLIST.
|
||||
* PNK_SWITCH binary pn_left: discriminant
|
||||
* pn_right: list of PNK_CASE nodes, with at most one
|
||||
* default node, or if there are let bindings
|
||||
|
|
@ -257,8 +249,6 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_FOR binary pn_left: either PNK_FORIN (for-in statement),
|
||||
* PNK_FOROF (for-of) or PNK_FORHEAD (for(;;))
|
||||
* pn_right: body
|
||||
* PNK_COMPREHENSIONFOR pn_left: either PNK_FORIN or PNK_FOROF
|
||||
* binary pn_right: body
|
||||
* PNK_FORIN ternary pn_kid1: declaration or expression to left of 'in'
|
||||
* pn_kid2: null
|
||||
* pn_kid3: object expr to right of 'in'
|
||||
|
|
@ -380,8 +370,6 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_CALL list pn_head: list of call, arg1, arg2, ... argN
|
||||
* pn_count: 1 + N (where N is number of args)
|
||||
* call is a MEMBER expr naming a callable object
|
||||
* PNK_GENEXP list Exactly like PNK_CALL, used for the implicit call
|
||||
* in the desugaring of a generator-expression.
|
||||
* PNK_ARRAY list pn_head: list of pn_count array element exprs
|
||||
* [,,] holes are represented by PNK_ELISION nodes
|
||||
* pn_xflags: PN_ENDCOMMA if extra comma at end
|
||||
|
|
@ -424,11 +412,6 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_YIELD, unary pn_kid: expr or null
|
||||
* PNK_YIELD_STAR,
|
||||
* PNK_AWAIT
|
||||
* PNK_ARRAYCOMP list pn_count: 1
|
||||
* pn_head: list of 1 element, which is block
|
||||
* enclosing for loop(s) and optionally
|
||||
* if-guarded PNK_ARRAYPUSH
|
||||
* PNK_ARRAYPUSH unary pn_kid: array comprehension expression
|
||||
* PNK_NOP nullary
|
||||
*/
|
||||
enum ParseNodeArity
|
||||
|
|
@ -552,7 +535,7 @@ class ParseNode
|
|||
ParseNode* left;
|
||||
ParseNode* right;
|
||||
union {
|
||||
unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */
|
||||
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
|
||||
bool isStatic; /* only for PNK_CLASSMETHOD */
|
||||
uint32_t offset; /* for the emitter's use on PNK_CASE nodes */
|
||||
};
|
||||
|
|
@ -713,22 +696,6 @@ class ParseNode
|
|||
return false;
|
||||
}
|
||||
|
||||
ParseNode* generatorExpr() const {
|
||||
MOZ_ASSERT(isKind(PNK_GENEXP));
|
||||
|
||||
ParseNode* callee = this->pn_head;
|
||||
MOZ_ASSERT(callee->isKind(PNK_FUNCTION));
|
||||
|
||||
ParseNode* paramsBody = callee->pn_body;
|
||||
MOZ_ASSERT(paramsBody->isKind(PNK_PARAMSBODY));
|
||||
|
||||
ParseNode* body = paramsBody->last();
|
||||
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
|
||||
MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) ||
|
||||
body->last()->isKind(PNK_COMPREHENSIONFOR));
|
||||
return body->last();
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute a pointer to the last element in a singly-linked list. NB: list
|
||||
* must be non-empty for correct PN_LAST usage -- this is asserted!
|
||||
|
|
@ -945,7 +912,7 @@ struct CodeNode : public ParseNode
|
|||
MOZ_ASSERT_IF(kind == PNK_MODULE, op == JSOP_NOP);
|
||||
MOZ_ASSERT(op == JSOP_NOP || // statement, module
|
||||
op == JSOP_LAMBDA_ARROW || // arrow function
|
||||
op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c.
|
||||
op == JSOP_LAMBDA); // expression, method, accessor, &c.
|
||||
MOZ_ASSERT(!pn_body);
|
||||
MOZ_ASSERT(!pn_objbox);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -302,9 +302,8 @@ SharedContext::computeThisBinding(Scope* scope)
|
|||
if (si.kind() == ScopeKind::Function) {
|
||||
JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
|
||||
|
||||
// Arrow functions and generator expression lambdas don't have
|
||||
// their own `this` binding.
|
||||
if (fun->isArrow() || fun->nonLazyScript()->isGeneratorExp())
|
||||
// Arrow functions don't have their own `this` binding.
|
||||
if (fun->isArrow())
|
||||
continue;
|
||||
|
||||
// Derived class constructors (including nested arrow functions and
|
||||
|
|
@ -468,7 +467,6 @@ FunctionBox::FunctionBox(JSContext* cx, LifoAlloc& alloc, ObjectBox* traceListHe
|
|||
length(0),
|
||||
generatorKind_(GeneratorKindAsBit(generatorKind)),
|
||||
asyncKindBits_(AsyncKindAsBits(asyncKind)),
|
||||
isGenexpLambda(false),
|
||||
hasDestructuringArgs(false),
|
||||
hasParameterExprs(false),
|
||||
hasDirectEvalInParameterExpr(false),
|
||||
|
|
@ -547,10 +545,7 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
|
|||
}
|
||||
}
|
||||
|
||||
if (isGenexpLambda)
|
||||
thisBinding_ = sc->thisBinding();
|
||||
else
|
||||
thisBinding_ = ThisBinding::Function;
|
||||
thisBinding_ = ThisBinding::Function;
|
||||
}
|
||||
|
||||
if (sc->inWith()) {
|
||||
|
|
@ -8361,307 +8356,6 @@ Parser<ParseHandler, CharT>::unaryExpr(YieldHandling yieldHandling,
|
|||
}
|
||||
|
||||
|
||||
/*** Comprehensions *******************************************************************************
|
||||
*
|
||||
* We currently support two flavors of comprehensions, all deprecated:
|
||||
*
|
||||
* [for (V of OBJ) if (COND) EXPR] // ES6-era array comprehension
|
||||
* (for (V of OBJ) if (COND) EXPR) // ES6-era generator expression
|
||||
*
|
||||
* (These flavors are called "ES6-era" because they were in ES6 draft
|
||||
* specifications for a while. Shortly after this syntax was implemented in SM,
|
||||
* TC39 decided to drop it.)
|
||||
*/
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::generatorComprehensionLambda(unsigned begin)
|
||||
{
|
||||
Node genfn = handler.newFunctionExpression(pos());
|
||||
if (!genfn)
|
||||
return null();
|
||||
|
||||
ParseContext* outerpc = pc;
|
||||
|
||||
// If we are off thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseScript, so cx will not
|
||||
// be necessary.
|
||||
RootedObject proto(context);
|
||||
JSContext* cx = context->helperThread() ? nullptr : context;
|
||||
proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, context->global());
|
||||
if (!proto)
|
||||
return null();
|
||||
|
||||
RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression,
|
||||
GeneratorKind::Generator, SyncFunction, proto));
|
||||
if (!fun)
|
||||
return null();
|
||||
|
||||
// Create box for fun->object early to root it.
|
||||
Directives directives(/* strict = */ outerpc->sc()->strict());
|
||||
FunctionBox* genFunbox = newFunctionBox(genfn, fun, /* toStringStart = */ begin, directives,
|
||||
GeneratorKind::Generator, SyncFunction);
|
||||
if (!genFunbox)
|
||||
return null();
|
||||
genFunbox->isGenexpLambda = true;
|
||||
genFunbox->initWithEnclosingParseContext(outerpc, Expression);
|
||||
genFunbox->setStart(tokenStream, begin);
|
||||
|
||||
SourceParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
|
||||
if (!genpc.init())
|
||||
return null();
|
||||
genpc.functionScope().useAsVarScope(&genpc);
|
||||
|
||||
/*
|
||||
* We assume conservatively that any deoptimization flags in pc->sc()
|
||||
* come from the kid. So we propagate these flags into genfn. For code
|
||||
* simplicity we also do not detect if the flags were only set in the
|
||||
* kid and could be removed from pc->sc().
|
||||
*/
|
||||
genFunbox->anyCxFlags = outerpc->sc()->anyCxFlags;
|
||||
|
||||
if (!declareDotGeneratorName())
|
||||
return null();
|
||||
|
||||
Node body = handler.newStatementList(TokenPos(begin, pos().end));
|
||||
if (!body)
|
||||
return null();
|
||||
|
||||
Node comp = comprehension(GeneratorKind::Generator);
|
||||
if (!comp)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_IN_PAREN);
|
||||
|
||||
uint32_t end = pos().end;
|
||||
handler.setBeginPosition(comp, begin);
|
||||
handler.setEndPosition(comp, end);
|
||||
genFunbox->setEnd(tokenStream);
|
||||
handler.addStatementToList(body, comp);
|
||||
handler.setEndPosition(body, end);
|
||||
handler.setBeginPosition(genfn, begin);
|
||||
handler.setEndPosition(genfn, end);
|
||||
|
||||
Node generator = newDotGeneratorName();
|
||||
if (!generator)
|
||||
return null();
|
||||
if (!handler.prependInitialYield(body, generator))
|
||||
return null();
|
||||
|
||||
if (!propagateFreeNamesAndMarkClosedOverBindings(pc->varScope()))
|
||||
return null();
|
||||
if (!finishFunction())
|
||||
return null();
|
||||
if (!leaveInnerFunction(outerpc))
|
||||
return null();
|
||||
|
||||
// Note that if we ever start syntax-parsing generators, we will also
|
||||
// need to propagate the closed-over variable set to the inner
|
||||
// lazyscript.
|
||||
if (!handler.setComprehensionLambdaBody(genfn, body))
|
||||
return null();
|
||||
|
||||
return genfn;
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::comprehensionFor(GeneratorKind comprehensionKind)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
|
||||
// FIXME: Destructuring binding (bug 980828).
|
||||
|
||||
MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
|
||||
RootedPropertyName name(context, bindingIdentifier(YieldIsKeyword));
|
||||
if (!name)
|
||||
return null();
|
||||
if (name == context->names().let) {
|
||||
error(JSMSG_LET_COMP_BINDING);
|
||||
return null();
|
||||
}
|
||||
TokenPos namePos = pos();
|
||||
Node lhs = newName(name);
|
||||
if (!lhs)
|
||||
return null();
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_OF))
|
||||
return null();
|
||||
if (!matched) {
|
||||
error(JSMSG_OF_AFTER_FOR_NAME);
|
||||
return null();
|
||||
}
|
||||
|
||||
Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
if (!rhs)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
|
||||
ParseContext::Scope scope(this);
|
||||
if (!scope.init(pc))
|
||||
return null();
|
||||
|
||||
{
|
||||
// Push a temporary ForLoopLexicalHead Statement that allows for
|
||||
// lexical declarations, as they are usually allowed only in braced
|
||||
// statements.
|
||||
ParseContext::Statement forHeadStmt(pc, StatementKind::ForLoopLexicalHead);
|
||||
if (!noteDeclaredName(name, DeclarationKind::Let, namePos))
|
||||
return null();
|
||||
}
|
||||
|
||||
Node decls = handler.newComprehensionBinding(lhs);
|
||||
if (!decls)
|
||||
return null();
|
||||
|
||||
Node tail = comprehensionTail(comprehensionKind);
|
||||
if (!tail)
|
||||
return null();
|
||||
|
||||
// Finish the lexical scope after parsing the tail.
|
||||
Node lexicalScope = finishLexicalScope(scope, decls);
|
||||
if (!lexicalScope)
|
||||
return null();
|
||||
|
||||
Node head = handler.newForInOrOfHead(PNK_FOROF, lexicalScope, rhs, headPos);
|
||||
if (!head)
|
||||
return null();
|
||||
|
||||
return handler.newComprehensionFor(begin, head, tail);
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::comprehensionIf(GeneratorKind comprehensionKind)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
if (!cond)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_COND);
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isUnparenthesizedAssignment(cond)) {
|
||||
if (!extraWarning(JSMSG_EQUAL_AS_ASSIGN))
|
||||
return null();
|
||||
}
|
||||
|
||||
Node then = comprehensionTail(comprehensionKind);
|
||||
if (!then)
|
||||
return null();
|
||||
|
||||
return handler.newIfStatement(begin, cond, then, null());
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::comprehensionTail(GeneratorKind comprehensionKind)
|
||||
{
|
||||
if (!CheckRecursionLimit(context))
|
||||
return null();
|
||||
|
||||
bool matched;
|
||||
if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched)
|
||||
return comprehensionFor(comprehensionKind);
|
||||
|
||||
if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
|
||||
return null();
|
||||
if (matched)
|
||||
return comprehensionIf(comprehensionKind);
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
if (!bodyExpr)
|
||||
return null();
|
||||
|
||||
if (comprehensionKind == GeneratorKind::NotGenerator)
|
||||
return handler.newArrayPush(begin, bodyExpr);
|
||||
|
||||
MOZ_ASSERT(comprehensionKind == GeneratorKind::Generator);
|
||||
Node yieldExpr = handler.newYieldExpression(begin, bodyExpr);
|
||||
if (!yieldExpr)
|
||||
return null();
|
||||
yieldExpr = handler.parenthesize(yieldExpr);
|
||||
|
||||
return handler.newExprStatement(yieldExpr, pos().end);
|
||||
}
|
||||
|
||||
// Parse an ES6-era generator or array comprehension, starting at the first
|
||||
// `for`. The caller is responsible for matching the ending TOK_RP or TOK_RB.
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::comprehension(GeneratorKind comprehensionKind)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
uint32_t startYieldOffset = pc->lastYieldOffset;
|
||||
|
||||
Node body = comprehensionFor(comprehensionKind);
|
||||
if (!body)
|
||||
return null();
|
||||
|
||||
if (comprehensionKind == GeneratorKind::Generator && pc->lastYieldOffset != startYieldOffset) {
|
||||
errorAt(pc->lastYieldOffset, JSMSG_BAD_GENEXP_BODY, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::arrayComprehension(uint32_t begin)
|
||||
{
|
||||
Node inner = comprehension(GeneratorKind::NotGenerator);
|
||||
if (!inner)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN_MOD(TOK_RB, TokenStream::Operand, JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION);
|
||||
|
||||
Node comp = handler.newList(PNK_ARRAYCOMP, inner);
|
||||
if (!comp)
|
||||
return null();
|
||||
|
||||
handler.setBeginPosition(comp, begin);
|
||||
handler.setEndPosition(comp, pos().end);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::generatorComprehension(uint32_t begin)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
// We have no problem parsing generator comprehensions inside lazy
|
||||
// functions, but the bytecode emitter currently can't handle them that way,
|
||||
// because when it goes to emit the code for the inner generator function,
|
||||
// it expects outer functions to have non-lazy scripts.
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
Node genfn = generatorComprehensionLambda(begin);
|
||||
if (!genfn)
|
||||
return null();
|
||||
|
||||
return handler.newGeneratorComprehension(genfn, TokenPos(begin, pos().end));
|
||||
}
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler, CharT>::assignExprWithoutYieldOrAwait(YieldHandling yieldHandling)
|
||||
|
|
@ -9331,10 +9025,6 @@ Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
|
|||
if (!tokenStream.getToken(&tt, TokenStream::Operand))
|
||||
return null();
|
||||
|
||||
// Handle an ES6-era array comprehension first.
|
||||
if (tt == TOK_FOR)
|
||||
return arrayComprehension(begin);
|
||||
|
||||
if (tt == TOK_RB) {
|
||||
/*
|
||||
* Mark empty arrays as non-constant, since we cannot easily
|
||||
|
|
@ -10030,12 +9720,6 @@ Parser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
|
|||
return handler.newNullLiteral(pos());
|
||||
}
|
||||
|
||||
if (next == TOK_FOR) {
|
||||
uint32_t begin = pos().begin;
|
||||
tokenStream.consumeKnownToken(next, TokenStream::Operand);
|
||||
return generatorComprehension(begin);
|
||||
}
|
||||
|
||||
// Pass |possibleError| to support destructuring in arrow parameters.
|
||||
Node expr = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError);
|
||||
if (!expr)
|
||||
|
|
|
|||
|
|
@ -780,15 +780,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
|
|||
|
||||
Node condition(InHandling inHandling, YieldHandling yieldHandling);
|
||||
|
||||
/* comprehensions */
|
||||
Node generatorComprehensionLambda(unsigned begin);
|
||||
Node comprehensionFor(GeneratorKind comprehensionKind);
|
||||
Node comprehensionIf(GeneratorKind comprehensionKind);
|
||||
Node comprehensionTail(GeneratorKind comprehensionKind);
|
||||
Node comprehension(GeneratorKind comprehensionKind);
|
||||
Node arrayComprehension(uint32_t begin);
|
||||
Node generatorComprehension(uint32_t begin);
|
||||
|
||||
bool argumentList(YieldHandling yieldHandling, Node listNode, bool* isSpread,
|
||||
PossibleError* possibleError = nullptr);
|
||||
Node destructuringDeclaration(DeclarationKind kind, YieldHandling yieldHandling,
|
||||
|
|
|
|||
|
|
@ -407,7 +407,6 @@ class FunctionBox : public ObjectBox, public SharedContext
|
|||
uint8_t generatorKind_; /* The GeneratorKind of this function. */
|
||||
uint8_t asyncKindBits_; /* The FunctionAsyncKind of this function. */
|
||||
|
||||
bool isGenexpLambda:1; /* lambda from generator expression */
|
||||
bool hasDestructuringArgs:1; /* parameter list contains destructuring expression */
|
||||
bool hasParameterExprs:1; /* parameter list contains expressions */
|
||||
bool hasDirectEvalInParameterExpr:1; /* parameter list contains direct eval */
|
||||
|
|
|
|||
|
|
@ -249,18 +249,12 @@ class SyntaxParseHandler
|
|||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newArrayPush(uint32_t begin, Node kid) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node appendOrCreateList(ParseNodeKind kind, Node left, Node right, ParseContext* pc) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
Node newGeneratorComprehension(Node genfn, const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newArrayComprehension(Node body, const TokenPos& pos) { return NodeGeneric; }
|
||||
Node newArrayLiteral(uint32_t begin) { return NodeUnparenthesizedArray; }
|
||||
MOZ_MUST_USE bool addElision(Node literal, const TokenPos& pos) { return true; }
|
||||
MOZ_MUST_USE bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
|
||||
|
|
@ -359,7 +353,6 @@ class SyntaxParseHandler
|
|||
Node newFunctionExpression(const TokenPos& pos) { return NodeFunctionDefinition; }
|
||||
Node newArrowFunction(const TokenPos& pos) { return NodeFunctionDefinition; }
|
||||
|
||||
bool setComprehensionLambdaBody(Node pn, Node body) { return true; }
|
||||
void setFunctionFormalParametersAndBody(Node pn, Node kid) {}
|
||||
void setFunctionBody(Node pn, Node kid) {}
|
||||
void setFunctionBox(Node pn, FunctionBox* funbox) {}
|
||||
|
|
@ -369,18 +362,6 @@ class SyntaxParseHandler
|
|||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newComprehensionFor(uint32_t begin, Node forHead, Node body) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newComprehensionBinding(Node kid) {
|
||||
// Careful: we're asking this well after the name was parsed, so the
|
||||
// value returned may not correspond to |kid|'s actual name. But it
|
||||
// *will* be truthy iff |kid| was a name, so we're safe.
|
||||
MOZ_ASSERT(isUnparenthesizedName(kid));
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newForHead(Node init, Node test, Node update, const TokenPos& pos) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -767,42 +767,6 @@ function test_syntax(postfixes, check_error, ignore_opts) {
|
|||
test("/a/ ");
|
||||
test("/a/g ");
|
||||
|
||||
// Array comprehensions
|
||||
|
||||
test("[for ");
|
||||
test("[for ( ");
|
||||
test("[for (x ");
|
||||
test("[for (x of ");
|
||||
test("[for (x of y ");
|
||||
test("[for (x of y) ");
|
||||
test("[for (x of y) x ");
|
||||
test("[for (x of y) if ");
|
||||
test("[for (x of y) if ( ");
|
||||
test("[for (x of y) if (x ");
|
||||
test("[for (x of y) if (x == ");
|
||||
test("[for (x of y) if (x == 1 ");
|
||||
test("[for (x of y) if (x == 1) ");
|
||||
test("[for (x of y) if (x == 1) x ");
|
||||
test("[for (x of y) if (x == 1) x] ");
|
||||
|
||||
// Generator comprehensions
|
||||
|
||||
test("(for ");
|
||||
test("(for ( ");
|
||||
test("(for (x ");
|
||||
test("(for (x of ");
|
||||
test("(for (x of y ");
|
||||
test("(for (x of y) ");
|
||||
test("(for (x of y) x ");
|
||||
test("(for (x of y) if ");
|
||||
test("(for (x of y) if ( ");
|
||||
test("(for (x of y) if (x ");
|
||||
test("(for (x of y) if (x == ");
|
||||
test("(for (x of y) if (x == 1 ");
|
||||
test("(for (x of y) if (x == 1) ");
|
||||
test("(for (x of y) if (x == 1) x ");
|
||||
test("(for (x of y) if (x == 1) x) ");
|
||||
|
||||
// ---- Left-hand-side expressions ----
|
||||
|
||||
// property access
|
||||
|
|
|
|||
|
|
@ -173,20 +173,6 @@ testThrow(`
|
|||
delete ...a) =>
|
||||
`, 7);
|
||||
|
||||
// array comprehension
|
||||
|
||||
testThrow(`
|
||||
[for (...a) =>
|
||||
`, 6);
|
||||
|
||||
testThrow(`
|
||||
[for (x of y) if (...a) =>
|
||||
`, 18);
|
||||
|
||||
testThrow(`
|
||||
[for (x of y) if (x) ...a) =>
|
||||
`, 21);
|
||||
|
||||
// new
|
||||
|
||||
testThrow(`
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
load(libdir + 'bytecode-cache.js');
|
||||
var test = "";
|
||||
|
||||
// The main test function.
|
||||
test = `
|
||||
var test = function (isContent) {
|
||||
// Returns generator object that iterates through pref values.
|
||||
let prefVals = (for (prefVal of [false, true]) prefVal);
|
||||
|
||||
for (x of prefVals) ;
|
||||
}
|
||||
|
||||
test()
|
||||
`;
|
||||
evalWithCache(test, {
|
||||
incremental: true,
|
||||
assertEqBytecode: true,
|
||||
assertEqResult : true
|
||||
});
|
||||
|
|
@ -1021,6 +1021,7 @@ BaselineCompiler::emitBody()
|
|||
case JSOP_SETINTRINSIC:
|
||||
// Run-once opcode during self-hosting initialization.
|
||||
case JSOP_UNUSED126:
|
||||
case JSOP_UNUSED206:
|
||||
case JSOP_UNUSED223:
|
||||
case JSOP_LIMIT:
|
||||
// === !! WARNING WARNING WARNING !! ===
|
||||
|
|
@ -2267,25 +2268,6 @@ BaselineCompiler::emit_JSOP_INITHIDDENPROP()
|
|||
return emit_JSOP_INITPROP();
|
||||
}
|
||||
|
||||
typedef bool (*NewbornArrayPushFn)(JSContext*, HandleObject, const Value&);
|
||||
static const VMFunction NewbornArrayPushInfo =
|
||||
FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush, "NewbornArrayPush");
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ARRAYPUSH()
|
||||
{
|
||||
// Keep value in R0, object in R1.
|
||||
frame.popRegsAndSync(2);
|
||||
masm.unboxObject(R1, R1.scratchReg());
|
||||
|
||||
prepareVMCall();
|
||||
|
||||
pushArg(R0);
|
||||
pushArg(R1.scratchReg());
|
||||
|
||||
return callVM(NewbornArrayPushInfo);
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_GETELEM()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ namespace jit {
|
|||
_(JSOP_INITHIDDENPROP) \
|
||||
_(JSOP_INITPROP_GETTER) \
|
||||
_(JSOP_INITPROP_SETTER) \
|
||||
_(JSOP_ARRAYPUSH) \
|
||||
_(JSOP_GETELEM) \
|
||||
_(JSOP_SETELEM) \
|
||||
_(JSOP_STRICTSETELEM) \
|
||||
|
|
|
|||
|
|
@ -2439,7 +2439,6 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_GENERATOR:
|
||||
|
||||
// Misc
|
||||
case JSOP_ARRAYPUSH:
|
||||
case JSOP_DELNAME:
|
||||
case JSOP_FINALLY:
|
||||
case JSOP_GETRVAL:
|
||||
|
|
@ -2458,6 +2457,7 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
break;
|
||||
|
||||
case JSOP_UNUSED126:
|
||||
case JSOP_UNUSED206:
|
||||
case JSOP_UNUSED223:
|
||||
case JSOP_LIMIT:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1024,12 +1024,8 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource)
|
|||
}
|
||||
|
||||
RootedScript script(cx);
|
||||
|
||||
if (fun->hasScript()) {
|
||||
if (fun->hasScript())
|
||||
script = fun->nonLazyScript();
|
||||
if (MOZ_UNLIKELY(script->isGeneratorExp()))
|
||||
return NewStringCopyZ<CanGC>(cx, "function genexp() {\n [generator expression]\n}");
|
||||
}
|
||||
|
||||
// Default class constructors are self-hosted, but have their source
|
||||
// objects overridden to refer to the span of the class statement or
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ class JSFunction : public js::NativeObject
|
|||
}
|
||||
|
||||
bool hasLexicalThis() const {
|
||||
return isArrow() || nonLazyScript()->isGeneratorExp();
|
||||
return isArrow();
|
||||
}
|
||||
|
||||
bool isBuiltinFunctionConstructor();
|
||||
|
|
|
|||
|
|
@ -340,7 +340,6 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
HasMappedArgsObj,
|
||||
FunctionHasThisBinding,
|
||||
FunctionHasExtraBodyVarScope,
|
||||
IsGeneratorExp,
|
||||
IsGenerator,
|
||||
IsAsync,
|
||||
HasRest,
|
||||
|
|
@ -454,8 +453,6 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
|
||||
if (!sourceObjectArg)
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGeneratorExp())
|
||||
scriptBits |= (1 << IsGeneratorExp);
|
||||
if (script->isGenerator())
|
||||
scriptBits |= (1 << IsGenerator);
|
||||
if (script->asyncKind() == AsyncFunction)
|
||||
|
|
@ -613,8 +610,6 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
|
|||
script->functionHasThisBinding_ = true;
|
||||
if (scriptBits & (1 << FunctionHasExtraBodyVarScope))
|
||||
script->functionHasExtraBodyVarScope_ = true;
|
||||
if (scriptBits & (1 << IsGeneratorExp))
|
||||
script->isGeneratorExp_ = true;
|
||||
if (scriptBits & (1 << HasSingleton))
|
||||
script->hasSingletons_ = true;
|
||||
if (scriptBits & (1 << TreatAsRunOnce))
|
||||
|
|
@ -2954,7 +2949,6 @@ JSScript::initFromFunctionBox(JSContext* cx, HandleScript script,
|
|||
|
||||
script->funLength_ = funbox->length;
|
||||
|
||||
script->isGeneratorExp_ = funbox->isGenexpLambda;
|
||||
script->setGeneratorKind(funbox->generatorKind());
|
||||
script->setAsyncKind(funbox->asyncKind());
|
||||
if (funbox->hasRest())
|
||||
|
|
@ -2979,7 +2973,6 @@ JSScript::initFromModuleContext(JSContext* cx, HandleScript script,
|
|||
script->isDerivedClassConstructor_ = false;
|
||||
script->funLength_ = 0;
|
||||
|
||||
script->isGeneratorExp_ = false;
|
||||
script->setGeneratorKind(GeneratorKind::NotGenerator);
|
||||
|
||||
// Since modules are only run once, mark the script so that initializers
|
||||
|
|
@ -3624,7 +3617,6 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
|
|||
dst->hasSingletons_ = src->hasSingletons();
|
||||
dst->treatAsRunOnce_ = src->treatAsRunOnce();
|
||||
dst->hasInnerFunctions_ = src->hasInnerFunctions();
|
||||
dst->isGeneratorExp_ = src->isGeneratorExp();
|
||||
dst->setGeneratorKind(src->generatorKind());
|
||||
dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor();
|
||||
dst->needsHomeObject_ = src->needsHomeObject();
|
||||
|
|
|
|||
|
|
@ -1102,10 +1102,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
// Lexical check did fail and bail out.
|
||||
bool failedLexicalCheck_:1;
|
||||
|
||||
// If the generator was created implicitly via a generator expression,
|
||||
// isGeneratorExp will be true.
|
||||
bool isGeneratorExp_:1;
|
||||
|
||||
// Script has an entry in JSCompartment::scriptCountsMap.
|
||||
bool hasScriptCounts_:1;
|
||||
|
||||
|
|
@ -1390,8 +1386,6 @@ class JSScript : public js::gc::TenuredCell
|
|||
}
|
||||
void setLikelyConstructorWrapper() { isLikelyConstructorWrapper_ = true; }
|
||||
|
||||
bool isGeneratorExp() const { return isGeneratorExp_; }
|
||||
|
||||
bool failedBoundsCheck() const {
|
||||
return failedBoundsCheck_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,11 +200,5 @@ assertEq(typeof asyncf, "function");
|
|||
var { [0 ? 1 : a => {}]: h } = { "a => {}": "boo-urns!" };
|
||||
assertEq(h, "boo-urns!");
|
||||
|
||||
var gencomp = (for (prop of [0]) 0 ? 1 : a => {});
|
||||
assertEq(typeof gencomp.next().value, "function");
|
||||
|
||||
var arrcomp = [for (prop of [0]) 0 ? 1 : a => {}];
|
||||
assertEq(typeof arrcomp[0], "function");
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
|
||||
function values(g) {
|
||||
return [for (x of g) x];
|
||||
}
|
||||
|
||||
function argumentsTest() {
|
||||
return values((for (i of [0,1,2]) arguments[i]));
|
||||
}
|
||||
|
||||
assertDeepEq(argumentsTest('a', 'b', 'c'), ['a', 'b', 'c']);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Interactions between yield and array comprehensions.
|
||||
|
||||
|
||||
function assertIteratorResult(result, value, done) {
|
||||
assertDeepEq(result.value, value);
|
||||
assertEq(result.done, done);
|
||||
}
|
||||
|
||||
function* t1() { return [for (x of yield 0) x*2] }
|
||||
|
||||
var o = t1();
|
||||
assertIteratorResult(o.next(), 0, false);
|
||||
assertIteratorResult(o.next([0, 1, 2]), [0, 2, 4], true);
|
||||
|
||||
function* t2() { return [for (x of [1,2,3]) yield x] }
|
||||
|
||||
o = t2();
|
||||
assertIteratorResult(o.next(), 1, false);
|
||||
assertIteratorResult(o.next(5), 2, false);
|
||||
assertIteratorResult(o.next(6), 3, false);
|
||||
assertIteratorResult(o.next(7), [5, 6, 7], true);
|
||||
|
||||
reportCompare(null, null, "test");
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
|
||||
// This file tests contextual restrictions for yield and arguments, and is
|
||||
// derived from js1_8/genexps/regress-634472.js.
|
||||
|
||||
function error(str) {
|
||||
var base;
|
||||
try {
|
||||
// the following line must not be broken up into multiple lines
|
||||
base = (function(){try{eval('throw new Error()')}catch(e){return e.lineNumber}})(); eval(str);
|
||||
return null;
|
||||
} catch (e) {
|
||||
e.lineNumber = e.lineNumber - base + 1;
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
const YIELD_PAREN = error("(function*(){(for (y of (yield 1, 2)) y)})").message;
|
||||
const GENEXP_YIELD = error("(function*(){(for (x of yield 1) x)})").message;
|
||||
const TOP_YIELD = error("yield").message;
|
||||
const GENERIC = error("(for)").message;
|
||||
const BAD_GENERATOR_SYNTAX = error("(for (x of []) x, 1)").message;
|
||||
const MISSING_SEMI = error("yield 1").message;
|
||||
const PAREN_PAREN = error("(foo").message;
|
||||
const FOR_OF_PAREN = error("(for (x of y, z) w)").message;
|
||||
|
||||
const cases = [
|
||||
// Expressions involving yield without a value, not currently implemented. Many
|
||||
// of these errors would need to be updated. (Note: line numbers below might be
|
||||
// mere placeholders, not actually the expected correct behavior -- check before
|
||||
// blindly uncommenting.)
|
||||
//{ expr: "yield", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield" },
|
||||
//{ expr: "1, yield", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list" },
|
||||
//{ expr: "yield, 1", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list" },
|
||||
//{ expr: "(yield)", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized" },
|
||||
//{ expr: "(1, yield)", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized" },
|
||||
//{ expr: "(yield, 1)", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized" },
|
||||
//{ expr: "((((yield))))", top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "deeply nested yield" },
|
||||
//{ expr: "(for (x of []) yield)", top: [TOP_YIELD, 777], fun: [GENERIC, 777], gen: [GENERIC, 777], desc: "simple yield in genexp" },
|
||||
//{ expr: "(for (x of []) yield, 1)", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list in genexp" },
|
||||
//{ expr: "(for (x of []) 1, yield)", top: [TOP_YIELD, 777], fun: [GENERIC, 777], gen: [GENERIC, 777], desc: "simple yield at end of list in genexp" },
|
||||
//{ expr: "(for (x of []) (yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in genexp" },
|
||||
//{ expr: "(for (x of []) 1, (yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in list in genexp" },
|
||||
//{ expr: "(for (x of []) (1, yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in genexp" },
|
||||
//{ expr: "(for (x of []) 1, (2, yield))", top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in list in genexp" },
|
||||
//{ expr: "(for (x of []) (yield, 1))", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized in genexp" },
|
||||
//{ expr: "(for (x of []) 1, (yield, 2))", top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], desc: "simple yield in list, parenthesized in list in genexp" },
|
||||
//{ expr: "(for (x of []) (function*() { yield }))", top: null, fun: null, gen: null, desc: "legal yield in nested function" },
|
||||
|
||||
// yield expressions
|
||||
{ expr: "yield 1", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" },
|
||||
{ expr: "1, yield 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" },
|
||||
{ expr: "yield 1, 2", top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" },
|
||||
{ expr: "(yield 1)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" },
|
||||
{ expr: "(1, yield 2)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" },
|
||||
{ expr: "(yield 1, 2)", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" },
|
||||
|
||||
// deeply nested yield expressions
|
||||
{ expr: "((((yield 1))))", top: [PAREN_PAREN, 2], fun: [PAREN_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" },
|
||||
|
||||
// arguments
|
||||
{ expr: "arguments", top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" },
|
||||
{ expr: "1, arguments", top: null, fun: null, gen: null, genexp: [FOR_OF_PAREN, 1], desc: "arguments in list" },
|
||||
|
||||
// yield in generator expressions
|
||||
{ expr: "(for (x of []) yield 1)", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in genexp" },
|
||||
{ expr: "(for (x of []) yield 1, 2)", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in list in genexp" },
|
||||
{ expr: "(for (x of []) 1, yield 2)", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list in genexp" },
|
||||
{ expr: "(for (x of []) (yield 1))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized in genexp" },
|
||||
{ expr: "(for (x of []) 1, (yield 2))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg, parenthesized in list in genexp" },
|
||||
{ expr: "(for (x of []) (1, yield 2))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized in genexp" },
|
||||
{ expr: "(for (x of []) 1, (2, yield 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list, parenthesized in list in genexp" },
|
||||
{ expr: "(for (x of []) (yield 1, 2))", top: [YIELD_PAREN, 2], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized in genexp" },
|
||||
{ expr: "(for (x of []) 1, (yield 2, 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg in list, parenthesized in list in genexp" },
|
||||
|
||||
// deeply nested yield in generator expressions
|
||||
{ expr: "(for (x of []) (((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in genexp" },
|
||||
{ expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in multiple genexps" },
|
||||
|
||||
// arguments in generator expressions
|
||||
{ expr: "(for (x of []) arguments)", top: null, fun: null, gen: null, genexp: null, desc: "simple arguments in genexp" },
|
||||
{ expr: "(for (x of []) 1, arguments)", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list in genexp" },
|
||||
{ expr: "(for (x of []) (arguments))", top: null, fun: null, gen: null, genexp: null, desc: "arguments, parenthesized in genexp" },
|
||||
{ expr: "(for (x of []) 1, (arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments, parenthesized in list in genexp" },
|
||||
{ expr: "(for (x of []) (1, arguments))", top: null, fun: null, gen: null, genexp: null, desc: "arguments in list, parenthesized in genexp" },
|
||||
{ expr: "(for (x of []) 1, (2, arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list, parenthesized in list in genexp" },
|
||||
|
||||
// deeply nested arguments in generator expressions
|
||||
{ expr: "(for (x of []) (((1, arguments))))", top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in genexp" },
|
||||
{ expr: "(for (y of []) (for (x of []) ((1, arguments))))", top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in multiple genexps" },
|
||||
|
||||
// legal yield/arguments in nested function
|
||||
{ expr: "(for (x of []) (function*() { yield 1 }))", top: null, fun: null, gen: null, genexp: null, desc: "legal yield in nested function" },
|
||||
{ expr: "(for (x of []) (function() { arguments }))", top: null, fun: null, gen: null, genexp: null, desc: "legal arguments in nested function" },
|
||||
{ expr: "(for (x of []) (function() arguments))", top: null, fun: null, gen: null, genexp: null, desc: "legal arguments in nested expression-closure" }
|
||||
];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function splitKeyword(str) {
|
||||
return str.
|
||||
// replace(/[)] yield/, ')\nyield\n').
|
||||
replace(/yield ([0-9])/, '\nyield $1\n').
|
||||
replace(/yield([^ ]|$)/, '\nyield\n$1').
|
||||
replace(/arguments/, '\narguments\n');
|
||||
}
|
||||
|
||||
function expectError1(err, ctx, msg, lineNumber) {
|
||||
reportCompare('object', typeof err, 'exn for: ' + msg);
|
||||
reportCompare(ctx, err.message, 'exn message for: ' + msg);
|
||||
reportCompare(lineNumber, err.lineNumber, 'exn token for: ' + msg);
|
||||
}
|
||||
|
||||
function expectError(expr, wrapCtx, [expect, lineNumber], msg) {
|
||||
expectError1(error(wrapCtx(expr)), expect, msg, lineNumber);
|
||||
}
|
||||
|
||||
function expectSuccess(err, msg) {
|
||||
reportCompare(null, err, 'parse: ' + msg);
|
||||
}
|
||||
|
||||
function atTop(str) { return str }
|
||||
function inFun(str) { return '(function(){' + str + '})' }
|
||||
function inGen(str) { return '(function*(){' + str + '})' }
|
||||
function inGenExp(str) { return '(for (y of ' + str + ') y)' }
|
||||
|
||||
var summary = '';
|
||||
|
||||
function test()
|
||||
{
|
||||
printStatus (summary);
|
||||
|
||||
for (var i = 0, len = cases.length; i < len; i++) {
|
||||
var expr, top, fun, gen, genexp, desc;
|
||||
expr = cases[i].expr;
|
||||
top = cases[i].top;
|
||||
fun = cases[i].fun;
|
||||
gen = cases[i].gen;
|
||||
genexp = cases[i].genexp;
|
||||
desc = cases[i].desc;
|
||||
|
||||
expr = splitKeyword(expr);
|
||||
|
||||
if (top)
|
||||
expectError(expr, atTop, top, 'top-level context, ' + desc);
|
||||
else
|
||||
expectSuccess(error(expr), 'top-level context, ' + desc);
|
||||
|
||||
if (fun)
|
||||
expectError(expr, inFun, fun, 'function context, ' + desc);
|
||||
else
|
||||
expectSuccess(error(inFun(expr)), 'function context, ' + desc);
|
||||
|
||||
if (gen)
|
||||
expectError(expr, inGen, gen, 'generator context, ' + desc);
|
||||
else
|
||||
expectSuccess(error(inGen(expr)), 'generator context, ' + desc);
|
||||
|
||||
if (genexp)
|
||||
expectError(expr, inGenExp, genexp, 'genexp context, ' + desc);
|
||||
else
|
||||
expectSuccess(error(inGenExp(expr)), 'genexp context, ' + desc);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
var BUGNUMBER = 1340089;
|
||||
var summary = "Comprehension should check the binding names";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
// Non strict mode.
|
||||
// Keywords, literals, 'let', and 'yield' are not allowed.
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("[for (true of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("(for (true of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("[for (throw of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("(for (throw of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("[for (let of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("(for (let of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("[for (yield of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
eval("(for (yield of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
eval("[for (public of [1]) 2]");
|
||||
eval("(for (public of [1]) 2)");
|
||||
|
||||
eval("[for (static of [1]) 2]");
|
||||
eval("(for (static of [1]) 2)");
|
||||
|
||||
// Strict mode.
|
||||
// All reserved words are not allowed.
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (true of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (true of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (throw of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (throw of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (let of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (let of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (yield of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (yield of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (public of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (public of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("[for (static of [1]) 2]");
|
||||
}, SyntaxError);
|
||||
assertThrowsInstanceOf(function () {
|
||||
"use strict";
|
||||
eval("(for (static of [1]) 2)");
|
||||
}, SyntaxError);
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
eval("[for (await of [1]) 2]");
|
||||
eval("(for (await of [1]) 2)");
|
||||
})();
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
// Interaction of eval with generator expressions.
|
||||
function a1() {
|
||||
var a = 10;
|
||||
var g = (for (y of [0]) eval('var a=42;'));
|
||||
g.next();
|
||||
return a;
|
||||
}
|
||||
assertEq(a1(), 10);
|
||||
|
||||
function a2() {
|
||||
var a = 10;
|
||||
(for (y of [0]) eval('a=42')).next();
|
||||
return a;
|
||||
}
|
||||
assertEq(a2(), 42)
|
||||
|
||||
// Arguments and this.
|
||||
function b1() {
|
||||
return [for (arg of (for (i of [0, 1, 2]) arguments[i])) arg];
|
||||
}
|
||||
assertDeepEq(b1('a', 'b', 'c'), ['a', 'b', 'c']);
|
||||
|
||||
function b2() {
|
||||
return [for (x of (for (i of [0]) this)) x];
|
||||
}
|
||||
var b2o = { b2: b2 }
|
||||
assertDeepEq(b2o.b2(), [b2o])
|
||||
|
||||
// Assignment to eval or arguments.
|
||||
function c1() {
|
||||
return [for (arg of (for (i of [0, 1, 2]) arguments = i)) arg];
|
||||
}
|
||||
assertDeepEq(c1(), [0, 1, 2]);
|
||||
|
||||
function c2() {
|
||||
"use strict";
|
||||
return eval('[for (arg of (for (i of [0, 1, 2]) arguments = i)) arg]');
|
||||
}
|
||||
assertThrows(c2, SyntaxError);
|
||||
|
||||
function c3() {
|
||||
return [for (arg of (for (i of [0, 1, 2]) eval = i)) arg];
|
||||
}
|
||||
assertDeepEq(c3(), [0, 1, 2]);
|
||||
|
||||
function c4() {
|
||||
"use strict";
|
||||
return eval('[for (arg of (for (i of [0, 1, 2]) eval = i)) arg]');
|
||||
}
|
||||
assertThrows(c4, SyntaxError);
|
||||
|
||||
reportCompare(null, null, "test");
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 1299519;
|
||||
var summary =
|
||||
"Generator comprehension lambdas in derived constructors shouldn't assert";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
class Base {};
|
||||
|
||||
class Derived extends Base
|
||||
{
|
||||
constructor() {
|
||||
var a = (for (_ of []) _);
|
||||
var b = [for (_ of []) _];
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("Tests complete");
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// For and if clauses can nest without limit in comprehensions. This is
|
||||
// unlike JS 1.8 comprehensions, which can only have one trailing "if"
|
||||
// clause.
|
||||
|
||||
function* range(start, end) {
|
||||
for (var n = start; n < end; n++)
|
||||
yield n;
|
||||
}
|
||||
|
||||
function primesBetween6And25() {
|
||||
return [for (n of range(6, 25)) if (n % 2) if (n % 3) if (n % 5) n];
|
||||
}
|
||||
assertDeepEq(primesBetween6And25(), [7,11,13,17,19,23]);
|
||||
|
||||
function countUpToEvens(limit) {
|
||||
return [for (n of range(0, limit)) if (!(n % 2)) for (m of range(0, n)) m]
|
||||
}
|
||||
assertDeepEq(countUpToEvens(7), [0,1,0,1,2,3,0,1,2,3,4,5]);
|
||||
|
||||
reportCompare(null, null, "test");
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// The identifier of a ComprehensionFor is only bound within its tail.
|
||||
|
||||
function t() {
|
||||
var x = [0, 1, 2];
|
||||
return [for (x of x) x*2]
|
||||
}
|
||||
assertDeepEq(t(), [0, 2, 4]);
|
||||
|
||||
// Each iteration should create a fresh binding. Unfortunately this is
|
||||
// not currently the case, but bug 449811 will fix this.
|
||||
function t2() {
|
||||
var x = [0, 1, 2];
|
||||
return [for (x of x) ()=>x]
|
||||
}
|
||||
// FIXME: Should be [0, 1, 2].
|
||||
assertDeepEq([for (x of t2()) x()], [2, 2, 2]);
|
||||
|
||||
reportCompare(null, null, "test");
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
|
||||
function copy(obj) {
|
||||
var o = {};
|
||||
for (var i in obj)
|
||||
o[i] = obj[i];
|
||||
return o;
|
||||
}
|
||||
|
||||
Array.prototype.repeat = function (n) {
|
||||
var s = this.constructor();
|
||||
for (var i = 0; i < n; i++)
|
||||
s = s.concat(this);
|
||||
return s;
|
||||
}
|
||||
|
||||
String.prototype.center = function (w) {
|
||||
var n = this.length;
|
||||
if (w <= n)
|
||||
return this;
|
||||
var m = Math.floor((w - n) / 2);
|
||||
return ' '.repeat(m) + this + ' '.repeat(w - n - m);
|
||||
}
|
||||
|
||||
Array.prototype.toString = Array.prototype.toSource
|
||||
Object.prototype.toString = Object.prototype.toSource
|
||||
|
||||
function all(seq) {
|
||||
for (var e of seq)
|
||||
if (!e)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function some(seq) {
|
||||
for (var e of seq)
|
||||
if (e)
|
||||
return e;
|
||||
return false;
|
||||
}
|
||||
|
||||
function cross(A, B) {
|
||||
return [for (a of A) for (b of B) a+b];
|
||||
}
|
||||
|
||||
function dict(A) {
|
||||
var d = {};
|
||||
for (var e of A)
|
||||
d[e[0]] = e[1];
|
||||
return d;
|
||||
}
|
||||
|
||||
function set(A) {
|
||||
var s = [];
|
||||
for (var e of A)
|
||||
if (!s.includes(e))
|
||||
s.push(e);
|
||||
return s;
|
||||
}
|
||||
|
||||
function zip(A, B) {
|
||||
var z = [];
|
||||
var n = Math.min(A.length, B.length);
|
||||
for (var i = 0; i < n; i++)
|
||||
z.push([A[i], B[i]]);
|
||||
return z;
|
||||
}
|
||||
|
||||
rows = 'ABCDEFGHI';
|
||||
cols = '123456789';
|
||||
digits = '123456789';
|
||||
squares = cross(rows, cols);
|
||||
unitlist = [for (c of cols) cross(rows, c)]
|
||||
.concat([for (r of rows) cross(r, cols)])
|
||||
.concat([for (rs of ['ABC','DEF','GHI']) for (cs of ['123','456','789']) cross(rs, cs)]);
|
||||
units = dict((for (s of squares)
|
||||
[s, [for (u of unitlist) if (u.includes(s)) u]]));
|
||||
|
||||
peers = dict((for (s of squares)
|
||||
[s, set([for (u of units[s]) for (s2 of u) if (s2 != s) s2])]));
|
||||
|
||||
// Given a string of 81 digits (or . or 0 or -), return a dict of {cell:values}.
|
||||
function parse_grid(grid) {
|
||||
grid = [for (c of grid) if ('0.-123456789'.includes(c)) c];
|
||||
var values = dict((for (s of squares) [s, digits]));
|
||||
|
||||
for (var pair of zip(squares, grid)) {
|
||||
var s = pair[0], d = pair[1];
|
||||
if (digits.includes(d) && !assign(values, s, d))
|
||||
return false;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// Eliminate all the other values (except d) from values[s] and propagate.
|
||||
function assign(values, s, d) {
|
||||
if (all((for (d2 of values[s]) if (d2 != d) eliminate(values, s, d2))))
|
||||
return values;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Eliminate d from values[s]; propagate when values or places <= 2.
|
||||
function eliminate(values, s, d) {
|
||||
if (!values[s].includes(d))
|
||||
return values; // Already eliminated
|
||||
values[s] = values[s].replace(d, '');
|
||||
if (values[s].length == 0)
|
||||
return false; // Contradiction: removed last value
|
||||
if (values[s].length == 1) {
|
||||
// If there is only one value (d2) left in square, remove it from peers
|
||||
var d2 = values[s][0];
|
||||
if (!all((for (s2 of peers[s]) eliminate(values, s2, d2))))
|
||||
return false;
|
||||
}
|
||||
// Now check the places where d appears in the units of s
|
||||
for (var u of units[s]) {
|
||||
var dplaces = [for (s of u) if (values[s].includes(d)) s];
|
||||
if (dplaces.length == 0)
|
||||
return false;
|
||||
if (dplaces.length == 1)
|
||||
// d can only be in one place in unit; assign it there
|
||||
if (!assign(values, dplaces[0], d))
|
||||
return false;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
// Used for debugging.
|
||||
function print_board(values) {
|
||||
var width = 1 + Math.max.apply(Math, [for (s of squares) values[s].length]);
|
||||
var line = '\n' + ['-'.repeat(width*3)].repeat(3).join('+');
|
||||
for (var r of rows)
|
||||
print([for (c of cols)
|
||||
values[r+c].center(width) + ('36'.includes(c) && '|' || '')]
|
||||
.join('') + ('CF'.includes(r) && line || ''));
|
||||
print('\n');
|
||||
}
|
||||
|
||||
easy = "..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..";
|
||||
|
||||
print_board(parse_grid(easy));
|
||||
|
||||
// Using depth-first search and constraint propagation, try all possible values.
|
||||
function search(values) {
|
||||
if (!values)
|
||||
return false; // Failed earlier
|
||||
if (all((for (s of squares) values[s].length == 1)))
|
||||
return values; // Solved!
|
||||
|
||||
// Choose the unfilled square s with the fewest possibilities
|
||||
// XXX Math.min etc. should work with generator expressions and other iterators
|
||||
// XXX Math.min etc. should work on arrays (lists or tuples in Python) as well as numbers
|
||||
var a = [for (s of squares) if (values[s].length > 1) values[s].length + s].sort();
|
||||
var s = a[0].slice(-2);
|
||||
|
||||
return some((for (d of values[s]) search(assign(copy(values), s, d))));
|
||||
}
|
||||
|
||||
hard = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......';
|
||||
|
||||
print_board(search(parse_grid(hard)))
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
// "let" is not allowed as an identifier.
|
||||
|
||||
assertThrowsInstanceOf(function () { eval('[for (let of y) foo]') }, SyntaxError);
|
||||
assertThrowsInstanceOf(function () { eval('(for (let of y) foo)') }, SyntaxError);
|
||||
|
||||
reportCompare(null, null, "test");
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
assertEq( function () { g = (for (d of [0]) d); g.next(); }.toSource(),
|
||||
'(function () { g = (for (d of [0]) d); g.next(); })');
|
||||
|
||||
|
||||
assertEq( function () { return [for (d of [0]) d]; }.toSource(),
|
||||
'(function () { return [for (d of [0]) d]; })');
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
|
@ -14,10 +14,6 @@
|
|||
var c;
|
||||
|
||||
stmts.push(`var ${pattern} = ${val};`);
|
||||
if (!opt.no_comp) {
|
||||
stmts.push(`[for (x of [1]) ${pattern} = ${val}];`);
|
||||
stmts.push(`[...(for (x of [1]) ${pattern} = ${val})];`);
|
||||
}
|
||||
|
||||
for (var stmt of stmts) {
|
||||
if (!opt.no_plain) {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ assertEq(s.size, 0);
|
|||
// Symbols returned by Symbol.for() can be in Sets.
|
||||
var str = "how much wood would a woodchuck chuck if a woodchuck could chuck wood";
|
||||
var s2 = "how much wood would a woodchuck chuck if could";
|
||||
var arr = [for (word of str.split(" ")) Symbol.for(word)];
|
||||
var arr = str.split(" ").map(Symbol.for);
|
||||
s = new Set(arr);
|
||||
assertDeepEq([...s], [for (word of s2.split(" ")) Symbol.for(word)]);
|
||||
assertDeepEq([...s], s2.split(" ").map(Symbol.for));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
function test() {
|
||||
|
||||
// Transform the legacy comprehensions to less legacy comprehensions and test
|
||||
// them.
|
||||
function assertFormerlyES6ArrayComp(expr, body, blocks, filter) {
|
||||
let match = expr.match(/^\[(.*?) for (.*)\]$/);
|
||||
assertEq(match !== null, true);
|
||||
let expr2 = "[for " + match[2] + " " + match[1] + "]";
|
||||
assertExpr(expr2, compExpr(body, blocks, filter, "modern"));
|
||||
}
|
||||
|
||||
assertFormerlyES6ArrayComp("[ x for (x of foo)]",
|
||||
ident("x"), [compOfBlock(ident("x"), ident("foo"))], null);
|
||||
assertFormerlyES6ArrayComp("[ [x,y] for (x of foo) for (y of bar)]",
|
||||
arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null);
|
||||
assertFormerlyES6ArrayComp("[ [x,y,z] for (x of foo) for (y of bar) for (z of baz)]",
|
||||
arrExpr([ident("x"), ident("y"), ident("z")]),
|
||||
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
|
||||
null);
|
||||
|
||||
assertFormerlyES6ArrayComp("[ x for (x of foo) if (p)]",
|
||||
ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p"));
|
||||
assertFormerlyES6ArrayComp("[ [x,y] for (x of foo) for (y of bar) if (p)]",
|
||||
arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p"));
|
||||
assertFormerlyES6ArrayComp("[ [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) ]",
|
||||
arrExpr([ident("x"), ident("y"), ident("z")]),
|
||||
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
|
||||
ident("p"));
|
||||
|
||||
// FormerlyES6 comprehensions with multiple ComprehensionIf.
|
||||
|
||||
assertExpr("[for (x of foo) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], null, "modern"));
|
||||
assertExpr("[for (x of foo) if (c1) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1"))], null, "modern"));
|
||||
assertExpr("[for (x of foo) if (c1) if (c2) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")), compIf(ident("c2"))], null, "modern"));
|
||||
|
||||
assertExpr("[for (x of foo) if (c1) for (y of bar) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")),
|
||||
compOfBlock(ident("y"), ident("bar"))], null, "modern"));
|
||||
assertExpr("[for (x of foo) if (c1) for (y of bar) if (c2) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")),
|
||||
compOfBlock(ident("y"), ident("bar")),
|
||||
compIf(ident("c2"))], null, "modern"));
|
||||
assertExpr("[for (x of foo) if (c1) if (c2) for (y of bar) if (c3) if (c4) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")), compIf(ident("c2")),
|
||||
compOfBlock(ident("y"), ident("bar")),
|
||||
compIf(ident("c3")), compIf(ident("c4"))], null, "modern"));
|
||||
|
||||
assertExpr("[for (x of y) if (false) for (z of w) if (0) x]",
|
||||
compExpr(ident("x"), [compOfBlock(ident("x"), ident("y")),
|
||||
compIf(lit(false)),
|
||||
compOfBlock(ident("z"), ident("w")),
|
||||
compIf(lit(0))], null, "modern"));
|
||||
|
||||
}
|
||||
|
||||
runtest(test);
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
function test() {
|
||||
|
||||
// Translate legacy genexprs into less legacy genexprs and test them.
|
||||
function assertFormerlyES6GenExpr(expr, body, blocks, filter) {
|
||||
let match = expr.match(/^\((.*?) for (.*)\)$/);
|
||||
assertEq(match !== null, true);
|
||||
let expr2 = "(for " + match[2] + " " + match[1] + ")";
|
||||
assertExpr(expr2, genExpr(body, blocks, filter, "modern"));
|
||||
}
|
||||
|
||||
assertFormerlyES6GenExpr("( x for (x of foo))",
|
||||
ident("x"), [compOfBlock(ident("x"), ident("foo"))], null);
|
||||
assertFormerlyES6GenExpr("( [x,y] for (x of foo) for (y of bar))",
|
||||
arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null);
|
||||
assertFormerlyES6GenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz))",
|
||||
arrExpr([ident("x"), ident("y"), ident("z")]),
|
||||
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
|
||||
null);
|
||||
|
||||
assertFormerlyES6GenExpr("( x for (x of foo) if (p))",
|
||||
ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p"));
|
||||
assertFormerlyES6GenExpr("( [x,y] for (x of foo) for (y of bar) if (p))",
|
||||
arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p"));
|
||||
assertFormerlyES6GenExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) )",
|
||||
arrExpr([ident("x"), ident("y"), ident("z")]),
|
||||
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
|
||||
ident("p"));
|
||||
|
||||
// FormerlyES6 generator comprehension with multiple ComprehensionIf.
|
||||
|
||||
assertExpr("(for (x of foo) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], null, "modern"));
|
||||
assertExpr("(for (x of foo) if (c1) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1"))], null, "modern"));
|
||||
assertExpr("(for (x of foo) if (c1) if (c2) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")), compIf(ident("c2"))], null, "modern"));
|
||||
|
||||
assertExpr("(for (x of foo) if (c1) for (y of bar) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")),
|
||||
compOfBlock(ident("y"), ident("bar"))], null, "modern"));
|
||||
assertExpr("(for (x of foo) if (c1) for (y of bar) if (c2) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")),
|
||||
compOfBlock(ident("y"), ident("bar")),
|
||||
compIf(ident("c2"))], null, "modern"));
|
||||
assertExpr("(for (x of foo) if (c1) if (c2) for (y of bar) if (c3) if (c4) x)",
|
||||
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo")),
|
||||
compIf(ident("c1")), compIf(ident("c2")),
|
||||
compOfBlock(ident("y"), ident("bar")),
|
||||
compIf(ident("c3")), compIf(ident("c4"))], null, "modern"));
|
||||
|
||||
// NOTE: it would be good to test generator expressions both with and without upvars, just like functions above.
|
||||
|
||||
}
|
||||
|
||||
runtest(test);
|
||||
|
|
@ -23,9 +23,6 @@ var fourAC = nested.body[0].expression.left.right.arguments[0].right;
|
|||
|
||||
Pattern({ source: "quad.js", start: { line: 1, column: 20 }, end: { line: 1, column: 29 } }).match(fourAC.loc);
|
||||
|
||||
var generator = Reflect.parse("[ for \n(x of a) x+1 ]");
|
||||
Pattern({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } }).match(generator.body[0].expression.blocks[0].left.loc);
|
||||
|
||||
// No source location
|
||||
|
||||
assertEq(Reflect.parse("42", {loc:false}).loc, null);
|
||||
|
|
|
|||
|
|
@ -1978,6 +1978,7 @@ CASE(JSOP_NOP)
|
|||
CASE(JSOP_NOP_DESTRUCTURING)
|
||||
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
|
||||
CASE(JSOP_UNUSED126)
|
||||
CASE(JSOP_UNUSED206)
|
||||
CASE(JSOP_UNUSED223)
|
||||
CASE(JSOP_CONDSWITCH)
|
||||
{
|
||||
|
|
@ -4100,15 +4101,6 @@ CASE(JSOP_FINALYIELDRVAL)
|
|||
goto successful_return_continuation;
|
||||
}
|
||||
|
||||
CASE(JSOP_ARRAYPUSH)
|
||||
{
|
||||
ReservedRooted<JSObject*> obj(&rootObject0, ®S.sp[-1].toObject());
|
||||
if (!NewbornArrayPush(cx, obj, REGS.sp[-2]))
|
||||
goto error;
|
||||
REGS.sp -= 2;
|
||||
}
|
||||
END_CASE(JSOP_ARRAYPUSH)
|
||||
|
||||
CASE(JSOP_CHECKCLASSHERITAGE)
|
||||
{
|
||||
HandleValue heritage = REGS.stackHandleAt(-1);
|
||||
|
|
|
|||
|
|
@ -2106,17 +2106,8 @@
|
|||
* Stack: gen, val => rval
|
||||
*/ \
|
||||
macro(JSOP_RESUME, 205,"resume", NULL, 3, 2, 1, JOF_UINT8|JOF_INVOKE) \
|
||||
/*
|
||||
* Pops the top two values on the stack as 'obj' and 'v', pushes 'v' to
|
||||
* 'obj'.
|
||||
*
|
||||
* This opcode is used for Array Comprehension.
|
||||
* Category: Literals
|
||||
* Type: Array
|
||||
* Operands:
|
||||
* Stack: v, obj =>
|
||||
*/ \
|
||||
macro(JSOP_ARRAYPUSH, 206,"arraypush", NULL, 1, 2, 0, JOF_BYTE) \
|
||||
\
|
||||
macro(JSOP_UNUSED206, 206,"unused206", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
* No-op bytecode only emitted in some self-hosted functions. Not handled by
|
||||
|
|
|
|||
Loading…
Reference in a new issue