[JSC] Prototype dynamic-import
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Jan 2017 22:02:47 +0000 (22:02 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Jan 2017 22:02:47 +0000 (22:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165724

Reviewed by Saam Barati.

JSTests:

* stress/import-basic.js: Added.
(async.async.load):
(async):
(catch):
* stress/import-from-eval.js: Added.
(async):
(catch):
* stress/import-syntax.js: Added.
(testSyntaxError):
* stress/import-tests/cocoa.js: Added.
(export.Cocoa):
(export.hello):
* stress/import-tests/multiple.js: Added.
(export.result):
* stress/import-tests/multiple2.js: Added.
(export.ok):
* stress/import-tests/should.js: Added.
(export.shouldBe):
(export.shouldThrow):
* stress/modules-syntax-error.js:

Source/JavaScriptCore:

In this patch, we implement stage3 dynamic-import proposal[1].
This patch adds a new special operator `import`. And by using it, we can import
the module dynamically from modules and scripts. Before this feature, the module
is always imported statically and before executing the modules, importing the modules
needs to be done. And especially, the module can only be imported from the module.
So the classic script cannot import and use the modules. This dynamic-import relaxes
the above restrictions.

The typical dynamic-import form is the following.

    import("...").then(function (namespace) { ... });

You can pass any AssignmentExpression for the import operator. So you can determine
the importing modules dynamically.

    import(value).then(function (namespace) { ... });

And previously the module import declaration is only allowed in the top level statements.
But this import operator is just an expression. So you can use it in the function.
And you can use it conditionally.

    async function go(cond)
    {
        if (cond)
            return import("...");
        return undefined;
    }
    await go(true);

Currently, this patch just implements this feature only for the JSC shell.
JSC module loader requires a new hook, `importModule`. And the JSC shell implements
this hook. So, for now, this dynamic-import is not available in the browser side.
If you write this `import` call, it always returns the rejected promise.

import is implemented like a special operator similar to `super`.
This is because import is context-sensitive. If you call the `import`, the module
key resolution is done based on the caller's running context.

For example, if you are running the script which filename is "./ok/hello.js", the module
key for the call`import("./resource/syntax.js")` becomes `"./ok/resource/syntax.js"`.
But if you write the completely same import form in the script "./error/hello.js", the
key becomes "./error/resource/syntax.js". So exposing this feature as the `import`
function is misleading: this function becomes caller's context-sensitive. That's why
dynamic-import is specified as a special operator.

To resolve the module key, we need the caller's context information like the filename of
the caller. This is provided by the SourceOrigin implemented in r210149.
In the JSC shell implementation, this SourceOrigin holds the filename of the caller. So
based on this implementation, the module loader resolve the module key.
In the near future, we will extend this SourceOrigin to hold more information needed for
the browser-side import implementation.

[1]: https://tc39.github.io/proposal-dynamic-import/

* builtins/ModuleLoaderPrototype.js:
(importModule):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitGetTemplateObject):
(JSC::BytecodeGenerator::emitGetGlobalPrivate):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ImportNode::emitBytecode):
* jsc.cpp:
(absolutePath):
(GlobalObject::moduleLoaderImportModule):
(functionRun):
(functionLoad):
(functionCheckSyntax):
(runWithScripts):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createImportExpr):
* parser/NodeConstructors.h:
(JSC::ImportNode::ImportNode):
* parser/Nodes.h:
(JSC::ExpressionNode::isImportNode):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createImportExpr):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncImportModule):
* runtime/JSGlobalObjectFunctions.h:
* runtime/JSModuleLoader.cpp:
(JSC::JSModuleLoader::importModule):
(JSC::JSModuleLoader::getModuleNamespaceObject):
* runtime/JSModuleLoader.h:
* runtime/ModuleLoaderPrototype.cpp:
(JSC::moduleLoaderPrototypeGetModuleNamespaceObject):

Source/WebCore:

We do not set a handler for import for now.
So dynamic import feature is only enabled in the JSC shell right now.

* bindings/js/JSDOMWindowBase.cpp:
* bindings/js/JSWorkerGlobalScopeBase.cpp:

LayoutTests:

* sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt:

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

32 files changed:
JSTests/ChangeLog
JSTests/stress/import-basic.js [new file with mode: 0644]
JSTests/stress/import-from-eval.js [new file with mode: 0644]
JSTests/stress/import-syntax.js [new file with mode: 0644]
JSTests/stress/import-tests/cocoa.js [new file with mode: 0644]
JSTests/stress/import-tests/multiple.js [new file with mode: 0644]
JSTests/stress/import-tests/multiple2.js [new file with mode: 0644]
JSTests/stress/import-tests/should.js [new file with mode: 0644]
JSTests/stress/modules-syntax-error.js
LayoutTests/ChangeLog
LayoutTests/sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/JSModuleLoader.cpp
Source/JavaScriptCore/runtime/JSModuleLoader.h
Source/JavaScriptCore/runtime/ModuleLoaderPrototype.cpp
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMWindowBase.cpp
Source/WebCore/bindings/js/JSWorkerGlobalScopeBase.cpp

