[ES6] Support Module Syntax
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Aug 2015 21:26:49 +0000 (21:26 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Aug 2015 21:26:49 +0000 (21:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147422

Reviewed by Saam Barati.

Source/JavaScriptCore:

This patch introduces ES6 Modules syntax parsing part.
In this patch, ASTBuilder just produces the corresponding nodes to the ES6 Modules syntax,
and this patch does not include the code generator part.

Modules require 2 phase parsing. In the first pass, we just analyze the dependent modules
and do not execute the body or construct the AST. And after analyzing all the dependent
modules, we will parse the dependent modules next.
After all analyzing part is done, we will start the second pass. In the second pass, we
will parse the module, produce the AST, and execute the body.
If we don't do so, we need to create all the ASTs in the module's dependent graph at first
because the given module can be executed after the all dependent modules are executed. It
means that we need to hold so many parser arenas. To avoid this, the first pass only extracts
the dependent modules' information.

In this patch, we don't add this analyzing part yet. This patch only implements the second pass.
This patch aims at just implementing the syntax parsing functionality correctly.
After this patch is landed, we will create the ModuleDependencyAnalyzer that inherits SyntaxChecker
to collect the dependent modules fast[1].

To test the parsing, we added the "checkModuleSyntax" function into jsc shell.
By using this, we can parse the given string as the module.

[1]: https://bugs.webkit.org/show_bug.cgi?id=147353

* bytecompiler/NodesCodegen.cpp:
(JSC::ModuleProgramNode::emitBytecode):
(JSC::ImportDeclarationNode::emitBytecode):
(JSC::ExportAllDeclarationNode::emitBytecode):
(JSC::ExportDefaultDeclarationNode::emitBytecode):
(JSC::ExportLocalDeclarationNode::emitBytecode):
(JSC::ExportNamedDeclarationNode::emitBytecode):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionCheckModuleSyntax):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createModuleSpecifier):
(JSC::ASTBuilder::createImportSpecifier):
(JSC::ASTBuilder::createImportSpecifierList):
(JSC::ASTBuilder::appendImportSpecifier):
(JSC::ASTBuilder::createImportDeclaration):
(JSC::ASTBuilder::createExportAllDeclaration):
(JSC::ASTBuilder::createExportDefaultDeclaration):
(JSC::ASTBuilder::createExportLocalDeclaration):
(JSC::ASTBuilder::createExportNamedDeclaration):
(JSC::ASTBuilder::createExportSpecifier):
(JSC::ASTBuilder::createExportSpecifierList):
(JSC::ASTBuilder::appendExportSpecifier):
* parser/Keywords.table:
* parser/NodeConstructors.h:
(JSC::ModuleSpecifierNode::ModuleSpecifierNode):
(JSC::ImportSpecifierNode::ImportSpecifierNode):
(JSC::ImportDeclarationNode::ImportDeclarationNode):
(JSC::ExportAllDeclarationNode::ExportAllDeclarationNode):
(JSC::ExportDefaultDeclarationNode::ExportDefaultDeclarationNode):
(JSC::ExportLocalDeclarationNode::ExportLocalDeclarationNode):
(JSC::ExportNamedDeclarationNode::ExportNamedDeclarationNode):
(JSC::ExportSpecifierNode::ExportSpecifierNode):
* parser/Nodes.cpp:
(JSC::ModuleProgramNode::ModuleProgramNode):
* parser/Nodes.h:
(JSC::ModuleProgramNode::startColumn):
(JSC::ModuleProgramNode::endColumn):
(JSC::ModuleSpecifierNode::moduleName):
(JSC::ImportSpecifierNode::importedName):
(JSC::ImportSpecifierNode::localName):
(JSC::ImportSpecifierListNode::specifiers):
(JSC::ImportSpecifierListNode::append):
(JSC::ImportDeclarationNode::specifierList):
(JSC::ImportDeclarationNode::moduleSpecifier):
(JSC::ExportAllDeclarationNode::moduleSpecifier):
(JSC::ExportDefaultDeclarationNode::declaration):
(JSC::ExportLocalDeclarationNode::declaration):
(JSC::ExportSpecifierNode::exportedName):
(JSC::ExportSpecifierNode::localName):
(JSC::ExportSpecifierListNode::specifiers):
(JSC::ExportSpecifierListNode::append):
(JSC::ExportNamedDeclarationNode::specifierList):
(JSC::ExportNamedDeclarationNode::moduleSpecifier):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseModuleSourceElements):
(JSC::Parser<LexerType>::parseVariableDeclaration):
(JSC::Parser<LexerType>::parseVariableDeclarationList):
(JSC::Parser<LexerType>::createBindingPattern):
(JSC::Parser<LexerType>::tryParseDestructuringPatternExpression):
(JSC::Parser<LexerType>::parseDestructuringPattern):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseFormalParameters):
(JSC::Parser<LexerType>::parseFunctionParameters):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseClassDeclaration):
(JSC::Parser<LexerType>::parseModuleSpecifier):
(JSC::Parser<LexerType>::parseImportClauseItem):
(JSC::Parser<LexerType>::parseImportDeclaration):
(JSC::Parser<LexerType>::parseExportSpecifier):
(JSC::Parser<LexerType>::parseExportDeclaration):
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/Parser.h:
(JSC::isIdentifierOrKeyword):
(JSC::ModuleScopeData::create):
(JSC::ModuleScopeData::exportedBindings):
(JSC::ModuleScopeData::exportName):
(JSC::ModuleScopeData::exportBinding):
(JSC::Scope::Scope):
(JSC::Scope::setIsModule):
(JSC::Scope::moduleScopeData):
(JSC::Parser::matchContextualKeyword):
(JSC::Parser::matchIdentifierOrKeyword):
(JSC::Parser::isofToken): Deleted.
* parser/ParserModes.h:
* parser/ParserTokens.h:
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createModuleSpecifier):
(JSC::SyntaxChecker::createImportSpecifier):
(JSC::SyntaxChecker::createImportSpecifierList):
(JSC::SyntaxChecker::appendImportSpecifier):
(JSC::SyntaxChecker::createImportDeclaration):
(JSC::SyntaxChecker::createExportAllDeclaration):
(JSC::SyntaxChecker::createExportDefaultDeclaration):
(JSC::SyntaxChecker::createExportLocalDeclaration):
(JSC::SyntaxChecker::createExportNamedDeclaration):
(JSC::SyntaxChecker::createExportSpecifier):
(JSC::SyntaxChecker::createExportSpecifierList):
(JSC::SyntaxChecker::appendExportSpecifier):
* runtime/CommonIdentifiers.cpp:
(JSC::CommonIdentifiers::CommonIdentifiers):
* runtime/CommonIdentifiers.h:
* runtime/Completion.cpp:
(JSC::checkModuleSyntax):
* runtime/Completion.h:
* tests/stress/modules-syntax-error-with-names.js: Added.
(shouldThrow):
* tests/stress/modules-syntax-error.js: Added.
(shouldThrow):
(checkModuleSyntaxError.checkModuleSyntaxError.checkModuleSyntaxError):
* tests/stress/modules-syntax.js: Added.
(prototype.checkModuleSyntax):
(checkModuleSyntax):
* tests/stress/tagged-templates-syntax.js:

LayoutTests:

'export' and 'import' are changed from FutureReservedWord to Keyword in ES6.
http://www.ecma-international.org/ecma-262/6.0/#sec-keywords
And restrict 'super' use under the Script / Module contexts.

* js/dom/reserved-words-as-property-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.10-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt:
* sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.27-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@187890 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/reserved-words-as-property-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.10-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.27-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Keywords.table
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserModes.h
Source/JavaScriptCore/parser/ParserTokens.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/CommonIdentifiers.cpp
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/Completion.cpp
Source/JavaScriptCore/runtime/Completion.h
Source/JavaScriptCore/tests/stress/modules-syntax-error-with-names.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/modules-syntax-error.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/modules-syntax.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/tagged-templates-syntax.js

index 9daefd2..c6004b9 100644 (file)
@@ -1,3 +1,19 @@
+2015-08-04  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Support Module Syntax
+        https://bugs.webkit.org/show_bug.cgi?id=147422
+
+        Reviewed by Saam Barati.
+
+        'export' and 'import' are changed from FutureReservedWord to Keyword in ES6.
+        http://www.ecma-international.org/ecma-262/6.0/#sec-keywords
+        And restrict 'super' use under the Script / Module contexts.
+
+        * js/dom/reserved-words-as-property-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.10-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt:
+        * sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.27-expected.txt:
+
 2015-08-04  Alexey Proskuryakov  <ap@apple.com>
 
         Implement NPAPI redirect handling
index 890de19..6cc26b9 100644 (file)
@@ -1155,36 +1155,36 @@ PASS "use strict";({ enum: 42 }.enum === 42) is true
 PASS (function(){"use strict";({ enum: 42 }.enum === 42)}); true is true
 PASS "use strict";({ get enum(){}, set enum(x){}, parsedOkay: 42 }.parsedOkay === 42) is true
 PASS (function(){"use strict";({ get enum(){}, set enum(x){}, parsedOkay: 42 }.parsedOkay === 42)}); true is true
