зеркало из https://github.com/mozilla/gecko-dev.git
Bug 979865 - Part 3: Implement ES6 array comprehensions r=jorendorff
This commit is contained in:
Родитель
e14a60fe1e
Коммит
5ba1fe5cdf
|
@ -6423,6 +6423,178 @@ static const char js_generator_str[] = "generator";
|
|||
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
|
||||
{
|
||||
JS_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(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
if (name == context->names().let) {
|
||||
report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
|
||||
return null();
|
||||
}
|
||||
if (!tokenStream.matchContextualKeyword(context->names().of)) {
|
||||
report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
|
||||
return null();
|
||||
}
|
||||
|
||||
Node rhs = assignExpr();
|
||||
if (!rhs)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
|
||||
|
||||
TokenPos headPos(begin, pos().end);
|
||||
|
||||
StmtInfoPC stmtInfo(context);
|
||||
BindData<ParseHandler> data(context);
|
||||
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
|
||||
if (!blockObj)
|
||||
return null();
|
||||
data.initLet(DontHoistVars, *blockObj, JSMSG_TOO_MANY_LOCALS);
|
||||
Node lhs = newName(name);
|
||||
if (!lhs)
|
||||
return null();
|
||||
Node decls = handler.newList(PNK_LET, lhs, JSOP_NOP);
|
||||
if (!decls)
|
||||
return null();
|
||||
data.pn = lhs;
|
||||
if (!data.binder(&data, name, this))
|
||||
return null();
|
||||
Node letScope = pushLetScope(blockObj, &stmtInfo);
|
||||
if (!letScope)
|
||||
return null();
|
||||
handler.setLexicalScopeBody(letScope, decls);
|
||||
|
||||
Node assignLhs = newName(name);
|
||||
if (!assignLhs)
|
||||
return null();
|
||||
if (!noteNameUse(name, assignLhs))
|
||||
return null();
|
||||
handler.setOp(assignLhs, JSOP_SETNAME);
|
||||
|
||||
Node head = handler.newForHead(PNK_FOROF, letScope, assignLhs, rhs, headPos);
|
||||
if (!head)
|
||||
return null();
|
||||
|
||||
Node tail = comprehensionTail(comprehensionKind);
|
||||
if (!tail)
|
||||
return null();
|
||||
|
||||
PopStatementPC(tokenStream, pc);
|
||||
|
||||
return handler.newForStatement(begin, head, tail, JSOP_ITER);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
|
||||
Node cond = assignExpr();
|
||||
if (!cond)
|
||||
return null();
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
|
||||
|
||||
/* Check for (a = b) and warn about possible (a == b) mistype. */
|
||||
if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
|
||||
!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
|
||||
{
|
||||
return null();
|
||||
}
|
||||
|
||||
Node then = comprehensionTail(comprehensionKind);
|
||||
if (!then)
|
||||
return null();
|
||||
|
||||
return handler.newIfStatement(begin, cond, then, null());
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
|
||||
{
|
||||
JS_CHECK_RECURSION(context, return null());
|
||||
|
||||
if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
|
||||
return comprehensionFor(comprehensionKind);
|
||||
|
||||
if (tokenStream.matchToken(TOK_IF, TokenStream::Operand))
|
||||
return comprehensionIf(comprehensionKind);
|
||||
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
Node bodyExpr = assignExpr();
|
||||
if (!bodyExpr)
|
||||
return null();
|
||||
|
||||
if (comprehensionKind == NotGenerator)
|
||||
return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
|
||||
|
||||
JS_ASSERT(comprehensionKind == StarGenerator);
|
||||
Node yieldExpr = handler.newUnary(PNK_YIELD, JSOP_NOP, begin, bodyExpr);
|
||||
if (!yieldExpr)
|
||||
return null();
|
||||
handler.setInParens(yieldExpr);
|
||||
|
||||
return handler.newExprStatement(yieldExpr, pos().end);
|
||||
}
|
||||
|
||||
// Parse an ES6 generator or array comprehension, starting at the first 'for'.
|
||||
// The caller is responsible for matching the ending TOK_RP or TOK_RB.
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::comprehension(GeneratorKind comprehensionKind)
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
uint32_t startYieldOffset = pc->lastYieldOffset;
|
||||
|
||||
Node body = comprehensionFor(comprehensionKind);
|
||||
if (!body)
|
||||
return null();
|
||||
|
||||
if (comprehensionKind != NotGenerator && pc->lastYieldOffset != startYieldOffset) {
|
||||
reportWithOffset(ParseError, false, pc->lastYieldOffset,
|
||||
JSMSG_BAD_GENEXP_BODY, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::arrayComprehension(uint32_t begin)
|
||||
{
|
||||
Node inner = comprehension(NotGenerator);
|
||||
if (!inner)
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, 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 <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::assignExprWithoutYield(unsigned msg)
|
||||
|
@ -6678,7 +6850,8 @@ Parser<ParseHandler>::arrayInitializer()
|
|||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
|
||||
|
||||
Node literal = handler.newArrayLiteral(pos().begin, pc->blockidGen);
|
||||
uint32_t begin = pos().begin;
|
||||
Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
|
||||
if (!literal)
|
||||
return null();
|
||||
|
||||
|
@ -6688,6 +6861,9 @@ Parser<ParseHandler>::arrayInitializer()
|
|||
* determine their type.
|
||||
*/
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
} else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
|
||||
// ES6 array comprehension.
|
||||
return arrayComprehension(begin);
|
||||
} else {
|
||||
bool spread = false, missingTrailingComma = false;
|
||||
uint32_t index = 0;
|
||||
|
|
|
@ -547,6 +547,12 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
|||
Node legacyArrayComprehension(Node array);
|
||||
Node legacyGeneratorExpr(Node kid);
|
||||
|
||||
Node comprehensionTail(GeneratorKind comprehensionKind);
|
||||
Node comprehensionIf(GeneratorKind comprehensionKind);
|
||||
Node comprehensionFor(GeneratorKind comprehensionKind);
|
||||
Node comprehension(GeneratorKind comprehensionKind);
|
||||
Node arrayComprehension(uint32_t begin);
|
||||
|
||||
bool argumentList(Node listNode, bool *isSpread);
|
||||
Node letBlock(LetContext letContext);
|
||||
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
|
||||
|
|
|
@ -166,6 +166,14 @@ class SyntaxParseHandler
|
|||
void setFunctionBox(Node pn, FunctionBox *funbox) {}
|
||||
void addFunctionArgument(Node pn, Node argpn) {}
|
||||
|
||||
Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
|
||||
void setLexicalScopeBody(Node block, Node body) {}
|
||||
|
||||
|
|
|
@ -226,7 +226,7 @@ MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 172, 0, JSEXN_TYPEERR, "iterable for map s
|
|||
MSG_DEF(JSMSG_NOT_A_CODEPOINT, 173, 1, JSEXN_RANGEERR, "{0} is not a valid code point")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 174, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
|
||||
MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 0, JSEXN_TYPEERR, "already executing generator")
|
||||
MSG_DEF(JSMSG_UNUSED176, 176, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 176, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
|
||||
MSG_DEF(JSMSG_UNUSED177, 177, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED178, 178, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED179, 179, 0, JSEXN_NONE, "")
|
||||
|
@ -263,11 +263,11 @@ MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous gener
|
|||
MSG_DEF(JSMSG_PROTO_SETTING_SLOW, 210, 0, JSEXN_TYPEERR, "mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create")
|
||||
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
|
||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||
MSG_DEF(JSMSG_UNUSED213, 213, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_OF_AFTER_FOR_NAME, 213, 0, JSEXN_SYNTAXERR, "missing 'of' after for")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
|
||||
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
|
||||
MSG_DEF(JSMSG_UNUSED217, 217, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_LET_COMP_BINDING, 217, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
|
||||
MSG_DEF(JSMSG_BAD_SYMBOL, 219, 1, JSEXN_TYPEERR, "{0} is not a well-known @@-symbol")
|
||||
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_REFERENCEERR, "invalid delete operand")
|
||||
|
|
|
@ -86,7 +86,6 @@ const char js_import_str[] = "import";
|
|||
const char js_in_str[] = "in";
|
||||
const char js_instanceof_str[] = "instanceof";
|
||||
const char js_interface_str[] = "interface";
|
||||
const char js_let_str[] = "let";
|
||||
const char js_new_str[] = "new";
|
||||
const char js_package_str[] = "package";
|
||||
const char js_private_str[] = "private";
|
||||
|
|
|
@ -146,7 +146,6 @@ extern const char js_import_str[];
|
|||
extern const char js_in_str[];
|
||||
extern const char js_instanceof_str[];
|
||||
extern const char js_interface_str[];
|
||||
extern const char js_let_str[];
|
||||
extern const char js_new_str[];
|
||||
extern const char js_package_str[];
|
||||
extern const char js_private_str[];
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
macro(keys, keys, "keys") \
|
||||
macro(lastIndex, lastIndex, "lastIndex") \
|
||||
macro(length, length, "length") \
|
||||
macro(let, let, "let") \
|
||||
macro(line, line, "line") \
|
||||
macro(lineNumber, lineNumber, "lineNumber") \
|
||||
macro(loc, loc, "loc") \
|
||||
|
|
Загрузка…
Ссылка в новой задаче