index 621806c..b41efdd 100644 (file)
@@ -1,3 +1,31 @@
+2017-01-09  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Prototype dynamic-import
+        https://bugs.webkit.org/show_bug.cgi?id=165724
+
+        Reviewed by Saam Barati.
+
+        * stress/import-basic.js: Added.
+        (async.async.load):
+        (async):
+        (catch):
+        * stress/import-from-eval.js: Added.
+        (async):
+        (catch):
+        * stress/import-syntax.js: Added.
+        (testSyntaxError):
+        * stress/import-tests/cocoa.js: Added.
+        (export.Cocoa):
+        (export.hello):
+        * stress/import-tests/multiple.js: Added.
+        (export.result):
+        * stress/import-tests/multiple2.js: Added.
+        (export.ok):
+        * stress/import-tests/should.js: Added.
+        (export.shouldBe):
+        (export.shouldThrow):
+        * stress/modules-syntax-error.js:
+
 2017-01-09  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r210476.
diff --git a/JSTests/stress/import-basic.js b/JSTests/stress/import-basic.js
new file mode 100644 (file)
index 0000000..c5d967c
--- /dev/null
@@ -0,0 +1,53 @@
+(async function () {
+    const { shouldBe } = await import('./import-tests/should.js');
+    {
+        let a = await import('./import-tests/cocoa.js');
+        let b = await import('./import-tests/cocoa.js');
+        shouldBe(a, b);
+        shouldBe(a.hello(), 42);
+    }
+
+    {
+        let a = await import('./import-tests/multiple.js');
+        let a2 = await a.result();
+        shouldBe(a !== a2, true);
+        shouldBe(a2.ok(), 42);
+        let a3 = await a.result();
+        shouldBe(a2, a3);
+    }
+
+    {
+        let error = null;
+        try {
+            let a = await import({ toString() { throw new Error('out'); } });
+        } catch (e) {
+            error = e;
+        }
+        shouldBe(error !== null, true);
+        shouldBe(String(error), `Error: out`);
+    }
+
+    {
+        async function load(cond) {
+            if (cond)
+                return import('./import-tests/cocoa.js');
+            return undefined;
+        }
+
+        let v = await load(false);
+        shouldBe(v, undefined);
+        let v2 = await load(true);
+        let v3 = await import('./import-tests/cocoa.js');
+        shouldBe(v2, v2);
+    }
+
+    {
+        let value = './import-tests/cocoa.js';
+        let v = await import(value);
+        let v2 = await import('./import-tests/cocoa.js');
+        shouldBe(v, v2);
+    }
+}()).catch((error) => {
+    print(String(error));
+    abort();
+});
diff --git a/JSTests/stress/import-from-eval.js b/JSTests/stress/import-from-eval.js
new file mode 100644 (file)
index 0000000..b8e0792
--- /dev/null
@@ -0,0 +1,36 @@
+(async function () {
+    const { shouldBe, shouldThrow } = await import("./import-tests/should.js");
+
+    {
+        let cocoa = await eval(`import("./import-tests/cocoa.js")`);
+        shouldBe(cocoa.hello(), 42);
+    }
+
+    {
+        let cocoa = await (0, eval)(`import("./import-tests/cocoa.js")`);
+        shouldBe(cocoa.hello(), 42);
+    }
+
+    {
+        let cocoa = await eval(`eval('import("./import-tests/cocoa.js")')`);
+        shouldBe(cocoa.hello(), 42);
+    }
+
+    {
+        let cocoa = await ((new Function(`return eval('import("./import-tests/cocoa.js")')`))());
+        shouldBe(cocoa.hello(), 42);
+    }
+
+    {
+        let cocoa = await eval(`(new Function('return import("./import-tests/cocoa.js")'))()`);
+        shouldBe(cocoa.hello(), 42);
+    }
+
+    {
+        let cocoa = await [`import("./import-tests/cocoa.js")`].map(eval)[0];
+        shouldBe(cocoa.hello(), 42);
+    }
+}()).catch((error) => {
+    print(String(error));
+    abort();
+});
diff --git a/JSTests/stress/import-syntax.js b/JSTests/stress/import-syntax.js
new file mode 100644 (file)
index 0000000..8bf78da
--- /dev/null
@@ -0,0 +1,59 @@
+function testSyntaxError(script, message) {
+    var error = null;
+    try {
+        eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("Expected syntax error not thrown");
+
+    if (String(error) !== message)
+        throw new Error(`Bad error: ${String(error)}`);
+}
+
+async function testSyntax(script, message) {
+    var error = null;
+    try {
+        await eval(script);
+    } catch (e) {
+        error = e;
+    }
+    if (error) {
+        if (error instanceof SyntaxError)
+            throw new Error("Syntax error thrown");
+    }
+}
+
+testSyntaxError(`import)`, `SyntaxError: Unexpected token ')'. import call expects exactly one argument.`);
+testSyntaxError(`new import(`, `SyntaxError: Cannot use new with import.`);
+testSyntaxError(`import.hello()`, `SyntaxError: Unexpected token '.'. import call expects exactly one argument.`);
+testSyntaxError(`import[`, `SyntaxError: Unexpected token '['. import call expects exactly one argument.`);
+testSyntaxError(`import<`, `SyntaxError: Unexpected token '<'. import call expects exactly one argument.`);
+
+testSyntaxError(`import()`, `SyntaxError: Unexpected token ')'`);
+testSyntaxError(`import(a, b)`, `SyntaxError: Unexpected token ','. import call expects exactly one argument.`);
+testSyntaxError(`import(a, b, c)`, `SyntaxError: Unexpected token ','. import call expects exactly one argument.`);
+testSyntaxError(`import(...a)`, `SyntaxError: Unexpected token '...'`);
+testSyntaxError(`import(,a)`, `SyntaxError: Unexpected token ','`);
+testSyntaxError(`import(,)`, `SyntaxError: Unexpected token ','`);
+testSyntaxError(`import("Hello";`, `SyntaxError: Unexpected token ';'. import call expects exactly one argument.`);
+testSyntaxError(`import("Hello"];`, `SyntaxError: Unexpected token ']'. import call expects exactly one argument.`);
+testSyntaxError(`import("Hello",;`, `SyntaxError: Unexpected token ','. import call expects exactly one argument.`);
+testSyntaxError(`import("Hello", "Hello2";`, `SyntaxError: Unexpected token ','. import call expects exactly one argument.`);
+
+
+testSyntaxError(`import = 42`, `SyntaxError: Unexpected token '='. import call expects exactly one argument.`);
+testSyntaxError(`[import] = 42`, `SyntaxError: Unexpected token ']'. import call expects exactly one argument.`);
+testSyntaxError(`{import} = 42`, `SyntaxError: Unexpected token '}'. import call expects exactly one argument.`);
+testSyntaxError(`let import = 42`, `SyntaxError: Unexpected keyword 'import'`);
+testSyntaxError(`var import = 42`, `SyntaxError: Cannot use the keyword 'import' as a variable name.`);
+testSyntaxError(`const import = 42`, `SyntaxError: Cannot use the keyword 'import' as a lexical variable name.`);
+
+(async function () {
+    await testSyntax(`import("./import-tests/cocoa.js")`);
+    await testSyntax(`import("./import-tests/../import-tests/cocoa.js")`);
+}()).catch((error) => {
+    print(String(error));
+    abort();
+});
diff --git a/JSTests/stress/import-tests/cocoa.js b/JSTests/stress/import-tests/cocoa.js
new file mode 100644 (file)
index 0000000..910b02c
--- /dev/null
@@ -0,0 +1,7 @@
+export class Cocoa {
+}
+
+export function hello()
+{
+    return 42;
+}
diff --git a/JSTests/stress/import-tests/multiple.js b/JSTests/stress/import-tests/multiple.js
new file mode 100644 (file)
index 0000000..41805c4
--- /dev/null
@@ -0,0 +1,4 @@
+export function result()
+{
+    return import('./multiple2.js');
+}
diff --git a/JSTests/stress/import-tests/multiple2.js b/JSTests/stress/import-tests/multiple2.js
new file mode 100644 (file)
index 0000000..ba6f85a
--- /dev/null
@@ -0,0 +1,4 @@
+export function ok()
+{
+    return 42;
+}
diff --git a/JSTests/stress/import-tests/should.js b/JSTests/stress/import-tests/should.js
new file mode 100644 (file)
index 0000000..a006f84
--- /dev/null
@@ -0,0 +1,19 @@
+export function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+export 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)}`);
+}
index eacf2fb..51828de 100644 (file)
@@ -107,25 +107,25 @@ checkModuleSyntaxError(String.raw`
 function noTopLevel() {
     import * as from from "Cocoa"
 }
-`, `SyntaxError: Unexpected keyword 'import':3`);
+`, `SyntaxError: Unexpected token '*'. import call expects exactly one argument.:3`);
 
 checkModuleSyntaxError(String.raw`
 if (noTopLevel) {
     import * as from from "Cocoa"
 }
-`, `SyntaxError: Unexpected keyword 'import':3`);
+`, `SyntaxError: Unexpected token '*'. import call expects exactly one argument.:3`);
 
 checkModuleSyntaxError(String.raw`
 {
     import * as from from "Cocoa"
 }
-`, `SyntaxError: Unexpected keyword 'import':3`);
+`, `SyntaxError: Unexpected token '*'. import call expects exactly one argument.:3`);
 
 checkModuleSyntaxError(String.raw`
 for (var i = 0; i < 1000; ++i) {
     import * as from from "Cocoa"
 }
-`, `SyntaxError: Unexpected keyword 'import':3`);
+`, `SyntaxError: Unexpected token '*'. import call expects exactly one argument.:3`);
 
 checkModuleSyntaxError(String.raw`
 import for from "Cocoa";
index 8c4133f..0c5235b 100644 (file)
@@ -1,3 +1,12 @@
+2017-01-09  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Prototype dynamic-import
+        https://bugs.webkit.org/show_bug.cgi?id=165724
+
+        Reviewed by Saam Barati.
+
+        * sputnik/Conformance/07_Lexical_Conventions/7.5_Tokens/7.5.3_Future_Reserved_Words/S7.5.3_A1.16-expected.txt:
+
 2017-01-09  Andy Estes  <aestes@apple.com>
 
         [QuickLook] Add a layout test for webkit.org/b/135651