-PASS var export; true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){var export; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS var export = 42; export === 42 threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){var export = 42; export === 42}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS function g(export){  }; true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){function g(export){  }; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS /export/.test(function g(export){  }) threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){/export/.test(function g(export){  })}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS try{}catch(export){}; true threw exception SyntaxError: Cannot use the reserved word 'export' as a catch variable name..
-PASS (function(){try{}catch(export){}; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a catch variable name..
-PASS function export(){  }; true threw exception SyntaxError: Cannot use the reserved word 'export' as a function name..
-PASS (function(){function export(){  }; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a function name..
+PASS var export; true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){var export; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS var export = 42; export === 42 threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){var export = 42; export === 42}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS function g(export){  }; true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){function g(export){  }; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS /export/.test(function g(export){  }) threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){/export/.test(function g(export){  })}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS try{}catch(export){}; true threw exception SyntaxError: Cannot use the keyword 'export' as a catch variable name..
+PASS (function(){try{}catch(export){}; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a catch variable name..
+PASS function export(){  }; true threw exception SyntaxError: Cannot use the keyword 'export' as a function name..
+PASS (function(){function export(){  }; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a function name..
 PASS ({ "export": 42 }.export === 42) is true
 PASS (function(){({ "export": 42 }.export === 42)}); true is true
 PASS ({ export: 42 }.export === 42) is true
 PASS (function(){({ export: 42 }.export === 42)}); true is true
 PASS ({ get export(){}, set export(x){}, parsedOkay: 42 }.parsedOkay === 42) is true
 PASS (function(){({ get export(){}, set export(x){}, parsedOkay: 42 }.parsedOkay === 42)}); true is true
-PASS "use strict";var export; true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){"use strict";var export; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS "use strict";var export = 42; export === 42 threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){"use strict";var export = 42; export === 42}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS "use strict";function g(export){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){"use strict";function g(export){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS "use strict";/export/.test(function g(export){ "use strict"; }) threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS (function(){"use strict";/export/.test(function g(export){ "use strict"; })}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a variable name..
-PASS "use strict";try{}catch(export){}; true threw exception SyntaxError: Cannot use the reserved word 'export' as a catch variable name..
-PASS (function(){"use strict";try{}catch(export){}; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a catch variable name..
-PASS "use strict";function export(){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'export' as a function name..
-PASS (function(){"use strict";function export(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'export' as a function name..
+PASS "use strict";var export; true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){"use strict";var export; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS "use strict";var export = 42; export === 42 threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){"use strict";var export = 42; export === 42}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS "use strict";function g(export){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){"use strict";function g(export){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS "use strict";/export/.test(function g(export){ "use strict"; }) threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS (function(){"use strict";/export/.test(function g(export){ "use strict"; })}); true threw exception SyntaxError: Cannot use the keyword 'export' as a variable name..
+PASS "use strict";try{}catch(export){}; true threw exception SyntaxError: Cannot use the keyword 'export' as a catch variable name..
+PASS (function(){"use strict";try{}catch(export){}; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a catch variable name..
+PASS "use strict";function export(){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'export' as a function name..
+PASS (function(){"use strict";function export(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'export' as a function name..
 PASS "use strict";({ "export": 42 }.export === 42) is true
 PASS (function(){"use strict";({ "export": 42 }.export === 42)}); true is true
 PASS "use strict";({ export: 42 }.export === 42) is true
@@ -1227,36 +1227,36 @@ PASS "use strict";({ extends: 42 }.extends === 42) is true
 PASS (function(){"use strict";({ extends: 42 }.extends === 42)}); true is true
 PASS "use strict";({ get extends(){}, set extends(x){}, parsedOkay: 42 }.parsedOkay === 42) is true
 PASS (function(){"use strict";({ get extends(){}, set extends(x){}, parsedOkay: 42 }.parsedOkay === 42)}); true is true
-PASS var import; true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){var import; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS var import = 42; import === 42 threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){var import = 42; import === 42}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS function g(import){  }; true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){function g(import){  }; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS /import/.test(function g(import){  }) threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){/import/.test(function g(import){  })}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS try{}catch(import){}; true threw exception SyntaxError: Cannot use the reserved word 'import' as a catch variable name..
-PASS (function(){try{}catch(import){}; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a catch variable name..
-PASS function import(){  }; true threw exception SyntaxError: Cannot use the reserved word 'import' as a function name..
-PASS (function(){function import(){  }; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a function name..
+PASS var import; true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){var import; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS var import = 42; import === 42 threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){var import = 42; import === 42}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS function g(import){  }; true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){function g(import){  }; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS /import/.test(function g(import){  }) threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){/import/.test(function g(import){  })}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS try{}catch(import){}; true threw exception SyntaxError: Cannot use the keyword 'import' as a catch variable name..
+PASS (function(){try{}catch(import){}; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a catch variable name..
+PASS function import(){  }; true threw exception SyntaxError: Cannot use the keyword 'import' as a function name..
+PASS (function(){function import(){  }; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a function name..
 PASS ({ "import": 42 }.import === 42) is true
 PASS (function(){({ "import": 42 }.import === 42)}); true is true
 PASS ({ import: 42 }.import === 42) is true
 PASS (function(){({ import: 42 }.import === 42)}); true is true
 PASS ({ get import(){}, set import(x){}, parsedOkay: 42 }.parsedOkay === 42) is true
 PASS (function(){({ get import(){}, set import(x){}, parsedOkay: 42 }.parsedOkay === 42)}); true is true
-PASS "use strict";var import; true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){"use strict";var import; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS "use strict";var import = 42; import === 42 threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){"use strict";var import = 42; import === 42}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS "use strict";function g(import){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){"use strict";function g(import){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS "use strict";/import/.test(function g(import){ "use strict"; }) threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS (function(){"use strict";/import/.test(function g(import){ "use strict"; })}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a variable name..
-PASS "use strict";try{}catch(import){}; true threw exception SyntaxError: Cannot use the reserved word 'import' as a catch variable name..
-PASS (function(){"use strict";try{}catch(import){}; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a catch variable name..
-PASS "use strict";function import(){ "use strict"; }; true threw exception SyntaxError: Cannot use the reserved word 'import' as a function name..
-PASS (function(){"use strict";function import(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the reserved word 'import' as a function name..
+PASS "use strict";var import; true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){"use strict";var import; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS "use strict";var import = 42; import === 42 threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){"use strict";var import = 42; import === 42}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS "use strict";function g(import){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){"use strict";function g(import){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS "use strict";/import/.test(function g(import){ "use strict"; }) threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS (function(){"use strict";/import/.test(function g(import){ "use strict"; })}); true threw exception SyntaxError: Cannot use the keyword 'import' as a variable name..
+PASS "use strict";try{}catch(import){}; true threw exception SyntaxError: Cannot use the keyword 'import' as a catch variable name..
+PASS (function(){"use strict";try{}catch(import){}; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a catch variable name..
+PASS "use strict";function import(){ "use strict"; }; true threw exception SyntaxError: Cannot use the keyword 'import' as a function name..
+PASS (function(){"use strict";function import(){ "use strict"; }; true}); true threw exception SyntaxError: Cannot use the keyword 'import' as a function name..
 PASS "use strict";({ "import": 42 }.import === 42) is true
 PASS (function(){"use strict";({ "import": 42 }.import === 42)}); true is true
 PASS "use strict";({ import: 42 }.import === 42) is true
index faff0f3..1954511 100644 (file)
@@ -1,3 +1,151 @@
+2015-08-04  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Support Module Syntax
+        https://bugs.webkit.org/show_bug.cgi?id=147422
+
+        Reviewed by Saam Barati.
+
+        This patch introduces ES6 Modules syntax parsing part.
+        In this patch, ASTBuilder just produces the corresponding nodes to the ES6 Modules syntax,
+        and this patch does not include the code generator part.
+
+        Modules require 2 phase parsing. In the first pass, we just analyze the dependent modules
+        and do not execute the body or construct the AST. And after analyzing all the dependent
+        modules, we will parse the dependent modules next.
+        After all analyzing part is done, we will start the second pass. In the second pass, we
+        will parse the module, produce the AST, and execute the body.
+        If we don't do so, we need to create all the ASTs in the module's dependent graph at first
+        because the given module can be executed after the all dependent modules are executed. It
+        means that we need to hold so many parser arenas. To avoid this, the first pass only extracts
+        the dependent modules' information.
+
+        In this patch, we don't add this analyzing part yet. This patch only implements the second pass.
+        This patch aims at just implementing the syntax parsing functionality correctly.
+        After this patch is landed, we will create the ModuleDependencyAnalyzer that inherits SyntaxChecker
+        to collect the dependent modules fast[1].
+
+        To test the parsing, we added the "checkModuleSyntax" function into jsc shell.
+        By using this, we can parse the given string as the module.
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=147353
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ModuleProgramNode::emitBytecode):
+        (JSC::ImportDeclarationNode::emitBytecode):
+        (JSC::ExportAllDeclarationNode::emitBytecode):
+        (JSC::ExportDefaultDeclarationNode::emitBytecode):
+        (JSC::ExportLocalDeclarationNode::emitBytecode):
+        (JSC::ExportNamedDeclarationNode::emitBytecode):
+        * jsc.cpp:
+        (GlobalObject::finishCreation):
+        (functionCheckModuleSyntax):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createModuleSpecifier):
+        (JSC::ASTBuilder::createImportSpecifier):
+        (JSC::ASTBuilder::createImportSpecifierList):
+        (JSC::ASTBuilder::appendImportSpecifier):
+        (JSC::ASTBuilder::createImportDeclaration):
+        (JSC::ASTBuilder::createExportAllDeclaration):
+        (JSC::ASTBuilder::createExportDefaultDeclaration):
+        (JSC::ASTBuilder::createExportLocalDeclaration):
+        (JSC::ASTBuilder::createExportNamedDeclaration):
+        (JSC::ASTBuilder::createExportSpecifier):
+        (JSC::ASTBuilder::createExportSpecifierList):
+        (JSC::ASTBuilder::appendExportSpecifier):
+        * parser/Keywords.table:
+        * parser/NodeConstructors.h:
+        (JSC::ModuleSpecifierNode::ModuleSpecifierNode):
+        (JSC::ImportSpecifierNode::ImportSpecifierNode):
+        (JSC::ImportDeclarationNode::ImportDeclarationNode):
+        (JSC::ExportAllDeclarationNode::ExportAllDeclarationNode):
+        (JSC::ExportDefaultDeclarationNode::ExportDefaultDeclarationNode):
+        (JSC::ExportLocalDeclarationNode::ExportLocalDeclarationNode):
+        (JSC::ExportNamedDeclarationNode::ExportNamedDeclarationNode):
+        (JSC::ExportSpecifierNode::ExportSpecifierNode):
+        * parser/Nodes.cpp:
+        (JSC::ModuleProgramNode::ModuleProgramNode):
+        * parser/Nodes.h:
+        (JSC::ModuleProgramNode::startColumn):
+        (JSC::ModuleProgramNode::endColumn):
+        (JSC::ModuleSpecifierNode::moduleName):
+        (JSC::ImportSpecifierNode::importedName):
+        (JSC::ImportSpecifierNode::localName):
+        (JSC::ImportSpecifierListNode::specifiers):
+        (JSC::ImportSpecifierListNode::append):
+        (JSC::ImportDeclarationNode::specifierList):
+        (JSC::ImportDeclarationNode::moduleSpecifier):
+        (JSC::ExportAllDeclarationNode::moduleSpecifier):
+        (JSC::ExportDefaultDeclarationNode::declaration):
+        (JSC::ExportLocalDeclarationNode::declaration):
+        (JSC::ExportSpecifierNode::exportedName):
+        (JSC::ExportSpecifierNode::localName):
+        (JSC::ExportSpecifierListNode::specifiers):
+        (JSC::ExportSpecifierListNode::append):
+        (JSC::ExportNamedDeclarationNode::specifierList):
+        (JSC::ExportNamedDeclarationNode::moduleSpecifier):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::Parser):
+        (JSC::Parser<LexerType>::parseInner):
+        (JSC::Parser<LexerType>::parseModuleSourceElements):
+        (JSC::Parser<LexerType>::parseVariableDeclaration):
+        (JSC::Parser<LexerType>::parseVariableDeclarationList):
+        (JSC::Parser<LexerType>::createBindingPattern):
+        (JSC::Parser<LexerType>::tryParseDestructuringPatternExpression):
+        (JSC::Parser<LexerType>::parseDestructuringPattern):
+        (JSC::Parser<LexerType>::parseForStatement):
+        (JSC::Parser<LexerType>::parseFormalParameters):
+        (JSC::Parser<LexerType>::parseFunctionParameters):
+        (JSC::Parser<LexerType>::parseFunctionDeclaration):
+        (JSC::Parser<LexerType>::parseClassDeclaration):
+        (JSC::Parser<LexerType>::parseModuleSpecifier):
+        (JSC::Parser<LexerType>::parseImportClauseItem):
+        (JSC::Parser<LexerType>::parseImportDeclaration):
+        (JSC::Parser<LexerType>::parseExportSpecifier):
+        (JSC::Parser<LexerType>::parseExportDeclaration):
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        * parser/Parser.h:
+        (JSC::isIdentifierOrKeyword):
+        (JSC::ModuleScopeData::create):
+        (JSC::ModuleScopeData::exportedBindings):
+        (JSC::ModuleScopeData::exportName):
+        (JSC::ModuleScopeData::exportBinding):
+        (JSC::Scope::Scope):
+        (JSC::Scope::setIsModule):
+        (JSC::Scope::moduleScopeData):
+        (JSC::Parser::matchContextualKeyword):
+        (JSC::Parser::matchIdentifierOrKeyword):
+        (JSC::Parser::isofToken): Deleted.
+        * parser/ParserModes.h:
+        * parser/ParserTokens.h:
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createModuleSpecifier):
+        (JSC::SyntaxChecker::createImportSpecifier):
+        (JSC::SyntaxChecker::createImportSpecifierList):
+        (JSC::SyntaxChecker::appendImportSpecifier):
+        (JSC::SyntaxChecker::createImportDeclaration):
+        (JSC::SyntaxChecker::createExportAllDeclaration):
+        (JSC::SyntaxChecker::createExportDefaultDeclaration):
+        (JSC::SyntaxChecker::createExportLocalDeclaration):
+        (JSC::SyntaxChecker::createExportNamedDeclaration):
+        (JSC::SyntaxChecker::createExportSpecifier):
+        (JSC::SyntaxChecker::createExportSpecifierList):
+        (JSC::SyntaxChecker::appendExportSpecifier):
+        * runtime/CommonIdentifiers.cpp:
+        (JSC::CommonIdentifiers::CommonIdentifiers):
+        * runtime/CommonIdentifiers.h:
+        * runtime/Completion.cpp:
+        (JSC::checkModuleSyntax):
+        * runtime/Completion.h:
+        * tests/stress/modules-syntax-error-with-names.js: Added.
+        (shouldThrow):
+        * tests/stress/modules-syntax-error.js: Added.
+        (shouldThrow):
+        (checkModuleSyntaxError.checkModuleSyntaxError.checkModuleSyntaxError):
+        * tests/stress/modules-syntax.js: Added.
+        (prototype.checkModuleSyntax):
+        (checkModuleSyntax):
+        * tests/stress/tagged-templates-syntax.js:
+
 2015-08-03  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         Introduce COMPILER(GCC_OR_CLANG) guard and make COMPILER(GCC) true only for GCC
index 9d51fe9..c2d6928 100644 (file)
@@ -2888,6 +2888,12 @@ void ProgramNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
     generator.emitEnd(dstRegister.get());
 }
 
+// ------------------------------ ModuleProgramNode --------------------
+
+void ModuleProgramNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
 // ------------------------------ EvalNode -----------------------------
 
 void EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
@@ -3038,7 +3044,37 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     return generator.moveToDestinationIfNeeded(dst, constructor.get());
 }
 #endif
-    
+
+// ------------------------------ ImportDeclarationNode -----------------------
+
+void ImportDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
+// ------------------------------ ExportAllDeclarationNode --------------------
+
+void ExportAllDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
+// ------------------------------ ExportDefaultDeclarationNode ----------------
+
+void ExportDefaultDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
+// ------------------------------ ExportLocalDeclarationNode ------------------
+
+void ExportLocalDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
+// ------------------------------ ExportNamedDeclarationNode ------------------
+
+void ExportNamedDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*)
+{
+}
+
 // ------------------------------ DestructuringAssignmentNode -----------------
 RegisterID* DestructuringAssignmentNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
index cee8f67..318718a 100644 (file)
@@ -497,6 +497,9 @@ static EncodedJSValue JSC_HOST_CALL functionEnableExceptionFuzz(ExecState*);
 #if ENABLE(WEBASSEMBLY)
 static EncodedJSValue JSC_HOST_CALL functionLoadWebAssembly(ExecState*);
 #endif
+#if ENABLE(ES6_MODULES)
+static EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState*);
+#endif
 
 #if ENABLE(SAMPLING_FLAGS)
 static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
@@ -663,6 +666,9 @@ protected:
 #if ENABLE(WEBASSEMBLY)
         addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 1);
 #endif
+#if ENABLE(ES6_MODULES)
+        addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
+#endif
 
         JSArray* array = constructEmptyArray(globalExec(), 0);
         for (size_t i = 0; i < arguments.size(); ++i)
@@ -1209,6 +1215,24 @@ EncodedJSValue JSC_HOST_CALL functionLoadWebAssembly(ExecState* exec)
 }
 #endif
 
+#if ENABLE(ES6_MODULES)
+EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState* exec)
+{
+    String source = exec->argument(0).toString(exec)->value(exec);
+
+    StopWatch stopWatch;
+    stopWatch.start();
+
+    ParserError error;
+    bool validSyntax = checkModuleSyntax(exec->vm(), makeSource(source), error);
+    stopWatch.stop();
+
+    if (!validSyntax)
+        exec->vm().throwException(exec, jsNontrivialString(exec, toString("SyntaxError: ", error.message(), ":", error.line())));
+    return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
+}
+#endif
+
 // Use SEH for Release builds only to get rid of the crash report dialog
 // (luckily the same tests fail in Release and Debug builds so far). Need to
 // be in a separate main function because the jscmain function requires object
index afdba10..02c6ba4 100644 (file)
@@ -113,6 +113,11 @@ public:
 #if ENABLE(ES6_CLASS_SYNTAX)
     typedef ClassExprNode* ClassExpression;
 #endif
+    typedef ModuleSpecifierNode* ModuleSpecifier;
+    typedef ImportSpecifierNode* ImportSpecifier;
+    typedef ImportSpecifierListNode* ImportSpecifierList;
+    typedef ExportSpecifierNode* ExportSpecifier;
+    typedef ExportSpecifierListNode* ExportSpecifierList;
     typedef StatementNode* Statement;
     typedef ClauseListNode* ClauseList;
     typedef CaseClauseNode* Clause;
@@ -614,7 +619,67 @@ public:
         result->setLoc(startLine, endLine, location.startOffset, location.lineStartOffset);
         return result;
     }
-    
+
+    ModuleSpecifierNode* createModuleSpecifier(const JSTokenLocation& location, const Identifier& moduleName)
+    {
+        return new (m_parserArena) ModuleSpecifierNode(location, moduleName);
+    }
+
+    ImportSpecifierNode* createImportSpecifier(const JSTokenLocation& location, const Identifier& importedName, const Identifier& localName)
+    {
+        return new (m_parserArena) ImportSpecifierNode(location, importedName, localName);
+    }
+
+    ImportSpecifierListNode* createImportSpecifierList()
+    {
+        return new (m_parserArena) ImportSpecifierListNode();
+    }
+
+    void appendImportSpecifier(ImportSpecifierListNode* specifierList, ImportSpecifierNode* specifier)
+    {
+        specifierList->append(specifier);
+    }
+
+    StatementNode* createImportDeclaration(const JSTokenLocation& location, ImportSpecifierListNode* importSpecifierList, ModuleSpecifierNode* moduleSpecifier)
+    {
+        return new (m_parserArena) ImportDeclarationNode(location, importSpecifierList, moduleSpecifier);
+    }
+
+    StatementNode* createExportAllDeclaration(const JSTokenLocation& location, ModuleSpecifierNode* moduleSpecifier)
+    {
+        return new (m_parserArena) ExportAllDeclarationNode(location, moduleSpecifier);
+    }
+
+    StatementNode* createExportDefaultDeclaration(const JSTokenLocation& location, StatementNode* declaration)
+    {
+        return new (m_parserArena) ExportDefaultDeclarationNode(location, declaration);
+    }
+
+    StatementNode* createExportLocalDeclaration(const JSTokenLocation& location, StatementNode* declaration)
+    {
+        return new (m_parserArena) ExportLocalDeclarationNode(location, declaration);
+    }
+
+    StatementNode* createExportNamedDeclaration(const JSTokenLocation& location, ExportSpecifierListNode* exportSpecifierList, ModuleSpecifierNode* moduleSpecifier)
+    {
+        return new (m_parserArena) ExportNamedDeclarationNode(location, exportSpecifierList, moduleSpecifier);
+    }
+
+    ExportSpecifierNode* createExportSpecifier(const JSTokenLocation& location, const Identifier& localName, const Identifier& exportedName)
+    {
+        return new (m_parserArena) ExportSpecifierNode(location, localName, exportedName);
+    }
+
+    ExportSpecifierListNode* createExportSpecifierList()
+    {
+        return new (m_parserArena) ExportSpecifierListNode();
+    }
+
+    void appendExportSpecifier(ExportSpecifierListNode* specifierList, ExportSpecifierNode* specifier)
+    {
+        specifierList->append(specifier);
+    }
+
     void appendStatement(JSC::SourceElements* elements, JSC::StatementNode* statement)
     {
         elements->append(statement);
index 29cfd14..e2ba528 100644 (file)
@@ -41,8 +41,8 @@ debugger      DEBUGGER
 
 # Reserved for future use.
 enum            RESERVED
-export          RESERVED
-import          RESERVED
+export          EXPORT
+import          IMPORT
 
 # Reserved for future use in strict code.
 implements      RESERVED_IF_STRICT
index dd20b8f..4a35379 100644 (file)
@@ -733,6 +733,58 @@ namespace JSC {
     {
     }
 
+    inline ModuleSpecifierNode::ModuleSpecifierNode(const JSTokenLocation& location, const Identifier& moduleName)
+        : Node(location)
+        , m_moduleName(moduleName)
+    {
+    }
+
+    inline ImportSpecifierNode::ImportSpecifierNode(const JSTokenLocation& location, const Identifier& importedName, const Identifier& localName)
+        : Node(location)
+        , m_importedName(importedName)
+        , m_localName(localName)
+    {
+    }
+
+    inline ImportDeclarationNode::ImportDeclarationNode(const JSTokenLocation& location, ImportSpecifierListNode* importSpecifierList, ModuleSpecifierNode* moduleSpecifier)
+        : StatementNode(location)
+        , m_specifierList(importSpecifierList)
+        , m_moduleSpecifier(moduleSpecifier)
+    {
+    }
+
+    inline ExportAllDeclarationNode::ExportAllDeclarationNode(const JSTokenLocation& location, ModuleSpecifierNode* moduleSpecifier)
+        : StatementNode(location)
+        , m_moduleSpecifier(moduleSpecifier)
+    {
+    }
+
+    inline ExportDefaultDeclarationNode::ExportDefaultDeclarationNode(const JSTokenLocation& location, StatementNode* declaration)
+        : StatementNode(location)
+        , m_declaration(declaration)
+    {
+    }
+
+    inline ExportLocalDeclarationNode::ExportLocalDeclarationNode(const JSTokenLocation& location, StatementNode* declaration)
+        : StatementNode(location)
+        , m_declaration(declaration)
+    {
+    }
+
+    inline ExportNamedDeclarationNode::ExportNamedDeclarationNode(const JSTokenLocation& location, ExportSpecifierListNode* exportSpecifierList, ModuleSpecifierNode* moduleSpecifier)
+        : StatementNode(location)
+        , m_specifierList(exportSpecifierList)
+        , m_moduleSpecifier(moduleSpecifier)
+    {
+    }
+
+    inline ExportSpecifierNode::ExportSpecifierNode(const JSTokenLocation& location, const Identifier& localName, const Identifier& exportedName)
+        : Node(location)
+        , m_localName(localName)
+        , m_exportedName(exportedName)
+    {
+    }
+
     inline EmptyVarExpression::EmptyVarExpression(const JSTokenLocation& location, const Identifier& ident)
         : ExpressionNode(location)
         , m_ident(ident)
index 43d7e05..99154b9 100644 (file)
@@ -129,6 +129,15 @@ void ProgramNode::setClosedVariables(Vector<RefPtr<UniquedStringImpl>>&& closedV
     m_closedVariables = WTF::move(closedVariables);
 }
 
+// ------------------------------ ModuleProgramNode -----------------------------
+
+ModuleProgramNode::ModuleProgramNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, int numConstants)
+    : ScopeNode(parserArena, startLocation, endLocation, source, children, varEnvironment, funcStack, lexicalVariables, features, numConstants)
+    , m_startColumn(startColumn)
+    , m_endColumn(endColumn)
+{
+}
+
 // ------------------------------ EvalNode -----------------------------
 
 EvalNode::EvalNode(ParserArena& parserArena, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned, unsigned endColumn, SourceElements* children, VariableEnvironment& varEnvironment, FunctionStack& funcStack, VariableEnvironment& lexicalVariables, FunctionParameters*, const SourceCode& source, CodeFeatures features, int numConstants)
index 000d860..ac5fb11 100644 (file)
@@ -1622,6 +1622,143 @@ namespace JSC {
         unsigned m_endColumn;
     };
 
+    class ModuleProgramNode : public ScopeNode {
+    public:
+        ModuleProgramNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, SourceElements*, VariableEnvironment&, FunctionStack&, VariableEnvironment&, FunctionParameters*, const SourceCode&, CodeFeatures, int numConstants);
+
+        unsigned startColumn() const { return m_startColumn; }
+        unsigned endColumn() const { return m_endColumn; }
+
+        static const bool scopeIsFunction = false;
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        unsigned m_startColumn;
+        unsigned m_endColumn;
+    };
+
+    class ModuleSpecifierNode : public Node {
+    public:
+        ModuleSpecifierNode(const JSTokenLocation&, const Identifier& moduleName);
+
+        const Identifier& moduleName() { return m_moduleName; }
+
+    private:
+        const Identifier& m_moduleName;
+    };
+
+    class ImportSpecifierNode : public Node {
+    public:
+        ImportSpecifierNode(const JSTokenLocation&, const Identifier& importedName, const Identifier& localName);
+
+        const Identifier& importedName() { return m_importedName; }
+        const Identifier& localName() { return m_localName; }
+
+    private:
+        const Identifier& m_importedName;
+        const Identifier& m_localName;
+    };
+
+    class ImportSpecifierListNode : public ParserArenaDeletable {
+    public:
+        typedef Vector<ImportSpecifierNode*, 3> Specifiers;
+
+        const Specifiers& specifiers() const { return m_specifiers; }
+        void append(ImportSpecifierNode* specifier)
+        {
+            m_specifiers.append(specifier);
+        }
+
+    private:
+        Specifiers m_specifiers;
+    };
+
+    class ImportDeclarationNode : public StatementNode {
+    public:
+        ImportDeclarationNode(const JSTokenLocation&, ImportSpecifierListNode*, ModuleSpecifierNode*);
+
+        ImportSpecifierListNode* specifierList() const { return m_specifierList; }
+        ModuleSpecifierNode* moduleSpecifier() const { return m_moduleSpecifier; }
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+
+        ImportSpecifierListNode* m_specifierList;
+        ModuleSpecifierNode* m_moduleSpecifier;
+    };
+
+    class ExportAllDeclarationNode : public StatementNode {
+    public:
+        ExportAllDeclarationNode(const JSTokenLocation&, ModuleSpecifierNode*);
+
+        ModuleSpecifierNode* moduleSpecifier() const { return m_moduleSpecifier; }
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        ModuleSpecifierNode* m_moduleSpecifier;
+    };
+
+    class ExportDefaultDeclarationNode : public StatementNode {
+    public:
+        ExportDefaultDeclarationNode(const JSTokenLocation&, StatementNode*);
+
+        const StatementNode& declaration() const { return *m_declaration; }
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        StatementNode* m_declaration;
+    };
+
+    class ExportLocalDeclarationNode : public StatementNode {
+    public:
+        ExportLocalDeclarationNode(const JSTokenLocation&, StatementNode*);
+
+        const StatementNode& declaration() const { return *m_declaration; }
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        StatementNode* m_declaration;
+    };
+
+    class ExportSpecifierNode : public Node {
+    public:
+        ExportSpecifierNode(const JSTokenLocation&, const Identifier& localName, const Identifier& exportedName);
+
+        const Identifier& exportedName() { return m_exportedName; }
+        const Identifier& localName() { return m_localName; }
+
+    private:
+        const Identifier& m_localName;
+        const Identifier& m_exportedName;
+    };
+
+    class ExportSpecifierListNode : public ParserArenaDeletable {
+    public:
+        typedef Vector<ExportSpecifierNode*, 3> Specifiers;
+
+        const Specifiers& specifiers() const { return m_specifiers; }
+        void append(ExportSpecifierNode* specifier)
+        {
+            m_specifiers.append(specifier);
+        }
+
+    private:
+        Specifiers m_specifiers;
+    };
+
+    class ExportNamedDeclarationNode : public StatementNode {
+    public:
+        ExportNamedDeclarationNode(const JSTokenLocation&, ExportSpecifierListNode*, ModuleSpecifierNode*);
+
+        ExportSpecifierListNode* specifierList() const { return m_specifierList; }
+        ModuleSpecifierNode* moduleSpecifier() const { return m_moduleSpecifier; }
+
+    private:
+        virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+        ExportSpecifierListNode* m_specifierList;
+        ModuleSpecifierNode* m_moduleSpecifier { nullptr };
+    };
+
     class FunctionParameters : public ParserArenaDeletable {
     public:
         FunctionParameters();
index 0faaaf9..acc0188 100644 (file)
@@ -209,6 +209,7 @@ Parser<LexerType>::Parser(
     , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin)
     , m_defaultConstructorKind(defaultConstructorKind)
     , m_thisTDZMode(thisTDZMode)
+    , m_codeType(codeType)
 {
     m_lexer = std::make_unique<LexerType>(vm, builtinMode);
     m_lexer->setCode(source, &m_parserArena);
@@ -220,6 +221,8 @@ Parser<LexerType>::Parser(
     ScopeRef scope = pushScope();
     if (codeType == JSParserCodeType::Function)
         scope->setIsFunction();
+    if (codeType == JSParserCodeType::Module)
+        scope->setIsModule();
     if (strictMode == JSParserStrictMode::Strict)
         scope->setStrictMode();
 
@@ -267,6 +270,10 @@ String Parser<LexerType>::parseInner(const Identifier& calleeName, FunctionParse
     if (!hasError()) {
         if (isArrowFunctionBodyExpression)
             sourceElements = parseArrowFunctionSingleExpressionBodySourceElements(context);
+#if ENABLE(ES6_MODULES)
+        else if (m_codeType == JSParserCodeType::Module)
+            sourceElements = parseModuleSourceElements(context);
+#endif
         else
             sourceElements = parseSourceElements(context, CheckForStrictMode);
     }
@@ -403,6 +410,45 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseSourceEl
     propagateError();
     return sourceElements;
 }
+
+template <typename LexerType>
+template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseModuleSourceElements(TreeBuilder& context)
+{
+    TreeSourceElements sourceElements = context.createSourceElements();
+
+    // FIXME: When some sort of ModuleAnalyzer TreeBuilder is landed,
+    // this SyntaxChecker will be replaced with typedef under the TreeBuilder,
+    // like TreeBuilder::ModuleStatementItemBuilder.
+    // https://bugs.webkit.org/show_bug.cgi?id=147353
+    SyntaxChecker moduleStatementItemBuilder(const_cast<VM*>(m_vm), m_lexer.get());
+
+    while (true) {
+        if (match(IMPORT) || match(EXPORT)) {
+            TreeStatement statement = 0;
+            if (match(IMPORT))
+                statement = parseImportDeclaration(context);
+            else
+                statement = parseExportDeclaration(context);
+
+            if (!statement)
+                break;
+            context.appendStatement(sourceElements, statement);
+        } else {
+            const Identifier* directive = 0;
+            unsigned directiveLiteralLength = 0;
+            if (!parseStatementListItem(moduleStatementItemBuilder, directive, &directiveLiteralLength))
+                break;
+        }
+    }
+
+    propagateError();
+
+    for (const auto& uid : currentScope()->moduleScopeData().exportedBindings())
+        semanticFailIfFalse(currentScope()->hasDeclaredVariable(uid) || currentScope()->hasLexicallyDeclaredVariable(uid), "Exported binding '", uid.get(), "' needs to refer to a top-level declared variable");
+
+    return sourceElements;
+}
+
 template <typename LexerType>
 template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength)
 {
@@ -451,7 +497,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVariableDeclaration(TreeBuilder& context, DeclarationType declarationType)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVariableDeclaration(TreeBuilder& context, DeclarationType declarationType, ExportType exportType)
 {
     ASSERT(match(VAR) || match(LET) || match(CONSTTOKEN));
     JSTokenLocation location(tokenLocation());
@@ -462,7 +508,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseVariableDecla
     TreeExpression scratch2 = 0;
     JSTextPosition scratch3;
     bool scratchBool;
-    TreeExpression variableDecls = parseVariableDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext, declarationType, scratchBool);
+    TreeExpression variableDecls = parseVariableDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext, declarationType, exportType, scratchBool);
     propagateError();
     failIfFalse(autoSemiColon(), "Expected ';' after variable declaration");
     
@@ -517,7 +563,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseWhileStatemen
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext, DeclarationType declarationType, bool& forLoopConstDoesNotHaveInitializer)
+template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext, DeclarationType declarationType, ExportType exportType, bool& forLoopConstDoesNotHaveInitializer)
 {
     ASSERT(declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::VarDeclaration || declarationType == DeclarationType::ConstDeclaration);
     TreeExpression head = 0;
@@ -555,6 +601,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDecl
                     RELEASE_ASSERT_NOT_REACHED();
                 }
             }
+            semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*name), "Cannot export a duplicate name '", name->impl(), "'");
+
             if (hasInitializer) {
                 JSTextPosition varDivot = tokenStartPosition() + 1;
                 initStart = tokenStartPosition();
@@ -576,7 +624,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDecl
             }
         } else {
             lastIdent = 0;
-            auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), nullptr, nullptr, assignmentContext);
+            auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), exportType, nullptr, nullptr, assignmentContext);
             failIfFalse(pattern, "Cannot parse this destructuring pattern");
             hasInitializer = match(EQUAL);
             failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration");
@@ -604,7 +652,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseVariableDecl
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, int depth, JSToken token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier)
 {
     ASSERT(!name.isNull());
     
@@ -656,6 +704,8 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::createB
             }
         }
     }
+
+    semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(name), "Cannot export a duplicate name '", name.impl(), "'");
     return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext);
 }
 
@@ -691,11 +741,11 @@ template <class TreeBuilder> TreeSourceElements Parser<LexerType>::parseArrowFun
 template <typename LexerType>
 template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::tryParseDestructuringPatternExpression(TreeBuilder& context, AssignmentContext bindingContext)
 {
-    return parseDestructuringPattern(context, DestructureToExpressions, nullptr, nullptr, bindingContext);
+    return parseDestructuringPattern(context, DestructureToExpressions, ExportType::NotExported, nullptr, nullptr, bindingContext);
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
+template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth)
 {
     failIfStackOverflow();
     int nonLHSCount = m_nonLHSCount;
@@ -724,7 +774,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             if (UNLIKELY(match(DOTDOTDOT))) {
                 JSTokenLocation location = m_token.m_location;
                 next();
-                auto innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
                 if (kind == DestructureToExpressions && !innerPattern)
                     return 0;
                 failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -737,7 +787,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             }
 
             JSTokenLocation location = m_token.m_location;
-            auto innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+            auto innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
             failIfFalse(innerPattern, "Cannot parse this destructuring pattern");
@@ -774,9 +824,9 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                 JSToken identifierToken = m_token;
                 next();
                 if (consume(COLON))
-                    innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                    innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
                 else
-                    innerPattern = createBindingPattern(context, kind, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier);
+                    innerPattern = createBindingPattern(context, kind, exportType, *propertyName, depth + 1, identifierToken, bindingContext, duplicateIdentifier);
             } else {
                 JSTokenType tokenType = m_token.m_type;
                 switch (m_token.m_type) {
@@ -807,7 +857,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
                     
                     failWithMessage("Expected a ':' prior to a named destructuring property");
                 }
-                innerPattern = parseDestructuringPattern(context, kind, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
+                innerPattern = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1);
             }
             if (kind == DestructureToExpressions && !innerPattern)
                 return 0;
@@ -832,7 +882,7 @@ template <class TreeBuilder> TreeDestructuringPattern Parser<LexerType>::parseDe
             failWithMessage("Expected a parameter pattern or a ')' in parameter list");
         }
         failIfTrue(match(LET) && (kind == DestructureToLet || kind == DestructureToConst), "Can't use 'let' as an identifier name for a LexicalDeclaration");
-        pattern = createBindingPattern(context, kind, *m_token.m_data.ident, depth, m_token, bindingContext, duplicateIdentifier);
+        pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, depth, m_token, bindingContext, duplicateIdentifier);
         next();
         break;
     }
@@ -913,7 +963,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseForStatement(
             declarationType = DeclarationType::ConstDeclaration;
         else
             RELEASE_ASSERT_NOT_REACHED();
-        decls = parseVariableDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext, declarationType, forLoopConstDoesNotHaveInitializer);
+        decls = parseVariableDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext, declarationType, ExportType::NotExported, forLoopConstDoesNotHaveInitializer);
         m_allowsIn = true;
         propagateError();
 
@@ -1457,7 +1507,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeB
 
     const Identifier* duplicateParameter = nullptr;
     bool hasDestructuringPattern = false;
-    auto parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter, &hasDestructuringPattern);
+    auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
     failIfFalse(parameter, "Cannot parse parameter pattern");
     auto defaultValue = parseDefaultValueForDestructuringPattern(context);
     propagateError();
@@ -1465,7 +1515,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFormalParameters(TreeB
     context.appendParameter(list, parameter, defaultValue);
     parameterCount++;
     while (consume(COMMA)) {
-        parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter, &hasDestructuringPattern);
+        parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
         failIfFalse(parameter, "Cannot parse parameter pattern");
         defaultValue = parseDefaultValueForDestructuringPattern(context);
         propagateError();
@@ -1546,7 +1596,7 @@ template <typename LexerType> template <class TreeBuilder> int Parser<LexerType>
                 consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration");
             } else {
                 functionInfo.parameterCount = 1;
-                auto parameter = parseDestructuringPattern(context, DestructureToParameters);
+                auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported);
                 failIfFalse(parameter, "Cannot parse parameter pattern");
                 context.appendParameter(parameterList, parameter, 0);
             }
@@ -1566,7 +1616,7 @@ template <typename LexerType> template <class TreeBuilder> int Parser<LexerType>
     } else if (mode == SetterMode) {
         failIfTrue(match(CLOSEPAREN), "setter functions must have one parameter");
         const Identifier* duplicateParameter = nullptr;
-        auto parameter = parseDestructuringPattern(context, DestructureToParameters, &duplicateParameter);
+        auto parameter = parseDestructuringPattern(context, DestructureToParameters, ExportType::NotExported, &duplicateParameter);
         failIfFalse(parameter, "setter functions must have one parameter");
         auto defaultValue = parseDefaultValueForDestructuringPattern(context);
         propagateError();
@@ -1795,7 +1845,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDeclaration(TreeBuilder& context, ExportType exportType)
 {
     ASSERT(match(FUNCTION));
     JSTokenLocation location(tokenLocation());
@@ -1806,12 +1856,13 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
         functionKeywordStart, functionInfo, StandardFunctionParseType)), "Cannot parse this function");
     failIfFalse(functionInfo.name, "Function statements must have a name");
     failIfTrueIfStrict(declareVariable(functionInfo.name) & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode");
+    semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
     return context.createFuncDeclStatement(location, functionInfo);
 }
 
 #if ENABLE(ES6_CLASS_SYNTAX)
 template <typename LexerType>
-template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context)
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclaration(TreeBuilder& context, ExportType exportType)
 {
     ASSERT(match(CLASSTOKEN));
     JSTokenLocation location(tokenLocation());
@@ -1825,6 +1876,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseClassDeclarat
     DeclarationResultMask declarationResult = declareVariable(info.className, DeclarationType::LetDeclaration);
     if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
         internalFailWithMessage(false, "Cannot declare a class twice: '", info.className->impl(), "'");
+    semanticFailIfTrue(exportType == ExportType::Exported && !currentScope()->moduleScopeData().exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'");
 
     JSTextPosition classEnd = lastTokenEndPosition();
     unsigned classEndLine = tokenLine();
@@ -2149,6 +2201,357 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseIfStatement(T
 }
 
 template <typename LexerType>
+template <class TreeBuilder> typename TreeBuilder::ModuleSpecifier Parser<LexerType>::parseModuleSpecifier(TreeBuilder& context)
+{
+    // ModuleSpecifier represents the module name imported by the script.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-imports
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-exports
+    JSTokenLocation specifierLocation(tokenLocation());
+    failIfFalse(match(STRING), "Imported modules names must be string literals");
+    const Identifier* moduleName = m_token.m_data.ident;
+    next();
+    return context.createModuleSpecifier(specifierLocation, *moduleName);
+}
+
+template <typename LexerType>
+template <class TreeBuilder> typename TreeBuilder::ImportSpecifier Parser<LexerType>::parseImportClauseItem(TreeBuilder& context, ImportSpecifierType specifierType)
+{
+    // Produced node is the item of the ImportClause.
+    // That is the ImportSpecifier, ImportedDefaultBinding or NameSpaceImport.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-imports
+    JSTokenLocation specifierLocation(tokenLocation());
+    JSToken localNameToken;
+    const Identifier* importedName = nullptr;
+    const Identifier* localName = nullptr;
+
+    switch (specifierType) {
+    case ImportSpecifierType::NamespaceImport: {
+        // NameSpaceImport :
+        // * as ImportedBinding
+        // e.g.
+        //     * as namespace
+        ASSERT(match(TIMES));
+        importedName = &m_vm->propertyNames->timesIdentifier;
+        next();
+
+        failIfFalse(matchContextualKeyword(m_vm->propertyNames->as), "Expected 'as' before imported binding name");
+        next();
+
+        matchOrFail(IDENT, "Expected a variable name for the import declaration");
+        localNameToken = m_token;
+        localName = m_token.m_data.ident;
+        next();
+        break;
+    }
+
+    case ImportSpecifierType::NamedImport: {
+        // ImportSpecifier :
+        // ImportedBinding
+        // IdentifierName as ImportedBinding
+        // e.g.
+        //     A
+        //     A as B
+        ASSERT(matchIdentifierOrKeyword());
+        localNameToken = m_token;
+        localName = m_token.m_data.ident;
+        importedName = localName;
+        next();
+
+        if (matchContextualKeyword(m_vm->propertyNames->as)) {
+            next();
+            matchOrFail(IDENT, "Expected a variable name for the import declaration");
+            localNameToken = m_token;
+            localName = m_token.m_data.ident;
+            next();
+        }
+        break;
+    }
+
+    case ImportSpecifierType::DefaultImport: {
+        // ImportedDefaultBinding :
+        // ImportedBinding
+        ASSERT(match(IDENT));
+        localNameToken = m_token;
+        localName = m_token.m_data.ident;
+        importedName = &m_vm->propertyNames->defaultKeyword;
+        next();
+        break;
+    }
+    }
+
+    semanticFailIfTrue(localNameToken.m_type & KeywordTokenFlag, "Cannot use keyword as imported binding name");
+    DeclarationResultMask declarationResult = declareVariable(localName, DeclarationType::ConstDeclaration);
+    if (declarationResult != DeclarationResult::Valid) {
+        failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an imported binding named ", localName->impl(), " in strict mode");
+        if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
+            internalFailWithMessage(false, "Cannot declare an imported binding name twice: '", localName->impl(), "'");
+    }
+
+    return context.createImportSpecifier(specifierLocation, *importedName, *localName);
+}
+
+template <typename LexerType>
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseImportDeclaration(TreeBuilder& context)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-imports
+    ASSERT(match(IMPORT));
+    JSTokenLocation importLocation(tokenLocation());
+    next();
+
+    auto specifierList = context.createImportSpecifierList();
+
+    if (match(STRING)) {
+        // import ModuleSpecifier ;
+        auto moduleSpecifier = parseModuleSpecifier(context);
+        failIfFalse(moduleSpecifier, "Cannot parse the module name");
+        failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration");
+        return context.createImportDeclaration(importLocation, specifierList, moduleSpecifier);
+    }
+
+    bool isFinishedParsingImport = false;
+    if (match(IDENT)) {
+        // ImportedDefaultBinding :
+        // ImportedBinding
+        auto specifier = parseImportClauseItem(context, ImportSpecifierType::DefaultImport);
+        failIfFalse(specifier, "Cannot parse the default import");
+        context.appendImportSpecifier(specifierList, specifier);
+        if (match(COMMA))
+            next();
+        else
+            isFinishedParsingImport = true;
+    }
+
+    if (!isFinishedParsingImport) {
+        if (match(TIMES)) {
+            // import NameSpaceImport FromClause ;
+            auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamespaceImport);
+            failIfFalse(specifier, "Cannot parse the namespace import");
+            context.appendImportSpecifier(specifierList, specifier);
+        } else if (match(OPENBRACE)) {
+            // NamedImports :
+            // { }
+            // { ImportsList }
+            // { ImportsList , }
+            next();
+
+            while (!match(CLOSEBRACE)) {
+                failIfFalse(matchIdentifierOrKeyword(), "Expected an imported name for the import declaration");
+                auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamedImport);
+                failIfFalse(specifier, "Cannot parse the named import");
+                context.appendImportSpecifier(specifierList, specifier);
+                if (!consume(COMMA))
+                    break;
+            }
+            handleProductionOrFail(CLOSEBRACE, "}", "end", "import list");
+        } else
+            failWithMessage("Expected namespace import or import list");
+    }
+
+    // FromClause :
+    // from ModuleSpecifier
+
+    failIfFalse(matchContextualKeyword(m_vm->propertyNames->from), "Expected 'from' before imported module name");
+    next();
+
+    auto moduleSpecifier = parseModuleSpecifier(context);
+    failIfFalse(moduleSpecifier, "Cannot parse the module name");
+    failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration");
+
+    return context.createImportDeclaration(importLocation, specifierList, moduleSpecifier);
+}
+
+template <typename LexerType>
+template <class TreeBuilder> typename TreeBuilder::ExportSpecifier Parser<LexerType>::parseExportSpecifier(TreeBuilder& context, Vector<const Identifier*>& maybeLocalNames, bool& hasKeywordForLocalBindings)
+{
+    // ExportSpecifier :
+    // IdentifierName
+    // IdentifierName as IdentifierName
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-exports
+    ASSERT(matchIdentifierOrKeyword());
+    JSTokenLocation specifierLocation(tokenLocation());
+    if (m_token.m_type & KeywordTokenFlag)
+        hasKeywordForLocalBindings = true;
+    const Identifier* localName = m_token.m_data.ident;
+    const Identifier* exportedName = localName;
+    next();
+
+    if (matchContextualKeyword(m_vm->propertyNames->as)) {
+        next();
+        failIfFalse(matchIdentifierOrKeyword(), "Expected an exported name for the export declaration");
+        exportedName = m_token.m_data.ident;
+        next();
+    }
+
+    semanticFailIfFalse(currentScope()->moduleScopeData().exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'");
+    maybeLocalNames.append(localName);
+    return context.createExportSpecifier(specifierLocation, *localName, *exportedName);
+}
+
+template <typename LexerType>
+template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclaration(TreeBuilder& context)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-exports
+    ASSERT(match(EXPORT));
+    JSTokenLocation exportLocation(tokenLocation());
+    next();
+
+    switch (m_token.m_type) {
+    case TIMES: {
+        // export * FromClause ;
+        next();
+
+        failIfFalse(matchContextualKeyword(m_vm->propertyNames->from), "Expected 'from' before exported module name");
+        next();
+        auto moduleSpecifier = parseModuleSpecifier(context);
+        failIfFalse(moduleSpecifier, "Cannot parse the 'from' clause");
+        failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
+        return context.createExportAllDeclaration(exportLocation, moduleSpecifier);
+    }
+
+    case DEFAULT: {
+        // export default HoistableDeclaration[Default]
+        // export default ClassDeclaration[Default]
+        // export default [lookahead not-in {function, class}] AssignmentExpression[In] ;
+
+        next();
+
+        TreeStatement result = 0;
+        bool hasName = false;
+        bool isFunctionOrClassDeclaration = false;
+        SavePoint savePoint = createSavePoint();
+        if (match(FUNCTION)
+#if ENABLE(ES6_CLASS_SYNTAX)
+                || match(CLASSTOKEN)
+#endif
+                ) {
+            isFunctionOrClassDeclaration = true;
+            next();
+            // FIXME: When landing ES6 generators, we need to take care of that '*' comes.
+            hasName = match(IDENT);
+            restoreSavePoint(savePoint);
+        }
+
+        if (hasName) {
+            if (match(FUNCTION))
+                result = parseFunctionDeclaration(context);
+#if ENABLE(ES6_CLASS_SYNTAX)
+            else {
+                ASSERT(match(CLASSTOKEN));
+                result = parseClassDeclaration(context);
+            }
+#endif
+        } else {
+            JSTokenLocation location(tokenLocation());
+            JSTextPosition start = tokenStartPosition();
+            TreeExpression expression = parseAssignmentExpression(context);
+            failIfFalse(expression, "Cannot parse expression");
+            result = context.createExprStatement(location, expression, start, tokenEndPosition());
+            if (!isFunctionOrClassDeclaration)
+                failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
+        }
+        failIfFalse(result, "Cannot parse the declaration");
+        semanticFailIfFalse(currentScope()->moduleScopeData().exportName(m_vm->propertyNames->defaultKeyword), "Cannot export 'default' name twice.");
+
+        return context.createExportDefaultDeclaration(exportLocation, result);
+    }
+
+    case OPENBRACE: {
+        // export ExportClause FromClause ;
+        // export ExportClause ;
+        //
+        // ExportClause :
+        // { }
+        // { ExportsList }
+        // { ExportsList , }
+        //
+        // ExportsList :
+        // ExportSpecifier
+        // ExportsList , ExportSpecifier
+
+        next();
+
+        auto specifierList = context.createExportSpecifierList();
+        Vector<const Identifier*> maybeLocalNames;
+
+        bool hasKeywordForLocalBindings = false;
+        while (!match(CLOSEBRACE)) {
+            failIfFalse(matchIdentifierOrKeyword(), "Expected a variable name for the export declaration");
+            auto specifier = parseExportSpecifier(context, maybeLocalNames, hasKeywordForLocalBindings);
+            failIfFalse(specifier, "Cannot parse the named export");
+            context.appendExportSpecifier(specifierList, specifier);
+            if (!consume(COMMA))
+                break;
+        }
+        handleProductionOrFail(CLOSEBRACE, "}", "end", "export list");
+
+        typename TreeBuilder::ModuleSpecifier moduleSpecifier = 0;
+        if (matchContextualKeyword(m_vm->propertyNames->from)) {
+            next();
+            moduleSpecifier = parseModuleSpecifier(context);
+            failIfFalse(moduleSpecifier, "Cannot parse the 'from' clause");
+        }
+        failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration");
+
+        if (!moduleSpecifier) {
+            semanticFailIfTrue(hasKeywordForLocalBindings, "Cannot use keyword as exported variable name");
+            // Since this export declaration does not have module specifier part, it exports the local bindings.
+            // While the export declaration with module specifier does not have any effect on the current module's scope,
+            // the export named declaration without module specifier references the the local binding names.
+            // For example,
+            //   export { A, B, C as D } from "mod"
+            // does not have effect on the current module's scope. But,
+            //   export { A, B, C as D }
+            // will reference the current module's bindings.
+            for (const Identifier* localName : maybeLocalNames) {
+                currentScope()->useVariable(localName, m_vm->propertyNames->eval == *localName);
+                currentScope()->moduleScopeData().exportBinding(*localName);
+            }
+        }
+
+        return context.createExportNamedDeclaration(exportLocation, specifierList, moduleSpecifier);
+    }
+
+    default: {
+        // export VariableStatement
+        // export Declaration
+        TreeStatement result = 0;
+        switch (m_token.m_type) {
+        case VAR:
+            result = parseVariableDeclaration(context, DeclarationType::VarDeclaration, ExportType::Exported);
+            break;
+
+        case CONSTTOKEN:
+            result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration, ExportType::Exported);
+            break;
+
+        case LET:
+            result = parseVariableDeclaration(context, DeclarationType::LetDeclaration, ExportType::Exported);
+            break;
+
+        case FUNCTION:
+            result = parseFunctionDeclaration(context, ExportType::Exported);
+            break;
+
+#if ENABLE(ES6_CLASS_SYNTAX)
+        case CLASSTOKEN:
+            result = parseClassDeclaration(context, ExportType::Exported);
+            break;
+#endif
+
+        default:
+            failWithMessage("Expected either a declaration or a variable statement");
+            break;
+        }
+        failIfFalse(result, "Cannot parse the declaration");
+        return context.createExportLocalDeclaration(exportLocation, result);
+    }
+    }
+
+    RELEASE_ASSERT_NOT_REACHED();
+    return 0;
+}
+
+template <typename LexerType>
 template <class TreeBuilder> TreeExpression Parser<LexerType>::parseExpression(TreeBuilder& context)
 {
     failIfStackOverflow();
@@ -2926,6 +3329,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
     }
 
     if (baseIsSuper) {
+        semanticFailIfFalse(currentScope()->isFunction(), "super is only valid inside functions");
         base = context.createSuperExpr(location);
         next();
         currentScope()->setNeedsSuperBinding();
index 01941fc..37787a1 100644 (file)
@@ -127,6 +127,31 @@ ALWAYS_INLINE static bool isEvalOrArgumentsIdentifier(const VM* vm, const Identi
 {
     return isEval(vm, ident) || isArguments(vm, ident);
 }
+ALWAYS_INLINE static bool isIdentifierOrKeyword(const JSToken& token)
+{
+    return token.m_type == IDENT || token.m_type & KeywordTokenFlag;
+}
+
+class ModuleScopeData : public RefCounted<ModuleScopeData> {
+public:
+    static Ref<ModuleScopeData> create() { return adoptRef(*new ModuleScopeData); }
+
+    const IdentifierSet& exportedBindings() const { return m_exportedBindings; }
+
+    bool exportName(const Identifier& exportedName)
+    {
+        return m_exportedNames.add(exportedName.impl()).isNewEntry;
+    }
+
+    void exportBinding(const Identifier& localName)
+    {
+        m_exportedBindings.add(localName.impl());
+    }
+
+private:
+    IdentifierSet m_exportedNames { };
+    IdentifierSet m_exportedBindings { };
+};
 
 struct Scope {
     Scope(const VM* vm, bool isFunction, bool strictMode)
@@ -164,6 +189,7 @@ struct Scope {
         , m_isValidStrictMode(rhs.m_isValidStrictMode)
         , m_loopDepth(rhs.m_loopDepth)
         , m_switchDepth(rhs.m_switchDepth)
+        , m_moduleScopeData(rhs.m_moduleScopeData)
     {
         if (rhs.m_labels) {
             m_labels = std::make_unique<LabelStack>();
@@ -215,6 +241,11 @@ struct Scope {
         setIsLexicalScope();
     }
 
+    void setIsModule()
+    {
+        m_moduleScopeData = ModuleScopeData::create();
+    }
+
     bool isFunction() const { return m_isFunction; }
     bool isFunctionBoundary() const { return m_isFunctionBoundary; }
 
@@ -236,6 +267,12 @@ struct Scope {
         return m_lexicalVariables;
     }
 
+    ModuleScopeData& moduleScopeData() const
+    {
+        ASSERT(m_moduleScopeData);
+        return *m_moduleScopeData;
+    }
+
     void computeLexicallyCapturedVariablesAndPurgeCandidates()
     {
         // Because variables may be defined at any time in the range of a lexical scope, we must
@@ -532,6 +569,7 @@ private:
     IdentifierSet m_usedVariables;
     IdentifierSet m_closedVariableCandidates;
     IdentifierSet m_writtenVariables;
+    RefPtr<ModuleScopeData> m_moduleScopeData { };
 };
 
 typedef Vector<Scope, 10> ScopeStack;
@@ -847,9 +885,14 @@ private:
         return m_token.m_type == expected;
     }
     
-    ALWAYS_INLINE bool isofToken()
+    ALWAYS_INLINE bool matchContextualKeyword(const Identifier& identifier)
+    {
+        return m_token.m_type == IDENT && *m_token.m_data.ident == identifier;
+    }
+
+    ALWAYS_INLINE bool matchIdentifierOrKeyword()
     {
-        return m_token.m_type == IDENT && *m_token.m_data.ident == m_vm->propertyNames->of;
+        return isIdentifierOrKeyword(m_token);
     }
     
     ALWAYS_INLINE bool isEndOfArrowFunction()
@@ -1007,11 +1050,12 @@ private:
     template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode);
     template <class TreeBuilder> TreeStatement parseStatementListItem(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength);
     template <class TreeBuilder> TreeStatement parseStatement(TreeBuilder&, const Identifier*& directive, unsigned* directiveLiteralLength = 0);
+    enum class ExportType { Exported, NotExported };
 #if ENABLE(ES6_CLASS_SYNTAX)
-    template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&);
+    template <class TreeBuilder> TreeStatement parseClassDeclaration(TreeBuilder&, ExportType = ExportType::NotExported);
 #endif
-    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&);
-    template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType);
+    template <class TreeBuilder> TreeStatement parseFunctionDeclaration(TreeBuilder&, ExportType = ExportType::NotExported);
+    template <class TreeBuilder> TreeStatement parseVariableDeclaration(TreeBuilder&, DeclarationType, ExportType = ExportType::NotExported);
     template <class TreeBuilder> TreeStatement parseDoWhileStatement(TreeBuilder&);
     template <class TreeBuilder> TreeStatement parseWhileStatement(TreeBuilder&);
     template <class TreeBuilder> TreeStatement parseForStatement(TreeBuilder&);
@@ -1047,13 +1091,20 @@ private:
     template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, const JSTokenLocation&, int, int functionKeywordStart, int functionNameStart, int parametersStart, ConstructorKind, FunctionBodyType, unsigned, FunctionParseMode);
     template <class TreeBuilder> ALWAYS_INLINE bool parseFormalParameters(TreeBuilder&, TreeFormalParameterList, unsigned&);
     enum VarDeclarationListContext { ForLoopContext, VarDeclarationContext };
-    template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType, bool& forLoopConstDoesNotHaveInitializer);
+    template <class TreeBuilder> TreeExpression parseVariableDeclarationList(TreeBuilder&, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext, DeclarationType, ExportType, bool& forLoopConstDoesNotHaveInitializer);
     template <class TreeBuilder> TreeSourceElements parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder&);
     template <class TreeBuilder> TreeExpression parseArrowFunctionExpression(TreeBuilder&);
-    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
-    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern createBindingPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier&, int depth, JSToken, AssignmentContext, const Identifier** duplicateIdentifier);
+    template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern parseDestructuringPattern(TreeBuilder&, DestructuringKind, ExportType, const Identifier** duplicateIdentifier = nullptr, bool* hasDestructuringPattern = nullptr, AssignmentContext = AssignmentContext::DeclarationStatement, int depth = 0);
     template <class TreeBuilder> NEVER_INLINE TreeDestructuringPattern tryParseDestructuringPatternExpression(TreeBuilder&, AssignmentContext);
     template <class TreeBuilder> NEVER_INLINE TreeExpression parseDefaultValueForDestructuringPattern(TreeBuilder&);
+    template <class TreeBuilder> TreeSourceElements parseModuleSourceElements(TreeBuilder&);
+    enum class ImportSpecifierType { NamespaceImport, NamedImport, DefaultImport };
+    template <class TreeBuilder> typename TreeBuilder::ImportSpecifier parseImportClauseItem(TreeBuilder&, ImportSpecifierType);
+    template <class TreeBuilder> typename TreeBuilder::ModuleSpecifier parseModuleSpecifier(TreeBuilder&);
+    template <class TreeBuilder> TreeStatement parseImportDeclaration(TreeBuilder&);
+    template <class TreeBuilder> typename TreeBuilder::ExportSpecifier parseExportSpecifier(TreeBuilder& context, Vector<const Identifier*>& maybeLocalNames, bool& hasKeywordForLocalBindings);
+    template <class TreeBuilder> TreeStatement parseExportDeclaration(TreeBuilder&);
 
     template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionParseType);
     
@@ -1181,6 +1232,7 @@ private:
     Vector<RefPtr<UniquedStringImpl>> m_closedVariables;
     CodeFeatures m_features;
     int m_numConstants;
+    JSParserCodeType m_codeType;
     
     struct DepthManager {
         DepthManager(int* depth)
index b124054..c4d6167 100644 (file)
@@ -33,7 +33,7 @@ namespace JSC {
 
 enum class JSParserStrictMode { NotStrict, Strict };
 enum class JSParserBuiltinMode { NotBuiltin, Builtin };
-enum class JSParserCodeType { Program, Function };
+enum class JSParserCodeType { Program, Function, Module };
 
 enum class ConstructorKind { None, Base, Derived };
 enum class SuperBinding { Needed, NotNeeded };
index eb9c668..3dfdc28 100644 (file)
@@ -76,6 +76,8 @@ enum JSTokenType {
     FINALLY,
     DEBUGGER,
     ELSE,
+    IMPORT,
+    EXPORT,
 #if ENABLE(ES6_CLASS_SYNTAX)
     CLASSTOKEN,
     EXTENDS,
index 829493c..f665d31 100644 (file)
@@ -83,7 +83,10 @@ public:
         ClauseListResult, CommaExpr, DestructuringAssignment,
         TemplateStringResult, TemplateStringListResult,
         TemplateExpressionListResult, TemplateExpr,
-        TaggedTemplateExpr
+        TaggedTemplateExpr,
+        ModuleSpecifierResult,
+        ImportSpecifierResult, ImportSpecifierListResult,
+        ExportSpecifierResult, ExportSpecifierListResult
     };
     typedef int ExpressionType;
 
@@ -124,6 +127,11 @@ public:
 #if ENABLE(ES6_CLASS_SYNTAX)
     typedef int ClassExpression;
 #endif
+    typedef int ModuleSpecifier;
+    typedef int ImportSpecifier;
+    typedef int ImportSpecifierList;
+    typedef int ExportSpecifier;
+    typedef int ExportSpecifierList;
     typedef int Statement;
     typedef int ClauseList;
     typedef int Clause;
@@ -251,6 +259,19 @@ public:
     int createThrowStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
     int createDebugger(const JSTokenLocation&, int, int) { return StatementResult; }
     int createConstStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
+    int createModuleSpecifier(const JSTokenLocation&, const Identifier&) { return ModuleSpecifierResult; }
+    ImportSpecifier createImportSpecifier(const JSTokenLocation&, const Identifier&, const Identifier&) { return ImportSpecifierResult; }
+    ImportSpecifierList createImportSpecifierList() { return ImportSpecifierListResult; }
+    void appendImportSpecifier(ImportSpecifierList, ImportSpecifier) { }
+    int createImportDeclaration(const JSTokenLocation&, ImportSpecifierList, ModuleSpecifier) { return StatementResult; }
+    int createExportAllDeclaration(const JSTokenLocation&, ModuleSpecifier) { return StatementResult; }
+    int createExportDefaultDeclaration(const JSTokenLocation&, int) { return StatementResult; }
+    int createExportLocalDeclaration(const JSTokenLocation&, int) { return StatementResult; }
+    int createExportNamedDeclaration(const JSTokenLocation&, ExportSpecifierList, ModuleSpecifier) { return StatementResult; }
+    ExportSpecifier createExportSpecifier(const JSTokenLocation&, const Identifier&, const Identifier&) { return ExportSpecifierResult; }
+    ExportSpecifierList createExportSpecifierList() { return ExportSpecifierListResult; }
+    void appendExportSpecifier(ExportSpecifierList, ExportSpecifier) { }
+
     int appendConstDecl(const JSTokenLocation&, int, const Identifier*, int) { return StatementResult; }
     Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding)
     {
index 21bc4c7..fc6e62c 100644 (file)
@@ -39,6 +39,7 @@ CommonIdentifiers::CommonIdentifiers(VM* vm)
     , underscoreProto(Identifier::fromString(vm, "__proto__"))
     , thisIdentifier(Identifier::fromString(vm, "this"))
     , useStrictIdentifier(Identifier::fromString(vm, "use strict"))
+    , timesIdentifier(Identifier::fromString(vm, "*"))
     , m_builtinNames(new BuiltinNames(vm, this))
     JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(INITIALIZE_KEYWORD)
     JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME)
index 6efcd4c..6451ff3 100644 (file)
@@ -72,6 +72,7 @@
     macro(additionalJettisonReason) \
     macro(anonymous) \
     macro(arguments) \
+    macro(as) \
     macro(assign) \
     macro(back) \
     macro(bind) \
     macro(focus) \
     macro(forEach) \
     macro(forward) \
+    macro(from) \
     macro(fromCharCode) \
     macro(get) \
     macro(global) \
@@ -319,6 +321,7 @@ namespace JSC {
         const Identifier underscoreProto;
         const Identifier thisIdentifier;
         const Identifier useStrictIdentifier;
+        const Identifier timesIdentifier;
     private:
         std::unique_ptr<BuiltinNames> m_builtinNames;
 
index d361537..abaff7e 100644 (file)
@@ -61,6 +61,15 @@ bool checkSyntax(VM& vm, const SourceCode& source, ParserError& error)
         JSParserStrictMode::NotStrict, JSParserCodeType::Program, error);
 }
 
