Bug 927116 - Implement parser support for import declarations; r=jorendorff
☠☠ backed out by f37ed8d81612 ☠ ☠
authorEddy Bruel <ejpbruel@mozilla.com>
Wed, 06 Nov 2013 20:04:12 +0100
changeset 153809 13f60271f4f6729541c179a5a1e333ebb98cd6f2
parent 153808 8222e9ae0a215beb1e3536117d4e7446736c59c7
child 153810 17b2babbe34e383633b9cd8c5454f3510452b477
push id35905
push userejpbruel@mozilla.com
push dateWed, 06 Nov 2013 19:04:30 +0000
treeherdermozilla-inbound@13f60271f4f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs927116
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 927116 - Implement parser support for import declarations; r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/js.msg
js/src/vm/CommonPropertyNames.h
js/src/vm/Keywords.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6442,16 +6442,21 @@ frontend::EmitTree(ExclusiveContext *cx,
         break;
 
       case PNK_LET:
         ok = pn->isArity(PN_BINARY)
              ? EmitLet(cx, bce, pn)
              : EmitVariables(cx, bce, pn, InitializeVars);
         break;
 
+      case PNK_IMPORT:
+       // TODO: Implement emitter support for modules
+       bce->reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
+       return false;
+
       case PNK_ARRAYPUSH: {
         int slot;
 
         /*
          * The array object's stack index is in bce->arrayCompDepth. See below
          * under the array initialiser code generator for array comprehension
          * special casing. Note that the array object is a pure stack value,
          * unaliased by blocks, so we can EmitUnaliasedVarOp.
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -290,16 +290,26 @@ class FullParseHandler
 
         list->append(stmt);
     }
 
     ParseNode *newEmptyStatement(const TokenPos &pos) {
         return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode *) nullptr);
     }
 
+    ParseNode *newImportDeclaration(ParseNode *importSpecSet,
+                                    ParseNode *moduleSpec, const TokenPos &pos)
+    {
+        ParseNode *pn = new_<BinaryNode>(PNK_IMPORT, JSOP_NOP, pos,
+                                         importSpecSet, moduleSpec);
+        if (!pn)
+            return null();
+        return pn;
+    }
+
     ParseNode *newExprStatement(ParseNode *expr, uint32_t end) {
         JS_ASSERT(expr->pn_pos.end <= end);
         return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr);
     }
 
     ParseNode *newIfStatement(uint32_t begin, ParseNode *cond, ParseNode *thenBranch,
                               ParseNode *elseBranch)
     {
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -123,16 +123,19 @@ class UpvarCookie
     F(DEBUGGER) \
     F(YIELD) \
     F(YIELD_STAR) \
     F(GENEXP) \
     F(ARRAYCOMP) \
     F(ARRAYPUSH) \
     F(LEXICALSCOPE) \
     F(LET) \
+    F(IMPORT) \
+    F(IMPORT_SPEC_LIST) \
+    F(IMPORT_SPEC) \
     F(SEQ) \
     F(FORIN) \
     F(FOROF) \
     F(FORHEAD) \
     F(ARGSBODY) \
     F(SPREAD) \
     \
     /* Unary operators. */ \
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -3623,16 +3623,144 @@ Parser<FullParseHandler>::letStatement()
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::letStatement()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
+template<typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::importDeclaration()
+{
+    JS_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
+
+    if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
+        report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
+        return null();
+    }
+
+    uint32_t begin = pos().begin;
+    TokenKind tt = tokenStream.getToken();
+
+    Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
+    if (!importSpecSet)
+        return null();
+
+    if (tt == TOK_NAME || tt == TOK_LC) {
+        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
+            // 'a' as the binding name. This is equivalent to
+            // |import { default as a } from 'b'|.
+            Node importName = newName(context->names().default_);
+            if (!importName)
+                return null();
+
+            Node bindingName = newName(tokenStream.currentName());
+            if (!bindingName)
+                return null();
+
+            Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
+            if (!importSpec)
+                return null();
+
+            handler.addList(importSpecSet, importSpec);
+        } else {
+            do {
+                // Handle the forms |import {} from 'a'| and
+                // |import { ..., } from 'a'| (where ... is non empty), by
+                // escaping the loop early if the next token is }.
+                tt = tokenStream.peekToken(TokenStream::KeywordIsName);
+                if (tt == TOK_ERROR)
+                    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)
+                    return null();
+
+                if (tokenStream.getToken() == TOK_NAME &&
+                    tokenStream.currentName() == context->names().as)
+                {
+                    if (tokenStream.getToken() != 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)
+                    return null();
+
+                handler.addList(importSpecSet, importSpec);
+            } while (tokenStream.matchToken(TOK_COMMA));
+
+            MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
+        }
+
+        if (tokenStream.getToken() != TOK_NAME ||
+            tokenStream.currentName() != context->names().from)
+        {
+            report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
+            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();
+        }
+
+        // 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;
+    }
+
+    Node moduleSpec = stringLiteral();
+    if (!moduleSpec)
+        return null();
+
+    if (!MatchOrInsertSemicolon(tokenStream))
+        return null();
+
+    return handler.newImportDeclaration(importSpecSet, moduleSpec,
+                                        TokenPos(begin, pos().end));
+}
+
+template<>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::importDeclaration()
+{
+    JS_ALWAYS_FALSE(abortIfSyntaxParser());
+    return SyntaxParseHandler::NodeFailure;
+}
+
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement()
 {
     tokenStream.ungetToken();
     Node pnexpr = expr();
     if (!pnexpr)
         return null();
@@ -4902,16 +5030,18 @@ Parser<ParseHandler>::statement(bool can
 
         if (!MatchOrInsertSemicolon(tokenStream))
             return null();
         return pn;
       }
 
       case TOK_LET:
         return letStatement();
+      case TOK_IMPORT:
+        return importDeclaration();
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
       case TOK_IF:
         return ifStatement();
       case TOK_DO:
         return doWhileStatement();
       case TOK_WHILE:
         return whileStatement();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -507,16 +507,17 @@ class Parser : private AutoGCRooter, pub
     Node returnStatement();
     Node withStatement();
     Node labeledStatement();
     Node throwStatement();
     Node tryStatement();
     Node debuggerStatement();
 
     Node letStatement();
+    Node importDeclaration();
     Node expressionStatement();
     Node variables(ParseNodeKind kind, bool *psimple = nullptr,
                    StaticBlockObject *blockObj = nullptr,
                    VarContext varContext = HoistVars);
     Node expr();
     Node assignExpr();
     Node assignExprWithoutYield(unsigned err);
     Node yieldExpression();
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -412,8 +412,18 @@ MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPE_OBJEC
 MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 359, 0, JSEXN_RANGEERR, "too many constructor arguments")
 MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 360, 0, JSEXN_RANGEERR, "too many function arguments")
 MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGEE,     361, 2, JSEXN_ERR, "{0} is not a debuggee {1}")
 MSG_DEF(JSMSG_TYPEDOBJECT_NOT_TYPED_OBJECT, 362, 0, JSEXN_ERR, "Expected a typed object")
 MSG_DEF(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, 363, 1, JSEXN_TYPEERR, "No such property: {0}")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 364, 2, JSEXN_TYPEERR, "argument {0} invalid: expected {1}")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 365, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_BAD_TYPE, 366, 0, JSEXN_TYPEERR, "handle moved to destination of incorrect type")
