Bug 1065450 - Make Reflect.parse properly handle new-style array comprehensions and generator expressions. r=Waldo.

--HG--
extra : rebase_source : 07009c087b787880d147d362ddebdbd1f88453c4
extra : source : a37b8adef57b541329674f41720d3c0c71a46d54
This commit is contained in:
Jason Orendorff 2014-09-30 09:17:12 -05:00
parent fb845401b2
commit 79882186b4
3 changed files with 129 additions and 78 deletions

View File

@ -773,8 +773,8 @@ class ParseNode
MOZ_ASSERT(isKind(PNK_GENEXP));
ParseNode *callee = this->pn_head;
ParseNode *body = callee->pn_body;
MOZ_ASSERT(body->isKind(PNK_LEXICALSCOPE));
return body->pn_expr;
MOZ_ASSERT(body->isKind(PNK_LEXICALSCOPE) || body->isKind(PNK_FOR));
return body;
}
#endif

View File

@ -670,10 +670,10 @@ class NodeBuilder
MutableHandleValue dst);
bool comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter,
TokenPos *pos, MutableHandleValue dst);
bool isLegacy, TokenPos *pos, MutableHandleValue dst);
bool generatorExpression(HandleValue body, NodeVector &blocks, HandleValue filter,
TokenPos *pos, MutableHandleValue dst);
bool isLegacy, TokenPos *pos, MutableHandleValue dst);
bool letExpression(NodeVector &head, HandleValue expr, TokenPos *pos, MutableHandleValue dst);
@ -1411,39 +1411,49 @@ NodeBuilder::comprehensionBlock(HandleValue patt, HandleValue src, bool isForEac
bool
NodeBuilder::comprehensionExpression(HandleValue body, NodeVector &blocks, HandleValue filter,
TokenPos *pos, MutableHandleValue dst)
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), pos, dst);
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,
TokenPos *pos, MutableHandleValue dst)
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), pos, dst);
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);
}
@ -2577,7 +2587,7 @@ ASTSerializer::comprehensionBlock(ParseNode *pn, MutableHandleValue dst)
LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF)));
bool isForEach = pn->pn_iflags & JSITER_FOREACH;
bool isForEach = in->isKind(PNK_FORIN) && (pn->pn_iflags & JSITER_FOREACH);
bool isForOf = in->isKind(PNK_FOROF);
RootedValue patt(cx), src(cx);
@ -2589,11 +2599,16 @@ ASTSerializer::comprehensionBlock(ParseNode *pn, MutableHandleValue dst)
bool
ASTSerializer::comprehension(ParseNode *pn, MutableHandleValue dst)
{
LOCAL_ASSERT(pn->isKind(PNK_FOR));
// 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_FOR));
NodeVector blocks(cx);
ParseNode *next = pn;
while (next->isKind(PNK_FOR)) {
RootedValue block(cx);
if (!comprehensionBlock(next, &block) || !blocks.append(block))
@ -2618,17 +2633,21 @@ ASTSerializer::comprehension(ParseNode *pn, MutableHandleValue dst)
RootedValue body(cx);
return expression(next->pn_kid, &body) &&
builder.comprehensionExpression(body, blocks, filter, &pn->pn_pos, dst);
builder.comprehensionExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
}
bool
ASTSerializer::generatorExpression(ParseNode *pn, MutableHandleValue dst)
{
LOCAL_ASSERT(pn->isKind(PNK_FOR));
// 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_FOR));
NodeVector blocks(cx);
ParseNode *next = pn;
while (next->isKind(PNK_FOR)) {
RootedValue block(cx);
if (!comprehensionBlock(next, &block) || !blocks.append(block))
@ -2651,7 +2670,7 @@ ASTSerializer::generatorExpression(ParseNode *pn, MutableHandleValue dst)
RootedValue body(cx);
return expression(next->pn_kid->pn_kid, &body) &&
builder.generatorExpression(body, blocks, filter, &pn->pn_pos, dst);
builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst);
}
bool
@ -2998,9 +3017,7 @@ ASTSerializer::expression(ParseNode *pn, MutableHandleValue dst)
/* NB: it's no longer the case that pn_count could be 2. */
LOCAL_ASSERT(pn->pn_count == 1);
LOCAL_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE));
return comprehension(pn->pn_head->pn_expr, dst);
return comprehension(pn->pn_head, dst);
case PNK_LET:
return let(pn, true, dst);

View File

@ -99,8 +99,8 @@ function taggedTemplate(tagPart, templatePart) Pattern({ type: "TaggedTemplate",
arguments : templatePart })
function template(raw, cooked, ...args) Pattern([{ type: "CallSiteObject", raw: raw, cooked:
cooked}, ...args])
function compExpr(body, blocks, filter) Pattern({ type: "ComprehensionExpression", body: body, blocks: blocks, filter: filter })
function genExpr(body, blocks, filter) Pattern({ type: "GeneratorExpression", body: body, blocks: blocks, filter: filter })
function compExpr(body, blocks, filter, style) Pattern({ type: "ComprehensionExpression", body, blocks, filter, style })
function genExpr(body, blocks, filter, style) Pattern({ type: "GeneratorExpression", body, blocks, filter, style })
function graphExpr(idx, body) Pattern({ type: "GraphExpression", index: idx, expression: body })
function letExpr(head, body) Pattern({ type: "LetExpression", head: head, body: body })
function idxExpr(idx) Pattern({ type: "GraphIndexExpression", index: idx })
@ -381,7 +381,7 @@ assertExpr("2 + 3", binExpr("+", lit(2), lit(3)));
assertExpr("typeof(0?0:a)", unExpr("typeof", condExpr(lit(0), lit(0), ident("a"))));
// Bug 632029: constant-folding
assertExpr("[x for each (x in y) if (false)]", compExpr(ident("x"), [compEachBlock(ident("x"), ident("y"))], lit(false)));
assertExpr("[x for each (x in y) if (false)]", compExpr(ident("x"), [compEachBlock(ident("x"), ident("y"))], lit(false), "legacy"));
// Bug 632056: constant-folding
program([exprStmt(ident("f")),
@ -814,114 +814,146 @@ assertExpr("({ set x(v) { return 42 } })",
// comprehensions
assertExpr("[ x for (x in foo)]",
compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null));
compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null, "legacy"));
assertExpr("[ [x,y] for (x in foo) for (y in bar)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null));
compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null, "legacy"));
assertExpr("[ [x,y,z] for (x in foo) for (y in bar) for (z in baz)]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))],
null));
null,
"legacy"));
assertExpr("[ x for (x in foo) if (p)]",
compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p")));
compExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p"), "legacy"));
assertExpr("[ [x,y] for (x in foo) for (y in bar) if (p)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p")));
compExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p"), "legacy"));
assertExpr("[ [x,y,z] for (x in foo) for (y in bar) for (z in baz) if (p) ]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))],
ident("p")));
ident("p"),
"legacy"));
assertExpr("[ x for each (x in foo)]",
compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null));
compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null, "legacy"));
assertExpr("[ [x,y] for each (x in foo) for each (y in bar)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null));
compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null, "legacy"));
assertExpr("[ [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz)]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))],
null));
null,
"legacy"));
assertExpr("[ x for each (x in foo) if (p)]",
compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p")));
compExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p"), "legacy"));
assertExpr("[ [x,y] for each (x in foo) for each (y in bar) if (p)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p")));
compExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p"), "legacy"));
assertExpr("[ [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz) if (p) ]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))],
ident("p")));
ident("p"),
"legacy"));
assertExpr("[ x for (x of foo)]",
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], null));
assertExpr("[ [x,y] for (x of foo) for (y of bar)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null));
assertExpr("[ [x,y,z] for (x of foo) for (y of bar) for (z of baz)]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
null));
// Comprehension expressions using for-of can be written in two different styles.
function assertLegacyAndModernArrayComp(expr, body, blocks, filter) {
assertExpr(expr, compExpr(body, blocks, filter, "legacy"));
assertExpr("[ x for (x of foo) if (p)]",
compExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p")));
assertExpr("[ [x,y] for (x of foo) for (y of bar) if (p)]",
compExpr(arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p")));
assertExpr("[ [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) ]",
compExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
ident("p")));
// Transform the legacy comprehension to a modern comprehension and test it
// that way too.
let match = expr.match(/^\[(.*?) for (.*)\]$/);
assertEq(match !== null, true);
let expr2 = "[for " + match[2] + " " + match[1] + "]";
assertExpr(expr2, compExpr(body, blocks, filter, "modern"));
}
assertLegacyAndModernArrayComp("[ x for (x of foo)]",
ident("x"), [compOfBlock(ident("x"), ident("foo"))], null);
assertLegacyAndModernArrayComp("[ [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);
assertLegacyAndModernArrayComp("[ [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);
assertLegacyAndModernArrayComp("[ x for (x of foo) if (p)]",
ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p"));
assertLegacyAndModernArrayComp("[ [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"));
assertLegacyAndModernArrayComp("[ [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"));
// generator expressions
assertExpr("( x for (x in foo))",
genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null));
genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], null, "legacy"));
assertExpr("( [x,y] for (x in foo) for (y in bar))",
genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null));
genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], null, "legacy"));
assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz))",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))],
null));
null,
"legacy"));
assertExpr("( x for (x in foo) if (p))",
genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p")));
genExpr(ident("x"), [compBlock(ident("x"), ident("foo"))], ident("p"), "legacy"));
assertExpr("( [x,y] for (x in foo) for (y in bar) if (p))",
genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p")));
genExpr(arrExpr([ident("x"), ident("y")]), [compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar"))], ident("p"), "legacy"));
assertExpr("( [x,y,z] for (x in foo) for (y in bar) for (z in baz) if (p) )",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compBlock(ident("x"), ident("foo")), compBlock(ident("y"), ident("bar")), compBlock(ident("z"), ident("baz"))],
ident("p")));
ident("p"),
"legacy"));
assertExpr("( x for each (x in foo))",
genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null));
genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], null, "legacy"));
assertExpr("( [x,y] for each (x in foo) for each (y in bar))",
genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null));
genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], null, "legacy"));
assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz))",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))],
null));
null,
"legacy"));
assertExpr("( x for each (x in foo) if (p))",
genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p")));
genExpr(ident("x"), [compEachBlock(ident("x"), ident("foo"))], ident("p"), "legacy"));
assertExpr("( [x,y] for each (x in foo) for each (y in bar) if (p))",
genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p")));
genExpr(arrExpr([ident("x"), ident("y")]), [compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar"))], ident("p"), "legacy"));
assertExpr("( [x,y,z] for each (x in foo) for each (y in bar) for each (z in baz) if (p) )",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compEachBlock(ident("x"), ident("foo")), compEachBlock(ident("y"), ident("bar")), compEachBlock(ident("z"), ident("baz"))],
ident("p")));
ident("p"),
"legacy"));
assertExpr("( x for (x of foo))",
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], null));
assertExpr("( [x,y] for (x of foo) for (y of bar))",
genExpr(arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], null));
assertExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz))",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
null));
// Generator expressions using for-of can be written in two different styles.
function assertLegacyAndModernGenExpr(expr, body, blocks, filter) {
assertExpr(expr, genExpr(body, blocks, filter, "legacy"));
assertExpr("( x for (x of foo) if (p))",
genExpr(ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p")));
assertExpr("( [x,y] for (x of foo) for (y of bar) if (p))",
genExpr(arrExpr([ident("x"), ident("y")]), [compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar"))], ident("p")));
assertExpr("( [x,y,z] for (x of foo) for (y of bar) for (z of baz) if (p) )",
genExpr(arrExpr([ident("x"), ident("y"), ident("z")]),
[compOfBlock(ident("x"), ident("foo")), compOfBlock(ident("y"), ident("bar")), compOfBlock(ident("z"), ident("baz"))],
ident("p")));
// Transform the legacy genexpr to a modern genexpr and test it that way
// too.
let match = expr.match(/^\((.*?) for (.*)\)$/);
assertEq(match !== null, true);
let expr2 = "(for " + match[2] + " " + match[1] + ")";
assertExpr(expr2, genExpr(body, blocks, filter, "modern"));
}
assertLegacyAndModernGenExpr("( x for (x of foo))",
ident("x"), [compOfBlock(ident("x"), ident("foo"))], null);
assertLegacyAndModernGenExpr("( [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);
assertLegacyAndModernGenExpr("( [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);
assertLegacyAndModernGenExpr("( x for (x of foo) if (p))",
ident("x"), [compOfBlock(ident("x"), ident("foo"))], ident("p"));
assertLegacyAndModernGenExpr("( [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"));
assertLegacyAndModernGenExpr("( [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"));
// NOTE: it would be good to test generator expressions both with and without upvars, just like functions above.
@ -1061,7 +1093,9 @@ assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [], 2, null), {
assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }",
tryStmt(blockStmt([]), [2, 2], null, null),
{ catchClause: function() 2 });
assertGlobalExpr("[x for (y in z) for (x in y)]", compExpr(ident("x"), [3, 3], null), { comprehensionBlock: function() 3 });
assertGlobalExpr("[x for (y in z) for (x in y)]",
compExpr(ident("x"), [3, 3], null, "legacy"),
{ comprehensionBlock: function() 3 });
assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: function() 1 });
assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: function() 2 });