index 0d24774..bcce016 100644 (file)
@@ -1,3 +1,102 @@
+2017-01-09  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Prototype dynamic-import
+        https://bugs.webkit.org/show_bug.cgi?id=165724
+
+        Reviewed by Saam Barati.
+
+        In this patch, we implement stage3 dynamic-import proposal[1].
+        This patch adds a new special operator `import`. And by using it, we can import
+        the module dynamically from modules and scripts. Before this feature, the module
+        is always imported statically and before executing the modules, importing the modules
+        needs to be done. And especially, the module can only be imported from the module.
+        So the classic script cannot import and use the modules. This dynamic-import relaxes
+        the above restrictions.
+
+        The typical dynamic-import form is the following.
+
+            import("...").then(function (namespace) { ... });
+
+        You can pass any AssignmentExpression for the import operator. So you can determine
+        the importing modules dynamically.
+
+            import(value).then(function (namespace) { ... });
+
+        And previously the module import declaration is only allowed in the top level statements.
+        But this import operator is just an expression. So you can use it in the function.
+        And you can use it conditionally.
+
+            async function go(cond)
+            {
+                if (cond)
+                    return import("...");
+                return undefined;
+            }
+            await go(true);
+
+        Currently, this patch just implements this feature only for the JSC shell.
+        JSC module loader requires a new hook, `importModule`. And the JSC shell implements
+        this hook. So, for now, this dynamic-import is not available in the browser side.
+        If you write this `import` call, it always returns the rejected promise.
+
+        import is implemented like a special operator similar to `super`.
+        This is because import is context-sensitive. If you call the `import`, the module
+        key resolution is done based on the caller's running context.
+
+        For example, if you are running the script which filename is "./ok/hello.js", the module
+        key for the call`import("./resource/syntax.js")` becomes `"./ok/resource/syntax.js"`.
+        But if you write the completely same import form in the script "./error/hello.js", the
+        key becomes "./error/resource/syntax.js". So exposing this feature as the `import`
+        function is misleading: this function becomes caller's context-sensitive. That's why
+        dynamic-import is specified as a special operator.
+
+        To resolve the module key, we need the caller's context information like the filename of
+        the caller. This is provided by the SourceOrigin implemented in r210149.
+        In the JSC shell implementation, this SourceOrigin holds the filename of the caller. So
+        based on this implementation, the module loader resolve the module key.
+        In the near future, we will extend this SourceOrigin to hold more information needed for
+        the browser-side import implementation.
+
+        [1]: https://tc39.github.io/proposal-dynamic-import/
+
+        * builtins/ModuleLoaderPrototype.js:
+        (importModule):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitGetTemplateObject):
+        (JSC::BytecodeGenerator::emitGetGlobalPrivate):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ImportNode::emitBytecode):
+        * jsc.cpp:
+        (absolutePath):
+        (GlobalObject::moduleLoaderImportModule):
+        (functionRun):
+        (functionLoad):
+        (functionCheckSyntax):
+        (runWithScripts):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createImportExpr):
+        * parser/NodeConstructors.h:
+        (JSC::ImportNode::ImportNode):
+        * parser/Nodes.h:
+        (JSC::ExpressionNode::isImportNode):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createImportExpr):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObject.h:
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncImportModule):
+        * runtime/JSGlobalObjectFunctions.h:
+        * runtime/JSModuleLoader.cpp:
+        (JSC::JSModuleLoader::importModule):
+        (JSC::JSModuleLoader::getModuleNamespaceObject):
+        * runtime/JSModuleLoader.h:
+        * runtime/ModuleLoaderPrototype.cpp:
+        (JSC::moduleLoaderPrototypeGetModuleNamespaceObject):
+
 2017-01-08  Filip Pizlo  <fpizlo@apple.com>
 
         Make the collector's fixpoint smart about scheduling work
index 01c2b21..600285b 100644 (file)
@@ -470,3 +470,19 @@ function linkAndEvaluateModule(key, initiator)
     this.link(entry, initiator);
     return this.moduleEvaluation(entry.module, initiator);
 }
+
+function importModule(moduleName, referrer, initiator)
+{
+    "use strict";
+
+    // Loader.resolve hook point.
+    // resolve: moduleName => Promise(moduleKey)
+    // Take the name and resolve it to the unique identifier for the resource location.
+    // For example, take the "jquery" and return the URL for the resource.
+    return this.resolve(moduleName, referrer, initiator).then((key) => {
+        return this.requestInstantiateAll(key, initiator);
+    }).then((entry) => {
+        this.linkAndEvaluateModule(entry.key, initiator);
+        return this.getModuleNamespaceObject(entry.module);
+    });
+}
index 423e574..30a8b6d 100644 (file)
@@ -4261,22 +4261,24 @@ RegisterID* BytecodeGenerator::emitGetTemplateObject(RegisterID* dst, TaggedTemp
         cookedStrings.append(templateString->value()->cooked().impl());
     }
 
-    RefPtr<RegisterID> getTemplateObject = nullptr;
-    Variable var = variable(propertyNames().builtinNames().getTemplateObjectPrivateName());
-    if (RegisterID* local = var.local())
-        getTemplateObject = emitMove(newTemporary(), local);
-    else {
-        getTemplateObject = newTemporary();
-        RefPtr<RegisterID> scope = newTemporary();
-        moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), var));
-        emitGetFromScope(getTemplateObject.get(), scope.get(), var, ThrowIfNotFound);
-    }
-
+    RefPtr<RegisterID> getTemplateObject = emitGetGlobalPrivate(newTemporary(), propertyNames().builtinNames().getTemplateObjectPrivateName());
     CallArguments arguments(*this, nullptr);
     emitLoad(arguments.thisRegister(), JSValue(addTemplateRegistryKeyConstant(m_vm->templateRegistryKeyTable().createKey(rawStrings, cookedStrings))));
     return emitCall(dst, getTemplateObject.get(), NoExpectedFunction, arguments, taggedTemplate->divot(), taggedTemplate->divotStart(), taggedTemplate->divotEnd(), DebuggableCall::No);
 }
 
+RegisterID* BytecodeGenerator::emitGetGlobalPrivate(RegisterID* dst, const Identifier& property)
+{
+    dst = tempDestination(dst);
+    Variable var = variable(property);
+    if (RegisterID* local = var.local())
+        return emitMove(dst, local);
+
+    RefPtr<RegisterID> scope = newTemporary();
+    moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), var));
+    return emitGetFromScope(dst, scope.get(), var, ThrowIfNotFound);
+}
+
 RegisterID* BytecodeGenerator::emitGetEnumerableLength(RegisterID* dst, RegisterID* base)
 {
     emitOpcode(op_get_enumerable_length);
index 0aaec9d..5f547ae 100644 (file)
@@ -678,6 +678,7 @@ namespace JSC {
         void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* = nullptr, RegisterID* forLoopSymbolTable = nullptr);
 
         RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
+        RegisterID* emitGetGlobalPrivate(RegisterID* dst, const Identifier& property);
 
         enum class ReturnFrom { Normal, Finally };
         RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal);
index 7becf75..5744d58 100644 (file)
@@ -190,6 +190,17 @@ RegisterID* SuperNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds
     return generator.moveToDestinationIfNeeded(generator.finalDestination(dst), result);
 }
 
+// ------------------------------ ImportNode -------------------------------------
+
+RegisterID* ImportNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    RefPtr<RegisterID> importModule = generator.emitGetGlobalPrivate(generator.newTemporary(), generator.propertyNames().builtinNames().importModulePrivateName());
+    CallArguments arguments(generator, nullptr, 1);
+    generator.emitLoad(arguments.thisRegister(), jsUndefined());
+    generator.emitNode(arguments.argumentRegister(0), m_expr);
+    return generator.emitCall(generator.finalDestination(dst, importModule.get()), importModule.get(), NoExpectedFunction, arguments, divot(), divotStart(), divotEnd(), DebuggableCall::No);
+}
+
 // ------------------------------ NewTargetNode ----------------------------------
 
 RegisterID* NewTargetNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
index 3a7a3f4..fdbbde9 100644 (file)
@@ -25,6 +25,7 @@
 #include "ArrayBuffer.h"
 #include "ArrayPrototype.h"
 #include "BuiltinExecutableCreator.h"
+#include "BuiltinNames.h"
 #include "ButterflyInlines.h"
 #include "CodeBlock.h"
 #include "Completion.h"
@@ -48,6 +49,7 @@
 #include "JSInternalPromise.h"
 #include "JSInternalPromiseDeferred.h"
 #include "JSLock.h"
+#include "JSModuleLoader.h"
 #include "JSNativeStdFunction.h"
 #include "JSONObject.h"
 #include "JSProxy.h"
@@ -1101,10 +1103,10 @@ static inline String stringFromUTF(const Vector& utf8)
 }
 
 template<typename Vector>
-static inline SourceCode jscSource(const Vector& utf8, const String& filename)
+static inline SourceCode jscSource(const Vector& utf8, const SourceOrigin& sourceOrigin, const String& filename)
 {
     String str = stringFromUTF(utf8);
-    return makeSource(str, SourceOrigin { filename }, filename);
+    return makeSource(str, sourceOrigin, filename);
 }
 
 class GlobalObject : public JSGlobalObject {
@@ -1277,6 +1279,7 @@ protected:
         putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function, NoIntrinsic, function));
     }
 
+    static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString*, const SourceOrigin&);
     static JSInternalPromise* moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
     static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue);
 };
@@ -1288,6 +1291,7 @@ const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
     &javaScriptRuntimeFlags,
     nullptr,
     &shouldInterruptScriptBeforeTimeout,
+    &moduleLoaderImportModule,
     &moduleLoaderResolve,
     &moduleLoaderFetch,
     nullptr,
@@ -1420,6 +1424,29 @@ static String resolvePath(const DirectoryName& directoryName, const ModuleName&
     return builder.toString();
 }
 
+static String absolutePath(const String& fileName)
+{
+    auto directoryName = currentWorkingDirectory();
+    if (!directoryName)
+        return fileName;
+    return resolvePath(directoryName.value(), ModuleName(fileName.impl()));
+}
+
+JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject*, ExecState* exec, JSModuleLoader* moduleLoader, JSString* moduleName, const SourceOrigin& sourceOrigin)
+{
+    auto* function = jsCast<JSObject*>(moduleLoader->get(exec, exec->propertyNames().builtinNames().importModulePublicName()));
+    CallData callData;
+    auto callType = JSC::getCallData(function, callData);
+    ASSERT(callType != CallType::None);
+
+    MarkedArgumentBuffer arguments;
+    arguments.append(moduleName);
+    arguments.append(jsString(exec, sourceOrigin.string()));
+    arguments.append(jsUndefined());
+
+    return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, moduleLoader, arguments));
+}
+
 JSInternalPromise* GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)
 {
     VM& vm = globalObject->vm();
@@ -1924,7 +1951,7 @@ EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
     NakedPtr<Exception> exception;
     StopWatch stopWatch;
     stopWatch.start();
-    evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), exception);
+    evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), exception);
     stopWatch.stop();
 
     if (exception) {
@@ -1976,7 +2003,7 @@ EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
     
     NakedPtr<Exception> evaluationException;
-    JSValue result = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), evaluationException);
+    JSValue result = evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
     if (evaluationException)
         throwException(exec, scope, evaluationException);
     return JSValue::encode(result);
@@ -2047,7 +2074,7 @@ EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
     stopWatch.start();
 
     JSValue syntaxException;
-    bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script, fileName), &syntaxException);
+    bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), &syntaxException);
     stopWatch.stop();
 
     if (!validSyntax)