+
+MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 367, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level")
+MSG_DEF(JSMSG_NO_IMPORT_NAME,         368, 0, JSEXN_SYNTAXERR, "missing import name")
+MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 369, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
+MSG_DEF(JSMSG_NO_BINDING_NAME,	      370, 0, JSEXN_SYNTAXERR, "missing binding name")
+MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST, 371, 0, JSEXN_SYNTAXERR, "missing '}' after module specifier list")
+MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 372, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set")
+MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT, 373, 0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
+MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM,   374, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
+MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED,  375, 0, JSEXN_SYNTAXERR, "modules are not implemented yet")
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -10,16 +10,17 @@
 #define vm_CommonPropertyNames_h
 
 #include "jsprototypes.h"
 
 #define FOR_EACH_COMMON_PROPERTYNAME(macro) \
     macro(anonymous, anonymous, "anonymous") \
     macro(apply, apply, "apply") \
     macro(arguments, arguments, "arguments") \
+    macro(as, as, "as") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(buffer, buffer, "buffer") \
     macro(builder, builder, "builder") \
     macro(byteLength, byteLength, "byteLength") \
     macro(byteAlignment, byteAlignment, "byteAlignment") \
     macro(byteOffset, byteOffset, "byteOffset") \
     macro(bytes, bytes, "bytes") \
     macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \
@@ -38,16 +39,17 @@
     macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
     macro(currency, currency, "currency") \
     macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
     macro(std_iterator, std_iterator, "@@iterator") \
     macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
     macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
+    macro(default_, default_, "default") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(done, done, "done") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
@@ -63,16 +65,17 @@
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
     macro(FillTypedArrayWithValue, FillTypedArrayWithValue, "FillTypedArrayWithValue") \
     macro(fix, fix, "fix") \
     macro(float32, float32, "float32") \
     macro(float64, float64, "float64") \
     macro(format, format, "format") \
+    macro(from, from, "from") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
     macro(getPropertyDescriptor, getPropertyDescriptor, "getPropertyDescriptor") \
     macro(global, global, "global") \
     macro(Handle, Handle, "Handle") \
     macro(has, has, "has") \
--- a/js/src/vm/Keywords.h
+++ b/js/src/vm/Keywords.h
@@ -36,22 +36,22 @@
     macro(this, this_, TOK_THIS, JSVERSION_DEFAULT) \
     macro(throw, throw_, TOK_THROW, JSVERSION_DEFAULT) \
     macro(try, try_, TOK_TRY, JSVERSION_DEFAULT) \
     macro(typeof, typeof, TOK_TYPEOF, JSVERSION_DEFAULT) \
     macro(var, var, TOK_VAR, JSVERSION_DEFAULT) \
     macro(void, void_, TOK_VOID, JSVERSION_DEFAULT) \
     macro(while, while_, TOK_WHILE, JSVERSION_DEFAULT) \
     macro(with, with, TOK_WITH, JSVERSION_DEFAULT) \
+    macro(import, import, TOK_IMPORT, JSVERSION_DEFAULT) \
     /* Reserved keywords. */ \
     macro(class, class_, TOK_RESERVED, JSVERSION_DEFAULT) \
     macro(enum, enum_, TOK_RESERVED, JSVERSION_DEFAULT) \
     macro(export, export, TOK_RESERVED, JSVERSION_DEFAULT) \
     macro(extends, extends, TOK_RESERVED, JSVERSION_DEFAULT) \
-    macro(import, import, TOK_RESERVED, JSVERSION_DEFAULT) \
     macro(super, super, TOK_RESERVED, JSVERSION_DEFAULT) \
     /* Future reserved keywords, but only in strict mode. */ \
     macro(implements, implements, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
     macro(interface, interface, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
     macro(package, package, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
     macro(private, private_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
     macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
     macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \