Bug 1154391 - Update import declarations to current ES6 spec r=shu

This commit is contained in:
Jon Coppeard 2015-05-28 09:57:57 +01:00
parent 9c9ab9548c
commit 4d97bbfd94
8 changed files with 282 additions and 63 deletions

View File

@ -332,6 +332,8 @@ enum ParseNodeKind
* in original source, not introduced via
* constant folding or other tree rewriting
* PNK_LABEL name pn_atom: label, pn_expr: labeled statement
* PNK_IMPORT binary pn_left: PNK_IMPORT_SPEC_LIST import specifiers
* pn_right: PNK_STRING module specifier
*
* <Expressions>
* All left-associated binary trees of the same type are optimized into lists

View File

@ -4177,6 +4177,105 @@ Parser<SyntaxParseHandler>::letDeclarationOrBlock(YieldHandling yieldHandling)
return SyntaxParseHandler::NodeFailure;
}
template<>
bool
Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
{
if (tt == TOK_LC) {
while (true) {
// Handle the forms |import {} from 'a'| and
// |import { ..., } from 'a'| (where ... is non empty), by
// escaping the loop early if the next token is }.
if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
return false;
if (tt == TOK_RC)
break;
// If the next token is a keyword, the previous call to
// peekToken matched it as a TOK_NAME, and put it in the
// lookahead buffer, so this call will match keywords as well.
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
Node importName = newName(tokenStream.currentName());
if (!importName)
return false;
if (!tokenStream.getToken(&tt))
return false;
if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
} else {
// Keywords cannot be bound to themselves, so an import name
// that is a keyword is a syntax error if it is not followed
// by the keyword 'as'.
// See the ImportSpecifier production in ES6 section 15.2.2.
if (IsKeyword(importName->name())) {
JSAutoByteString bytes;
if (!AtomToPrintableString(context, importName->name(), &bytes))
return false;
report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
return false;
}
tokenStream.ungetToken();
}
Node bindingName = newName(tokenStream.currentName());
if (!bindingName)
return false;
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
if (!importSpec)
return false;
handler.addList(importSpecSet, importSpec);
bool matched;
if (!tokenStream.matchToken(&matched, TOK_COMMA))
return false;
if (!matched)
break;
}
MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
} else {
MOZ_ASSERT(tt == TOK_MUL);
if (!tokenStream.getToken(&tt))
return false;
if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) {
report(ParseError, false, null(), JSMSG_AS_AFTER_IMPORT_STAR);
return false;
}
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
Node importName = newName(context->names().star);
if (!importName)
return null();
Node bindingName = newName(tokenStream.currentName());
if (!bindingName)
return false;
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
if (!importSpec)
return false;
handler.addList(importSpecSet, importSpec);
}
return true;
}
template<>
bool
Parser<SyntaxParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
{
MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
return false;
}
template<typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::importDeclaration()
@ -4197,7 +4296,7 @@ Parser<ParseHandler>::importDeclaration()
if (!importSpecSet)
return null();
if (tt == TOK_NAME || tt == TOK_LC) {
if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) {
if (tt == TOK_NAME) {
// Handle the form |import a from 'b'|, by adding a single import
// specifier to the list, with 'default' as the import name and
@ -4216,83 +4315,43 @@ Parser<ParseHandler>::importDeclaration()
return null();
handler.addList(importSpecSet, importSpec);
} else {
while (true) {
// Handle the forms |import {} from 'a'| and
// |import { ..., } from 'a'| (where ... is non empty), by
// escaping the loop early if the next token is }.
if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
return null();
if (tt == TOK_RC)
break;
// If the next token is a keyword, the previous call to
// peekToken matched it as a TOK_NAME, and put it in the
// lookahead buffer, so this call will match keywords as well.
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
Node importName = newName(tokenStream.currentName());
if (!importName)
if (!tokenStream.peekToken(&tt))
return null();
if (tt == TOK_COMMA) {
if (!tokenStream.getToken(&tt) || !tokenStream.getToken(&tt))
return null();
if (!tokenStream.getToken(&tt))
if (tt != TOK_LC && tt != TOK_MUL) {
report(ParseError, false, null(), JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
return null();
if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
if (!tokenStream.getToken(&tt))
return null();
if (tt != TOK_NAME) {
report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
return null();
}
} else {
// Keywords cannot be bound to themselves, so an import name
// that is a keyword is a syntax error if it is not followed
// by the keyword 'as'.
if (IsKeyword(importName->name())) {
JSAutoByteString bytes;
if (!AtomToPrintableString(context, importName->name(), &bytes))
return null();
report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
return null();
}
tokenStream.ungetToken();
}
Node bindingName = newName(tokenStream.currentName());
if (!bindingName)
return null();
Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
if (!importSpec)
if (!namedImportsOrNamespaceImport(tt, importSpecSet))
return null();
handler.addList(importSpecSet, importSpec);
bool matched;
if (!tokenStream.matchToken(&matched, TOK_COMMA))
return null();
if (!matched)
break;
}
MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
} else {
if (!namedImportsOrNamespaceImport(tt, importSpecSet))
return null();
}
if (!tokenStream.getToken(&tt))
return null();
if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_CLAUSE);
return null();
}
MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
} else {
if (tt != TOK_STRING) {
report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
return null();
}
} else if (tt == TOK_STRING) {
// Handle the form |import 'a'| by leaving the list empty. This is
// equivalent to |import {} from 'a'|.
importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
} else {
report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
return null();
}
Node moduleSpec = stringLiteral();
@ -4302,8 +4361,7 @@ Parser<ParseHandler>::importDeclaration()
if (!MatchOrInsertSemicolon(tokenStream))
return null();
return handler.newImportDeclaration(importSpecSet, moduleSpec,
TokenPos(begin, pos().end));
return handler.newImportDeclaration(importSpecSet, moduleSpec, TokenPos(begin, pos().end));
}
template<>