@@ -2909,7 +2936,7 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr
         bool isLastFile = i == scripts.size() - 1;
         if (isModule) {
             if (!promise)
-                promise = loadAndEvaluateModule(globalObject->globalExec(), jscSource(scriptBuffer, fileName));
+                promise = loadAndEvaluateModule(globalObject->globalExec(), jscSource(scriptBuffer, SourceOrigin { absolutePath(fileName) }, fileName));
             scope.clearException();
 
             JSFunction* fulfillHandler = JSNativeStdFunction::create(vm, globalObject, 1, String(), [&, isLastFile](ExecState* exec) {
@@ -2926,7 +2953,7 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr
             vm.drainMicrotasks();
         } else {
             NakedPtr<Exception> evaluationException;
-            JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(scriptBuffer, fileName), JSValue(), evaluationException);
+            JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(scriptBuffer, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
             ASSERT(!scope.exception());
             if (evaluationException)
                 returnValue = evaluationException->value();
@@ -2999,7 +3026,7 @@ static void runInteractive(GlobalObject* globalObject)
             break;
 
         NakedPtr<Exception> evaluationException;
-        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, sourceOrigin.string()), JSValue(), evaluationException);
+        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, sourceOrigin, sourceOrigin.string()), JSValue(), evaluationException);
 #endif
         if (evaluationException)
             printf("Exception: %s\n", evaluationException->value().toWTFString(globalObject->globalExec()).utf8().data());
index 57ec412..4e74b5b 100644 (file)
@@ -178,6 +178,12 @@ public:
     {
         return new (m_parserArena) SuperNode(location);
     }
+    ExpressionNode* createImportExpr(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
+    {
+        auto* node = new (m_parserArena) ImportNode(location, expr);
+        setExceptionLocation(node, start, divot, end);
+        return node;
+    }
     ExpressionNode* createNewTargetExpr(const JSTokenLocation location)
     {
         usesNewTarget();
index e27e209..7d2e2e9 100644 (file)
@@ -167,6 +167,12 @@ namespace JSC {
     {
     }
 
+    inline ImportNode::ImportNode(const JSTokenLocation& location, ExpressionNode* expr)
+        : ExpressionNode(location)
+        , m_expr(expr)
+    {
+    }
+
     inline NewTargetNode::NewTargetNode(const JSTokenLocation& location)
         : ExpressionNode(location)
     {
index 24110aa..7324cd4 100644 (file)
@@ -186,6 +186,7 @@ namespace JSC {
         virtual bool isBoolean() const { return false; }
         virtual bool isSpreadExpression() const { return false; }
         virtual bool isSuperNode() const { return false; }
+        virtual bool isImportNode() const { return false; }
         virtual bool isNewTarget() const { return false; }
         virtual bool isBytecodeIntrinsicNode() const { return false; }
 
@@ -570,6 +571,17 @@ namespace JSC {
         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     };
 
+    class ImportNode : public ExpressionNode, public ThrowableExpressionData {
+    public:
+        ImportNode(const JSTokenLocation&, ExpressionNode*);
+
+    private:
+        bool isImportNode() const override { return true; }
+        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+
+        ExpressionNode* m_expr;
+    };
+
     class NewTargetNode final : public ExpressionNode {
     public:
         NewTargetNode(const JSTokenLocation&);
index 65ec38a..d3c5d21 100644 (file)
@@ -4343,7 +4343,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
     }
 
     bool baseIsSuper = match(SUPER);
-    semanticFailIfTrue(baseIsSuper && newCount, "Cannot use new with super");
+    bool baseIsImport = match(IMPORT);
+    semanticFailIfTrue((baseIsSuper || baseIsImport) && newCount, "Cannot use new with ", getToken());
 
     bool baseIsNewTarget = false;
     if (newCount && match(DOT)) {
@@ -4383,6 +4384,14 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
                 semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded, "super is not valid in this context");
             }
         }
+    } else if (baseIsImport) {
+        JSTextPosition expressionEnd = lastTokenEndPosition();
+        next();
+        consumeOrFail(OPENPAREN, "import call expects exactly one argument");
+        TreeExpression expr = parseAssignmentExpression(context);
+        failIfFalse(expr, "Cannot parse expression");
+        consumeOrFail(CLOSEPAREN, "import call expects exactly one argument");
+        return context.createImportExpr(location, expr, expressionStart, expressionEnd, lastTokenEndPosition());
     } else if (!baseIsNewTarget) {
         const bool isAsync = match(ASYNC);
 
index f32854a..52d947a 100644 (file)
@@ -71,7 +71,7 @@ public:
     enum { NoneExpr = 0,
         ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr,
         ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr,
-        FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr,
+        FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, CallExpr,
         NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
         ConditionalExpr, AssignmentExpr, TypeofExpr, NewTargetExpr,
         DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter,
@@ -156,6 +156,7 @@ public:
     ExpressionType createLogicalNot(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
     ExpressionType createUnaryPlus(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
     ExpressionType createVoid(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
+    ExpressionType createImportExpr(const JSTokenLocation&, ExpressionType, int, int, int) { return ImportExpr; }
     ExpressionType createThisExpr(const JSTokenLocation&) { return ThisExpr; }
     ExpressionType createSuperExpr(const JSTokenLocation&) { return SuperExpr; }
     ExpressionType createNewTargetExpr(const JSTokenLocation&) { return NewTargetExpr; }
index a70ecf4..242e055 100644 (file)
@@ -256,6 +256,7 @@ const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = {
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     nullptr
 };
 
@@ -732,6 +733,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     JSFunction* privateFuncTrunc = JSFunction::create(vm, this, 0, String(), mathProtoFuncTrunc, TruncIntrinsic);
 
     JSFunction* privateFuncGetTemplateObject = JSFunction::create(vm, this, 0, String(), getTemplateObject);
+    JSFunction* privateFuncImportModule = JSFunction::create(vm, this, 0, String(), globalFuncImportModule);
     JSFunction* privateFuncTypedArrayLength = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncLength);
     JSFunction* privateFuncTypedArrayGetOriginalConstructor = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncGetOriginalConstructor);
     JSFunction* privateFuncTypedArraySort = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncSort);
@@ -785,6 +787,7 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->undefinedKeyword, jsUndefined(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().ownEnumerablePropertyKeysPrivateName(), JSFunction::create(vm, this, 0, String(), ownEnumerablePropertyKeys), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().getTemplateObjectPrivateName(), privateFuncGetTemplateObject, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().importModulePrivateName(), privateFuncImportModule, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().enqueueJobPrivateName(), JSFunction::create(vm, this, 0, String(), enqueueJob), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().ErrorPrivateName(), m_errorConstructor.get(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().RangeErrorPrivateName(), m_rangeErrorConstructor.get(), DontEnum | DontDelete | ReadOnly),
index b423c49..76aeb30 100644 (file)
@@ -184,6 +184,9 @@ struct GlobalObjectMethodTable {
     typedef bool (*ShouldInterruptScriptBeforeTimeoutPtr)(const JSGlobalObject*);
     ShouldInterruptScriptBeforeTimeoutPtr shouldInterruptScriptBeforeTimeout;
 
+    typedef JSInternalPromise* (*ModuleLoaderImportModulePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString*, const SourceOrigin&);
+    ModuleLoaderImportModulePtr moduleLoaderImportModule;
+
     typedef JSInternalPromise* (*ModuleLoaderResolvePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
     ModuleLoaderResolvePtr moduleLoaderResolve;
 
index 110f524..47e6ccd 100644 (file)
 
 #include "CallFrame.h"
 #include "EvalExecutable.h"
+#include "Exception.h"
 #include "IndirectEvalExecutable.h"
 #include "Interpreter.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
+#include "JSInternalPromise.h"
+#include "JSModuleLoader.h"
+#include "JSPromiseDeferred.h"
 #include "JSString.h"
 #include "JSStringBuilder.h"
 #include "Lexer.h"
@@ -925,4 +929,39 @@ EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL globalFuncImportModule(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto catchScope = DECLARE_CATCH_SCOPE(vm);
+
+    auto* globalObject = exec->lexicalGlobalObject();
+
+    auto* promise = JSPromiseDeferred::create(exec, globalObject);
+    RETURN_IF_EXCEPTION(catchScope, { });
+
+    auto sourceOrigin = exec->callerSourceOrigin();
+    if (sourceOrigin.isNull()) {
+        promise->reject(exec, createError(exec, ASCIILiteral("Could not resolve the module specifier.")));
+        return JSValue::encode(promise->promise());
+    }
+
+    RELEASE_ASSERT(exec->argumentCount() == 1);
+    auto* specifier = exec->uncheckedArgument(0).toString(exec);
+    if (Exception* exception = catchScope.exception()) {
+        catchScope.clearException();
+        promise->reject(exec, exception);
+        return JSValue::encode(promise->promise());
+    }
+
+    auto* internalPromise = globalObject->moduleLoader()->importModule(exec, specifier, sourceOrigin);
+    if (Exception* exception = catchScope.exception()) {
+        catchScope.clearException();
+        promise->reject(exec, exception);
+        return JSValue::encode(promise->promise());
+    }
+    promise->resolve(exec, internalPromise);
+
+    return JSValue::encode(promise->promise());
+}
+
 } // namespace JSC
index dcdba38..cd42704 100644 (file)
@@ -50,6 +50,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncThrowTypeErrorArgumentsCalleeAndCaller(Ex
 EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncProtoSetter(ExecState*);
 EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);
+EncodedJSValue JSC_HOST_CALL globalFuncImportModule(ExecState*);
 
 static const double mantissaOverflowLowerBound = 9007199254740992.0;
 double parseIntOverflow(const LChar*, unsigned length, int radix);
index bfd8f49..a86af96 100644 (file)
@@ -134,6 +134,30 @@ JSValue JSModuleLoader::linkAndEvaluateModule(ExecState* exec, JSValue moduleKey
     return call(exec, function, callType, callData, this, arguments);
 }
 
+JSInternalPromise* JSModuleLoader::importModule(ExecState* exec, JSString* moduleName, const SourceOrigin& referrer)
+{
+    if (Options::dumpModuleLoadingState())
+        dataLog("Loader [import] ", printableModuleKey(exec, moduleName), "\n");
+
+    auto* globalObject = exec->lexicalGlobalObject();
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
+    if (globalObject->globalObjectMethodTable()->moduleLoaderImportModule)
+        return globalObject->globalObjectMethodTable()->moduleLoaderImportModule(globalObject, exec, this, moduleName, referrer);
+
+    auto* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
+    auto moduleNameString = moduleName->value(exec);
+    if (UNLIKELY(scope.exception())) {
+        JSValue exception = scope.exception()->value();
+        scope.clearException();
+        deferred->reject(exec, exception);
+        return deferred->promise();
+    }
+    deferred->reject(exec, createError(exec, makeString("Could not import the module '", moduleNameString, "'.")));
+    return deferred->promise();
+}
+
 JSInternalPromise* JSModuleLoader::resolve(ExecState* exec, JSValue name, JSValue referrer, JSValue initiator)
 {
     if (Options::dumpModuleLoadingState())
@@ -197,4 +221,19 @@ JSValue JSModuleLoader::evaluate(ExecState* exec, JSValue key, JSValue moduleRec
     return moduleRecord->evaluate(exec);
 }
 
+JSModuleNamespaceObject* JSModuleLoader::getModuleNamespaceObject(ExecState* exec, JSValue moduleRecordValue)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* moduleRecord = jsDynamicCast<AbstractModuleRecord*>(moduleRecordValue);
+    if (!moduleRecord) {
+        throwTypeError(exec, scope);
+        return nullptr;
+    }
+
+    scope.release();
+    return moduleRecord->getModuleNamespace(exec);
+}
+
 } // namespace JSC
index d3bb3bc..73ecce6 100644 (file)
@@ -31,6 +31,7 @@
 namespace JSC {
 
 class JSInternalPromise;
+class JSModuleNamespaceObject;
 
 class JSModuleLoader : public JSNonFinalObject {
 private:
@@ -67,6 +68,7 @@ public:
     JSValue linkAndEvaluateModule(ExecState*, JSValue moduleKey, JSValue initiator);
 
     // Platform dependent hooked APIs.
+    JSInternalPromise* importModule(ExecState*, JSString* moduleName, const SourceOrigin& referrer);
     JSInternalPromise* resolve(ExecState*, JSValue name, JSValue referrer, JSValue initiator);
     JSInternalPromise* fetch(ExecState*, JSValue key, JSValue initiator);
     JSInternalPromise* instantiate(ExecState*, JSValue key, JSValue source, JSValue initiator);
@@ -74,6 +76,9 @@ public:
     // Additional platform dependent hooked APIs.
     JSValue evaluate(ExecState*, JSValue key, JSValue moduleRecord, JSValue initiator);
 
+    // Utility functions.
+    JSModuleNamespaceObject* getModuleNamespaceObject(ExecState*, JSValue moduleRecord);
+
 protected:
     void finishCreation(ExecState*, VM&, JSGlobalObject*);
 };
index ee07a9b..d95045c 100644 (file)
@@ -37,6 +37,7 @@
 #include "JSMap.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleLoader.h"
+#include "JSModuleNamespaceObject.h"
 #include "JSModuleRecord.h"
 #include "ModuleAnalyzer.h"
 #include "Nodes.h"
@@ -52,6 +53,7 @@ static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeModuleDeclarationInstan
 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolve(ExecState*);
 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeFetch(ExecState*);
 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeInstantiate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeGetModuleNamespaceObject(ExecState*);
 
 }
 
@@ -85,6 +87,8 @@ const ClassInfo ModuleLoaderPrototype::s_info = { "ModuleLoader", &Base::s_info,
     loadAndEvaluateModule          JSBuiltin                                           DontEnum|Function 3
     loadModule                     JSBuiltin                                           DontEnum|Function 3
     linkAndEvaluateModule          JSBuiltin                                           DontEnum|Function 2
+    importModule                   JSBuiltin                                           DontEnum|Function 3
+    getModuleNamespaceObject       moduleLoaderPrototypeGetModuleNamespaceObject       DontEnum|Function 1
     parseModule                    moduleLoaderPrototypeParseModule                    DontEnum|Function 2
     requestedModules               moduleLoaderPrototypeRequestedModules               DontEnum|Function 1
     resolve                        moduleLoaderPrototypeResolve                        DontEnum|Function 2
@@ -211,6 +215,19 @@ EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeInstantiate(ExecState* exec)
     return JSValue::encode(loader->instantiate(exec, exec->argument(0), exec->argument(1), exec->argument(2)));
 }
 
+EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeGetModuleNamespaceObject(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* loader = jsDynamicCast<JSModuleLoader*>(exec->thisValue());
+    if (!loader)
+        return JSValue::encode(jsUndefined());
+    auto* moduleNamespaceObject = loader->getModuleNamespaceObject(exec, exec->argument(0));
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    return JSValue::encode(moduleNamespaceObject);
+}
+
 // ------------------- Additional Hook Functions ---------------------------
 
 EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeEvaluate(ExecState* exec)
index 1ee5fe7..122ef99 100644 (file)
@@ -1,3 +1,16 @@
+2017-01-09  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Prototype dynamic-import
+        https://bugs.webkit.org/show_bug.cgi?id=165724
+
+        Reviewed by Saam Barati.
+
+        We do not set a handler for import for now.
+        So dynamic import feature is only enabled in the JSC shell right now.
+
+        * bindings/js/JSDOMWindowBase.cpp:
+        * bindings/js/JSWorkerGlobalScopeBase.cpp:
+
 2017-01-09  Youenn Fablet  <youennf@gmail.com>
 
         Merging ThreadableLoader redundant options on filtering responses
index fa8c52d..397e594 100644 (file)
@@ -64,6 +64,7 @@ const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = {
     &javaScriptRuntimeFlags,
     &queueTaskToEventLoop,
     &shouldInterruptScriptBeforeTimeout,
+    nullptr,
     &moduleLoaderResolve,
     &moduleLoaderFetch,
     nullptr,
index 62f76c8..1a5f7a0 100644 (file)
@@ -57,6 +57,7 @@ const GlobalObjectMethodTable JSWorkerGlobalScopeBase::s_globalObjectMethodTable
     nullptr,
     nullptr,
     nullptr,
+    nullptr,
     &defaultLanguage
 };