Bug 1414340 part 2 - Remove array/generator comprehensions from SpiderMonkey. r=evilpie

This commit is contained in:
Jan de Mooij 2017-11-10 11:52:29 +01:00
parent 296194e65f
commit d6b8e1b490
42 changed files with 135 additions and 2155 deletions

View file

@ -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);

View file

@ -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, &noteIndex))
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, &noteIndex))
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;

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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));

View file

@ -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:

View file

@ -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);
}

View file

@ -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)

View file

@ -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,

View file

@ -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 */

View file

@ -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;
}

View file

@ -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

View file

@ -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(`

View file

@ -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
});

View file

@ -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()
{

View file

@ -115,7 +115,6 @@ namespace jit {
_(JSOP_INITHIDDENPROP) \
_(JSOP_INITPROP_GETTER) \
_(JSOP_INITPROP_SETTER) \
_(JSOP_ARRAYPUSH) \
_(JSOP_GETELEM) \
_(JSOP_SETELEM) \
_(JSOP_STRICTSETELEM) \

View file

@ -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;

View file

@ -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

View file

@ -245,7 +245,7 @@ class JSFunction : public js::NativeObject
}
bool hasLexicalThis() const {
return isArrow() || nonLazyScript()->isGeneratorExp();
return isArrow();
}
bool isBuiltinFunctionConstructor();

View file

@ -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();

View file

@ -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_;
}

View file

@ -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);

View file

@ -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);

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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);

View file

@ -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");

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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, &REGS.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);

View file

@ -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