View File

@ -635,6 +635,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
Node destructuringExprWithoutYield(YieldHandling yieldHandling, BindData<ParseHandler>* data,
TokenKind tt, unsigned msg);
bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
enum ClassContext { ClassStatement, ClassExpression };
Node classDefinition(YieldHandling yieldHandling, ClassContext classContext);

View File

@ -38,6 +38,18 @@ program([
)
]).assert(Reflect.parse("import a from 'b'"));
program([
importDeclaration(
[
importSpecifier(
ident("*"),
ident("a")
)
],
lit("b")
)
]).assert(Reflect.parse("import * as a from 'b'"));
program([
importDeclaration(
[],
@ -93,6 +105,106 @@ program([
)
]).assert(Reflect.parse("import { as as as } from 'a'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("a")
),
importSpecifier(
ident("*"),
ident("b")
)
],
lit("c")
)
]).assert(Reflect.parse("import a, * as b from 'c'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("d")
)
],
lit("a")
)
]).assert(Reflect.parse("import d, {} from 'a'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("d")
),
importSpecifier(
ident("a"),
ident("a")
)
],
lit("b")
)
]).assert(Reflect.parse("import d, { a } from 'b'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("d")
),
importSpecifier(
ident("a"),
ident("b")
)
],
lit("c")
)
]).assert(Reflect.parse("import d, { a as b } from 'c'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("d")
),
importSpecifier(
ident("a"),
ident("a")
),
importSpecifier(
ident("b"),
ident("b")
),
],
lit("c")
)
]).assert(Reflect.parse("import d, { a, b } from 'c'"));
program([
importDeclaration(
[
importSpecifier(
ident("default"),
ident("d")
),
importSpecifier(
ident("a"),
ident("b")
),
importSpecifier(
ident("c"),
ident("d")
),
],
lit("e")
)
]).assert(Reflect.parse("import d, { a as b, c as d } from 'e'"));
program([
importDeclaration(
[
@ -184,3 +296,43 @@ assertThrowsInstanceOf(function() {
assertThrowsInstanceOf(function() {
Reflect.parse("import { true } from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import a,");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import a, b from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import * as a,");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import * as a, {} from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import as a from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import * a from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import * as from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import , {} from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import d, from 'a'");
}, SyntaxError);
assertThrowsInstanceOf(function() {
Reflect.parse("import * as true from 'b'");
}, SyntaxError);

View File

@ -182,6 +182,7 @@ MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS, 3, JSEXN_SYNTAXERR, "{0} functions must h
MSG_DEF(JSMSG_ARGUMENTS_AND_REST, 0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter")
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initialiser too large")
MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *")
MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
@ -249,7 +250,7 @@ MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 0, JSEXN_SYNTAXERR, "mistyped ; after con
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level")
MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 0, JSEXN_SYNTAXERR, "finally without try")
MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set")
MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT, 2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER, 0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 0, JSEXN_SYNTAXERR, "illegal character")
@ -271,6 +272,8 @@ MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS, 0, JSEXN_SYNTAXERR, "missing octal digits
MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED, 0, JSEXN_SYNTAXERR, "modules are not implemented yet")
MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
MSG_DEF(JSMSG_NAME_AFTER_DOT, 0, JSEXN_SYNTAXERR, "missing name after . operator")
MSG_DEF(JSMSG_NAME_AFTER_IMPORT_STAR_AS, 0, JSEXN_SYNTAXERR, "missing name after import * as")
MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma")
MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name")
MSG_DEF(JSMSG_NO_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class default constructors not yet implemented")

View File

@ -2156,6 +2156,7 @@ bool
ASTSerializer::importDeclaration(ParseNode* pn, MutableHandleValue dst)
{
MOZ_ASSERT(pn->isKind(PNK_IMPORT));
MOZ_ASSERT(pn->isArity(PN_BINARY));
MOZ_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));

View File

@ -187,6 +187,7 @@
macro(signMask, signMask, "signMask") \
macro(source, source, "source") \
macro(stack, stack, "stack") \
macro(star, star, "*") \
macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(static, static_, "static") \
macro(sticky, sticky, "sticky") \

View File

@ -29,11 +29,11 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 285;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 286;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 394,
static_assert(JSErr_Limit == 397,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "