Ignore:
Timestamp:
Jan 9, 2017, 2:02:47 PM (8 years ago)
Author:
Yusuke Suzuki
Message:

[JSC] Prototype dynamic-import
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 callimport("./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:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jsc.cpp

    r210229 r210522  
    2626#include "ArrayPrototype.h"
    2727#include "BuiltinExecutableCreator.h"
     28#include "BuiltinNames.h"
    2829#include "ButterflyInlines.h"
    2930#include "CodeBlock.h"
     
    4950#include "JSInternalPromiseDeferred.h"
    5051#include "JSLock.h"
     52#include "JSModuleLoader.h"
    5153#include "JSNativeStdFunction.h"
    5254#include "JSONObject.h"
     
    11021104
    11031105template<typename Vector>
    1104 static inline SourceCode jscSource(const Vector& utf8, const String& filename)
     1106static inline SourceCode jscSource(const Vector& utf8, const SourceOrigin& sourceOrigin, const String& filename)
    11051107{
    11061108    String str = stringFromUTF(utf8);
    1107     return makeSource(str, SourceOrigin { filename }, filename);
     1109    return makeSource(str, sourceOrigin, filename);
    11081110}
    11091111
     
    12781280    }
    12791281
     1282    static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString*, const SourceOrigin&);
    12801283    static JSInternalPromise* moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
    12811284    static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue);
     
    12891292    nullptr,
    12901293    &shouldInterruptScriptBeforeTimeout,
     1294    &moduleLoaderImportModule,
    12911295    &moduleLoaderResolve,
    12921296    &moduleLoaderFetch,
     
    14211425}
    14221426
     1427static String absolutePath(const String& fileName)
     1428{
     1429    auto directoryName = currentWorkingDirectory();
     1430    if (!directoryName)
     1431        return fileName;
     1432    return resolvePath(directoryName.value(), ModuleName(fileName.impl()));
     1433}
     1434
     1435JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject*, ExecState* exec, JSModuleLoader* moduleLoader, JSString* moduleName, const SourceOrigin& sourceOrigin)
     1436{
     1437    auto* function = jsCast<JSObject*>(moduleLoader->get(exec, exec->propertyNames().builtinNames().importModulePublicName()));
     1438    CallData callData;
     1439    auto callType = JSC::getCallData(function, callData);
     1440    ASSERT(callType != CallType::None);
     1441
     1442    MarkedArgumentBuffer arguments;
     1443    arguments.append(moduleName);
     1444    arguments.append(jsString(exec, sourceOrigin.string()));
     1445    arguments.append(jsUndefined());
     1446
     1447    return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, moduleLoader, arguments));
     1448}
     1449
    14231450JSInternalPromise* GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)
    14241451{
     
    19251952    StopWatch stopWatch;
    19261953    stopWatch.start();
    1927     evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), exception);
     1954    evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), exception);
    19281955    stopWatch.stop();
    19291956
     
    19772004   
    19782005    NakedPtr<Exception> evaluationException;
    1979     JSValue result = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), evaluationException);
     2006    JSValue result = evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
    19802007    if (evaluationException)
    19812008        throwException(exec, scope, evaluationException);
     
    20482075
    20492076    JSValue syntaxException;
    2050     bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script, fileName), &syntaxException);
     2077    bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), &syntaxException);
    20512078    stopWatch.stop();
    20522079
     
    29102937        if (isModule) {
    29112938            if (!promise)
    2912                 promise = loadAndEvaluateModule(globalObject->globalExec(), jscSource(scriptBuffer, fileName));
     2939                promise = loadAndEvaluateModule(globalObject->globalExec(), jscSource(scriptBuffer, SourceOrigin { absolutePath(fileName) }, fileName));
    29132940            scope.clearException();
    29142941
     
    29272954        } else {
    29282955            NakedPtr<Exception> evaluationException;
    2929             JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(scriptBuffer, fileName), JSValue(), evaluationException);
     2956            JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(scriptBuffer, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
    29302957            ASSERT(!scope.exception());
    29312958            if (evaluationException)
     
    30003027
    30013028        NakedPtr<Exception> evaluationException;
    3002         JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, sourceOrigin.string()), JSValue(), evaluationException);
     3029        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, sourceOrigin, sourceOrigin.string()), JSValue(), evaluationException);
    30033030#endif
    30043031        if (evaluationException)
Note: See TracChangeset for help on using the changeset viewer.