+bool checkModuleSyntax(VM& vm, const SourceCode& source, ParserError& error)
+{
+    JSLockHolder lock(vm);
+    RELEASE_ASSERT(vm.atomicStringTable() == wtfThreadData().atomicStringTable());
+    return !!parse<ModuleProgramNode>(
+        &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
+        JSParserStrictMode::Strict, JSParserCodeType::Module, error);
+}
+
 JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, NakedPtr<Exception>& returnedException)
 {
     JSLockHolder lock(exec);
index 089911e..702ed9f 100644 (file)
@@ -37,6 +37,7 @@ class VM;
 
 JS_EXPORT_PRIVATE bool checkSyntax(VM&, const SourceCode&, ParserError&);
 JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0);
+JS_EXPORT_PRIVATE bool checkModuleSyntax(VM&, const SourceCode&, ParserError&);
 JS_EXPORT_PRIVATE JSValue evaluate(ExecState*, const SourceCode&, JSValue thisValue, NakedPtr<Exception>& returnedException);
 inline JSValue evaluate(ExecState* exec, const SourceCode& sourceCode, JSValue thisValue = JSValue())
 {
diff --git a/Source/JavaScriptCore/tests/stress/modules-syntax-error-with-names.js b/Source/JavaScriptCore/tests/stress/modules-syntax-error-with-names.js
new file mode 100644 (file)
index 0000000..0431c0a
--- /dev/null
@@ -0,0 +1,217 @@
+//@ skip
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function checkModuleSyntaxError(source, errorMessage) {
+    shouldThrow(() => checkModuleSyntax(source), errorMessage);
+}
+
+// --------------- import -------------------
+
+checkModuleSyntaxError(String.raw`
+import A from "Cocoa"
+const A = 20;
+`, `SyntaxError: Cannot declare a const variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+const A = 20;
+import A from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import A from "Cocoa"
+let A = 20;
+`, `SyntaxError: Cannot declare a let variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+let A = 20;
+import A from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import * as A from "Cocoa"
+const A = 20;
+`, `SyntaxError: Cannot declare a const variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+const A = 20;
+import * as A from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import * as A from "Cocoa"
+let A = 20;
+`, `SyntaxError: Cannot declare a let variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+let A = 20;
+import * as A from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import { A } from "Cocoa"
+const A = 20;
+`, `SyntaxError: Cannot declare a const variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+const A = 20;
+import { A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import { A } from "Cocoa"
+let A = 20;
+`, `SyntaxError: Cannot declare a let variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+let A = 20;
+import { A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import { B as A } from "Cocoa"
+const A = 20;
+`, `SyntaxError: Cannot declare a const variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+const A = 20;
+import { B as A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import { B as A } from "Cocoa"
+let A = 20;
+`, `SyntaxError: Cannot declare a let variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+let A = 20;
+import { B as A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:3`);
+
+checkModuleSyntax(String.raw`
+import { A as B } from "Cocoa"
+const A = 20;
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntax(String.raw`
+const A = 20;
+import { A as B } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntax(String.raw`
+import { A as B } from "Cocoa"
+let A = 20;
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntax(String.raw`
+let A = 20;
+import { A as B } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { A, B as A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { A, A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { C as A, B as A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import A, { A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import A, { B as A } from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import A, * as A from "Cocoa"
+`, `SyntaxError: Cannot declare an imported binding name twice: 'A'.:2`);
+
+checkModuleSyntaxError(String.raw`
+import A from "Cocoa"
+const { A } = obj;
+`, `SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import A from "Cocoa"
+const [ A ] = array;
+`, `SyntaxError: Unexpected identifier 'A'. Cannot declare a lexical variable twice: 'A'.:3`);
+
+checkModuleSyntaxError(String.raw`
+import A from "Cocoa"
+const { B:A } = obj;
+`, `SyntaxError: Unexpected identifier 'A'. Cannot declare a lexical variable twice: 'A'.:3`);
+
+checkModuleSyntax(String.raw`
+import A from "Cocoa"
+const { A:B } = obj;
+`);
+
+// --------------- export -------------------
+
+checkModuleSyntaxError(String.raw`
+export { A as B } from 'mod'
+export B from 'mod2'
+`, `SyntaxError: Unexpected identifier 'B'. Expected either a declaration or a variable statement.:3`);
+
+checkModuleSyntax(String.raw`
+export { A as B } from 'mod'
+const A = 42;
+const B = 'Cocoa';
+`);
+
+checkModuleSyntaxError(String.raw`
+export { A as B }
+const B = 'Cocoa';
+`, `SyntaxError: Exported binding 'A' needs to refer to a top-level declared variable.:4`);
+
+
+checkModuleSyntaxError(String.raw`
+export default 20;
+export default function hello () { }
+`, `SyntaxError: Cannot export 'default' name twice..:4`);
+
+// FIXME: These tests also should be passed. But now, var and lexical declared names can be co-exist on Script / Module top level scope.
+// This will be fixed when this issue is fixed for Script environment.
+// http://www.ecma-international.org/ecma-262/6.0/#sec-scripts-static-semantics-early-errors
+// http://www.ecma-international.org/ecma-262/6.0/#sec-module-semantics-static-semantics-early-errors
+//
+// checkModuleSyntaxError(String.raw`
+// import A from "Cocoa"
+// var A = 20;
+// `, ``);
+//
+// checkModuleSyntaxError(String.raw`
+// var A = 20;
+// import A from "Cocoa"
+// `, ``);
+//
+// checkModuleSyntaxError(String.raw`
+// import A from "Cocoa"
+// var A = 20;
+// `, ``);
+//
+// checkModuleSyntaxError(String.raw`
+// var A = 20;
+// import A from "Cocoa"
+// `, ``);
+
+
diff --git a/Source/JavaScriptCore/tests/stress/modules-syntax-error.js b/Source/JavaScriptCore/tests/stress/modules-syntax-error.js
new file mode 100644 (file)
index 0000000..eaa7327
--- /dev/null
@@ -0,0 +1,320 @@
+//@ skip
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+function checkModuleSyntaxError(source, errorMessage) {
+    shouldThrow(() => checkModuleSyntax(source), errorMessage);
+}
+
+// --------------- import -------------------
+
+checkModuleSyntaxError(String.raw`
+import {,} from "Cocoa"
+`, `SyntaxError: Unexpected token ','. Expected an imported name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * from "Cocoa"
+`, `SyntaxError: Unexpected identifier 'from'. Expected 'as' before imported binding name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * from "Cocoa"
+`, `SyntaxError: Unexpected identifier 'from'. Expected 'as' before imported binding name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * of name from "Cocoa"
+`, `SyntaxError: Unexpected identifier 'of'. Expected 'as' before imported binding name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as name fro "Cocoa"
+`, `SyntaxError: Unexpected identifier 'fro'. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as name fro "Cocoa"
+`, `SyntaxError: Unexpected identifier 'fro'. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import d, { e, f, g as c }, c from "Cappuccino"
+`, `SyntaxError: Unexpected token ','. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import d, c from "Cappuccino"
+`, `SyntaxError: Unexpected identifier 'c'. Expected namespace import or import list.:2`);
+
+checkModuleSyntaxError(String.raw`
+import i, * as j, * as k from "Cappuccino"
+`, `SyntaxError: Unexpected token ','. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as a, b from "Cappuccino"
+`, `SyntaxError: Unexpected token ','. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { a, b, c as d }, e from "Cappuccino"
+`, `SyntaxError: Unexpected token ','. Expected 'from' before imported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import a
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import a from
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import { a }
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import {} from
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import *
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import * as
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import * from
+`, `SyntaxError: Unexpected identifier 'from'. Expected 'as' before imported binding name.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as from from
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import * as from from d
+`, `SyntaxError: Unexpected identifier 'd'. Imported modules names must be string literals.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as from from 20
+`, `SyntaxError: Unexpected number '20'. Imported modules names must be string literals.:2`);
+
+checkModuleSyntaxError(String.raw`
+function noTopLevel() {
+    import * as from from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'import':3`);
+
+checkModuleSyntaxError(String.raw`
+if (noTopLevel) {
+    import * as from from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'import':3`);
+
+checkModuleSyntaxError(String.raw`
+{
+    import * as from from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'import':3`);
+
+checkModuleSyntaxError(String.raw`
+for (var i = 0; i < 1000; ++i) {
+    import * as from from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'import':3`);
+
+checkModuleSyntaxError(String.raw`
+import for from "Cocoa";
+`, `SyntaxError: Unexpected keyword 'for'. Expected namespace import or import list.:2`);
+
+checkModuleSyntaxError(String.raw`
+import enum from "Cocoa";
+`, `SyntaxError: Unexpected use of reserved word 'enum'. Expected namespace import or import list.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as for from "Cocoa";
+`, `SyntaxError: Unexpected keyword 'for'. Expected a variable name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import * as enum from "Cocoa";
+`, `SyntaxError: Unexpected use of reserved word 'enum'. Expected a variable name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { module as default } from "Cocoa"
+`, `SyntaxError: Unexpected keyword 'default'. Expected a variable name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { module as enum } from "Cocoa"
+`, `SyntaxError: Unexpected use of reserved word 'enum'. Expected a variable name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { for } from "Cocoa"
+`, `SyntaxError: Cannot use keyword as imported binding name.:2`);
+
+
+checkModuleSyntaxError(String.raw`
+import a, { [assign] as c } from "Cocoa"
+`, `SyntaxError: Unexpected token '['. Expected an imported name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import d, { g as {obj} } from "Cappuccino"
+`, `SyntaxError: Unexpected token '{'. Expected a variable name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import d, { {obj} } from "Cappuccino"
+`, `SyntaxError: Unexpected token '{'. Expected an imported name for the import declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+import { binding
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import { hello, binding as
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+import { hello, binding as
+`, `SyntaxError: Unexpected end of script:3`);
+
+// --------------- export -------------------
+
+checkModuleSyntaxError(String.raw`
+export { , } from "Cocoa"
+`, `SyntaxError: Unexpected token ','. Expected a variable name for the export declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+export { a, , } from "Cocoa"
+`, `SyntaxError: Unexpected token ','. Expected a variable name for the export declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+export a from "Cocoa"
+`, `SyntaxError: Unexpected identifier 'a'. Expected either a declaration or a variable statement.:2`);
+
+checkModuleSyntaxError(String.raw`
+export a
+`, `SyntaxError: Unexpected identifier 'a'. Expected either a declaration or a variable statement.:2`);
+
+checkModuleSyntaxError(String.raw`
+export * as b from "Cocoa"
+`, `SyntaxError: Unexpected identifier 'as'. Expected 'from' before exported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+export * "Cocoa"
+`, `SyntaxError: Unexpected string literal "Cocoa". Expected 'from' before exported module name.:2`);
+
+checkModuleSyntaxError(String.raw`
+export const a;
+`, `SyntaxError: Unexpected token ';'. const declared variable 'a' must have an initializer.:2`);
+
+checkModuleSyntaxError(String.raw`
+export const a = 20, b;
+`, `SyntaxError: Unexpected token ';'. const declared variable 'b' must have an initializer.:2`);
+
+checkModuleSyntaxError(String.raw`
+export default 20, 30, 40;
+`, `SyntaxError: Unexpected token ','. Expected a ';' following a targeted export declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+export function () { }
+`, `SyntaxError: Function statements must have a name.:2`);
+
+checkModuleSyntaxError(String.raw`
+export class { }
+`, `SyntaxError: Class statements must have a name.:2`);
+
+checkModuleSyntaxError(String.raw`
+export class extends Drink {
+}
+`, `SyntaxError: Cannot use the keyword 'extends' as a class name.:2`);
+
+checkModuleSyntaxError(String.raw`
+export default 20 30
+`, `SyntaxError: Unexpected number '30'. Expected a ';' following a targeted export declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+export default 20 + 30, 40;
+`, `SyntaxError: Unexpected token ','. Expected a ';' following a targeted export declaration.:2`);
+
+checkModuleSyntaxError(String.raw`
+export { default as default }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { default }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { default as binding }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { hello, default as binding }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { implements }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { static }
+`, `SyntaxError: Cannot use keyword as exported variable name.:3`);
+
+checkModuleSyntaxError(String.raw`
+export { binding
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+export { hello, binding as
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+export { hello, binding as
+`, `SyntaxError: Unexpected end of script:3`);
+
+checkModuleSyntaxError(String.raw`
+function noTopLevel() {
+    export * from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'export':3`);
+
+checkModuleSyntaxError(String.raw`
+if (noTopLevel) {
+    export * from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'export':3`);
+
+checkModuleSyntaxError(String.raw`
+{
+    export * from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'export':3`);
+
+checkModuleSyntaxError(String.raw`
+for (var i = 0; i < 1000; ++i) {
+    export * from "Cocoa"
+}
+`, `SyntaxError: Unexpected keyword 'export':3`);
+
+// --------------- other ---------------------
+
+checkModuleSyntaxError(String.raw`
+new.target;
+`, `SyntaxError: new.target is only valid inside functions.:2`);
+
+checkModuleSyntaxError(String.raw`
+super();
+`, `SyntaxError: super is only valid inside functions.:2`);
+
+checkModuleSyntaxError(String.raw`
+super.test();
+`, `SyntaxError: super is only valid inside functions.:2`);
+
+checkModuleSyntaxError(String.raw`
+super.test = 20;
+`, `SyntaxError: super is only valid inside functions.:2`);
diff --git a/Source/JavaScriptCore/tests/stress/modules-syntax.js b/Source/JavaScriptCore/tests/stress/modules-syntax.js
new file mode 100644 (file)
index 0000000..9a3911f
--- /dev/null
@@ -0,0 +1,314 @@
+//@ skip
+
+checkModuleSyntax(String.raw`
+import "Cocoa";
+`);
+
+// Examples in 15.2.1.16
+// http://www.ecma-international.org/ecma-262/6.0/#sec-source-text-module-records
+checkModuleSyntax(String.raw`
+import v from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import * as ns from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import {x} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import {x,} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import {} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import {x as v} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+import "mod";
+`);
+
+checkModuleSyntax(String.raw`
+export var v;
+`);
+
+checkModuleSyntax(String.raw`
+export default function f(){};
+`);
+
+checkModuleSyntax(String.raw`
+export default function(){};
+`);
+
+checkModuleSyntax(String.raw`
+export default 42;
+`);
+
+checkModuleSyntax(String.raw`
+const x = 20;
+export {x};
+`);
+
+checkModuleSyntax(String.raw`
+const v = 20;
+export {v as x};
+`);
+
+checkModuleSyntax(String.raw`
+export {x} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+export {v as x} from "mod";
+`);
+
+checkModuleSyntax(String.raw`
+export * from "mod";
+`);
+
+// semicolon is not needed.
+checkModuleSyntax(String.raw`
+export default function () { } 40;
+`);
+
+checkModuleSyntax(String.raw`
+export default class { } 40;
+`);
+
+checkModuleSyntax(String.raw`
+export default function Cappuccino() { } 40
+`);
+
+checkModuleSyntax(String.raw`
+export default class Cappuccino { } 40
+`);
+
+checkModuleSyntax(String.raw`
+import a, { b as c } from "Cocoa"
+import d, { e, f, g as h } from "Cappuccino"
+import { } from "Cappuccino"
+import i, * as j from "Cappuccino"
+`);
+
+checkModuleSyntax(String.raw`
+import a, { } from "Cappuccino"
+`);
+
+checkModuleSyntax(String.raw`
+import a, { b, } from "Cappuccino"
+`);
+
+checkModuleSyntax(String.raw`
+import * as from from "Matcha"
+import * as as from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+import { default as module } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export * from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { a } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { a as b } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { a, b } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { a, c as d, b }
+let a, c, b;
+`);
+
+checkModuleSyntax(String.raw`
+export { }
+`);
+
+checkModuleSyntax(String.raw`
+export { a }
+let a;
+`);
+
+checkModuleSyntax(String.raw`
+export { a, }
+let a;
+`);
+
+checkModuleSyntax(String.raw`
+var a = 20;
+export { a as b }
+`);
+
+checkModuleSyntax(String.raw`
+export { a, b }
+var a, b = 40;
+`);
+
+checkModuleSyntax(String.raw`
+export { a, c as d, b }
+let a, c, b;
+`);
+
+checkModuleSyntax(String.raw`
+export var a;
+`);
+
+checkModuleSyntax(String.raw`
+export var a, b, c = 20;
+`);
+
+checkModuleSyntax(String.raw`
+export var a, { b, c } = obj, d = 20;
+`);
+
+checkModuleSyntax(String.raw`
+export const a = 20;
+`);
+
+checkModuleSyntax(String.raw`
+export const [b, ...a] = obj;
+`);
+
+checkModuleSyntax(String.raw`
+export const {b, c: d} = obj;
+`);
+
+checkModuleSyntax(String.raw`
+export let a;
+`);
+
+checkModuleSyntax(String.raw`
+export let a = 20;
+`);
+
+checkModuleSyntax(String.raw`
+export let a = 20, b = 30;
+`);
+
+checkModuleSyntax(String.raw`
+export let [b, ...a] = obj;
+`);
+
+checkModuleSyntax(String.raw`
+export let {b, c: d} = obj;
+`);
+
+checkModuleSyntax(String.raw`
+export function Cocoa() {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export class Cocoa {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export class Cocoa extends Drink {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default function Cocoa() {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default function () {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default class Cocoa {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default class {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default class Cocoa extends Drink {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default class extends Drink {
+}
+`);
+
+checkModuleSyntax(String.raw`
+export default 20;
+`);
+
+checkModuleSyntax(String.raw`
+export default "Cocoa";
+`);
+
+checkModuleSyntax(String.raw`
+export default 20 + 30;
+`);
+
+checkModuleSyntax(String.raw`
+export default call();
+`);
+
+checkModuleSyntax(String.raw`
+export { default } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { enum } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { default as default } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { enum as enum } from "Cocoa"
+`);
+
+checkModuleSyntax(String.raw`
+export { binding as default }
+let binding = 20;
+`);
+
+checkModuleSyntax(String.raw`
+export { binding as enum }
+var binding = 40;
+`);
+
+checkModuleSyntax(String.raw`
+export { binding as for }
+const binding = 40;
+`);
+
+// --------------- other ---------------------
+
+checkModuleSyntax(String.raw`
+let i = 20;
+`);
+
+checkModuleSyntax(String.raw`
+const i = 20;
+`);
index c199bac..ba781ae 100644 (file)
@@ -66,5 +66,5 @@ testSyntax("(class extends Hello { constructor() { super()`${tag}` } })");
 testSyntax("(class extends Hello { constructor() { super()`${tag} ${tag}` } })");
 testSyntax("(class extends Hello { constructor() { super()`${tag}${tag}` } })");
 
-testSyntaxError("super`Hello${tag}`", "SyntaxError: Cannot use super as tag for tagged templates.");
+testSyntaxError("super`Hello${tag}`", "SyntaxError: super is only valid inside functions.");
 testSyntaxError("(class { say() { super`Hello${tag}` } })", "SyntaxError: Cannot use super as tag for tagged templates.");