Changeset 223331 in webkit for trunk/Source/JavaScriptCore


Ignore:
Timestamp:
Oct 15, 2017, 6:55:16 PM (8 years ago)
Author:
Yusuke Suzuki
Message:

[JSC] Perform module specifier validation at parsing time
https://bugs.webkit.org/show_bug.cgi?id=178256

Reviewed by Darin Adler.

Source/JavaScriptCore:

This patch make module loader's resolve operation synchronous. And we validate
module's requested module names when instantiating the module instead of satisfying
module's dependencies. This change is not observable to users. But this is precise
to the spec and this optimizes & simplifies the current module loader a bit by
reducing object allocations.

Previously, we have an object called pair in the module loader. This is pair of
module's name and module's record. And we use it to link one module to dependent
modules. Now, it is replaced with module's registry entry.

We also change our loader functions to take a registry entry instead of a module key.
Previous design is due to the consideration that these APIs may be exposed to users
in whatwg/loader spec. However, this won't happen. This change removes unnecessary
repeatedly hash map lookups.

  • builtins/ModuleLoaderPrototype.js:

(globalPrivate.newRegistryEntry):
(requestFetch):
(requestInstantiate):
(requestSatisfy):
(link):
(moduleEvaluation):
(loadModule):

  • jsc.cpp:

(GlobalObject::moduleLoaderResolve):

  • runtime/AbstractModuleRecord.cpp:

(JSC::AbstractModuleRecord::finishCreation):
(JSC::AbstractModuleRecord::hostResolveImportedModule):

  • runtime/JSGlobalObject.h:
  • runtime/JSModuleLoader.cpp:

(JSC::JSModuleLoader::resolveSync):
(JSC::JSModuleLoader::resolve):

  • runtime/JSModuleLoader.h:
  • runtime/ModuleLoaderPrototype.cpp:

(JSC::moduleLoaderPrototypeResolveSync):

Source/WebCore:

No behavior change in the current implementation.

  • bindings/js/JSDOMWindowBase.cpp:

(WebCore::JSDOMWindowBase::moduleLoaderResolve):

  • bindings/js/JSDOMWindowBase.h:
  • bindings/js/ScriptModuleLoader.cpp:

(WebCore::ScriptModuleLoader::resolve):

  • bindings/js/ScriptModuleLoader.h:
Location:
trunk/Source/JavaScriptCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r223321 r223331  
     12017-10-15  Yusuke Suzuki  <[email protected]>
     2
     3        [JSC] Perform module specifier validation at parsing time
     4        https://bugs.webkit.org/show_bug.cgi?id=178256
     5
     6        Reviewed by Darin Adler.
     7
     8        This patch make module loader's `resolve` operation synchronous. And we validate
     9        module's requested module names when instantiating the module instead of satisfying
     10        module's dependencies. This change is not observable to users. But this is precise
     11        to the spec and this optimizes & simplifies the current module loader a bit by
     12        reducing object allocations.
     13
     14        Previously, we have an object called pair in the module loader. This is pair of
     15        module's name and module's record. And we use it to link one module to dependent
     16        modules. Now, it is replaced with module's registry entry.
     17
     18        We also change our loader functions to take a registry entry instead of a module key.
     19        Previous design is due to the consideration that these APIs may be exposed to users
     20        in whatwg/loader spec. However, this won't happen. This change removes unnecessary
     21        repeatedly hash map lookups.
     22
     23        * builtins/ModuleLoaderPrototype.js:
     24        (globalPrivate.newRegistryEntry):
     25        (requestFetch):
     26        (requestInstantiate):
     27        (requestSatisfy):
     28        (link):
     29        (moduleEvaluation):
     30        (loadModule):
     31        * jsc.cpp:
     32        (GlobalObject::moduleLoaderResolve):
     33        * runtime/AbstractModuleRecord.cpp:
     34        (JSC::AbstractModuleRecord::finishCreation):
     35        (JSC::AbstractModuleRecord::hostResolveImportedModule):
     36        * runtime/JSGlobalObject.h:
     37        * runtime/JSModuleLoader.cpp:
     38        (JSC::JSModuleLoader::resolveSync):
     39        (JSC::JSModuleLoader::resolve):
     40        * runtime/JSModuleLoader.h:
     41        * runtime/ModuleLoaderPrototype.cpp:
     42        (JSC::moduleLoaderPrototypeResolveSync):
     43
    1442017-10-14  Devin Rousso  <[email protected]>
    245
  • trunk/Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js

    r223237 r223331  
    100100        linkError: @undefined,
    101101        linkSucceeded: true,
     102        evaluated: false,
    102103    };
    103104}
     
    141142// Loader.
    142143
    143 function requestFetch(key, parameters, fetcher)
     144function requestFetch(entry, parameters, fetcher)
    144145{
    145146    // https://whatwg.github.io/loader/#request-fetch
     
    147148    "use strict";
    148149
    149     var entry = this.ensureRegistered(key);
    150150    if (entry.fetch)
    151151        return entry.fetch;
     
    157157    //     For example, JavaScriptCore shell can provide the hook fetching the resource
    158158    //     from the local file system.
    159     var fetchPromise = this.fetch(key, parameters, fetcher).then((source) => {
     159    var fetchPromise = this.fetch(entry.key, parameters, fetcher).then((source) => {
    160160        @setStateToMax(entry, @ModuleInstantiate);
    161161        return source;
     
    165165}
    166166
    167 function requestInstantiate(key, parameters, fetcher)
     167function requestInstantiate(entry, parameters, fetcher)
    168168{
    169169    // https://whatwg.github.io/loader/#request-instantiate
     
    171171    "use strict";
    172172
    173     var entry = this.ensureRegistered(key);
    174173    if (entry.instantiate)
    175174        return entry.instantiate;
    176175
    177     var instantiatePromise = this.requestFetch(key, parameters, fetcher).then((source) => {
    178         var moduleRecord = this.parseModule(entry.key, source);
     176    var instantiatePromise = this.requestFetch(entry, parameters, fetcher).then((source) => {
     177        var key = entry.key;
     178        var moduleRecord = this.parseModule(key, source);
    179179
    180180        // FIXME: Described in the draft,
     
    185185        // https://github.com/whatwg/loader/pull/67
    186186
    187         var dependencies = [];
    188187        var dependenciesMap = moduleRecord.dependenciesMap;
    189         moduleRecord.registryEntry = entry;
    190188        var requestedModules = this.requestedModules(moduleRecord);
     189        var dependencies = @newArrayWithSize(requestedModules.length);
    191190        for (var i = 0, length = requestedModules.length; i < length; ++i) {
    192             var depKey = requestedModules[i];
    193             var pair = {
    194                 key: depKey,
    195                 value: @undefined
    196             };
    197             @putByValDirect(dependencies, dependencies.length, pair);
    198             dependenciesMap.@set(depKey, pair);
     191            var depName = requestedModules[i];
     192            var depKey = this.resolveSync(depName, key, fetcher);
     193            var depEntry = this.ensureRegistered(depKey);
     194            @putByValDirect(dependencies, i, depEntry);
     195            dependenciesMap.@set(depName, depEntry);
    199196        }
    200197        entry.dependencies = dependencies;
     
    208205}
    209206
    210 function requestSatisfy(key, parameters, fetcher)
     207function requestSatisfy(entry, parameters, fetcher)
    211208{
    212209    // https://whatwg.github.io/loader/#satisfy-instance
     
    214211    "use strict";
    215212
    216     var entry = this.ensureRegistered(key);
    217213    if (entry.satisfy)
    218214        return entry.satisfy;
    219215
    220     var satisfyPromise = this.requestInstantiate(key, parameters, fetcher).then((entry) => {
    221         var depLoads = [];
     216    var satisfyPromise = this.requestInstantiate(entry, parameters, fetcher).then((entry) => {
     217        var depLoads = @newArrayWithSize(entry.dependencies.length);
    222218        for (var i = 0, length = entry.dependencies.length; i < length; ++i) {
    223             let pair = entry.dependencies[i];
    224 
    225             // Hook point.
    226             // 1. Loader.resolve.
    227             //     https://whatwg.github.io/loader/#browser-resolve
    228             //     Take the name and resolve it to the unique identifier for the resource location.
    229             //     For example, take the "jquery" and return the URL for the resource.
    230             var promise = this.resolve(pair.key, key, fetcher).then((depKey) => {
    231                 var depEntry = this.ensureRegistered(depKey);
    232 
    233                 // Recursive resolving. The dependencies of this entry is being resolved or already resolved.
    234                 // Stop tracing the circular dependencies.
    235                 // But to retrieve the instantiated module record correctly,
    236                 // we need to wait for the instantiation for the dependent module.
    237                 // For example, reaching here, the module is starting resolving the dependencies.
    238                 // But the module may or may not reach the instantiation phase in the loader's pipeline.
    239                 // If we wait for the Satisfy for this module, it construct the circular promise chain and
    240                 // rejected by the Promises runtime. Since only we need is the instantiated module, instead of waiting
    241                 // the Satisfy for this module, we just wait Instantiate for this.
    242                 if (depEntry.satisfy) {
    243                     return depEntry.instantiate.then((entry) => {
    244                         pair.value = entry.module;
    245                         return entry;
    246                     });
    247                 }
    248 
     219            var depEntry = entry.dependencies[i];
     220            var promise = @undefined;
     221
     222            // Recursive resolving. The dependencies of this entry is being resolved or already resolved.
     223            // Stop tracing the circular dependencies.
     224            // But to retrieve the instantiated module record correctly,
     225            // we need to wait for the instantiation for the dependent module.
     226            // For example, reaching here, the module is starting resolving the dependencies.
     227            // But the module may or may not reach the instantiation phase in the loader's pipeline.
     228            // If we wait for the Satisfy for this module, it construct the circular promise chain and
     229            // rejected by the Promises runtime. Since only we need is the instantiated module, instead of waiting
     230            // the Satisfy for this module, we just wait Instantiate for this.
     231            if (depEntry.satisfy)
     232                promise = depEntry.instantiate;
     233            else {
    249234                // Currently, module loader do not pass any information for non-top-level module fetching.
    250                 return this.requestSatisfy(depKey, @undefined, fetcher).then((entry) => {
    251                     pair.value = entry.module;
    252                     return entry;
    253                 });
    254             });
    255             @putByValDirect(depLoads, depLoads.length, promise);
     235                promise = this.requestSatisfy(depEntry, @undefined, fetcher);
     236            }
     237            @putByValDirect(depLoads, i, promise);
    256238        }
    257239
     
    285267        // without constructing the dependency graph by calling dependencyGraph.
    286268        var dependencies = entry.dependencies;
    287         for (var i = 0, length = dependencies.length; i < length; ++i) {
    288             var pair = dependencies[i];
    289             this.link(pair.value.registryEntry, fetcher);
    290         }
     269        for (var i = 0, length = dependencies.length; i < length; ++i)
     270            this.link(dependencies[i], fetcher);
    291271
    292272        this.moduleDeclarationInstantiation(entry.module, entry.key, fetcher);
     
    300280// Module semantics.
    301281
    302 function moduleEvaluation(moduleRecord, fetcher)
     282function moduleEvaluation(entry, fetcher)
    303283{
    304284    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation
     
    306286    "use strict";
    307287
    308     if (moduleRecord.evaluated)
     288    if (entry.evaluated)
    309289        return;
    310     moduleRecord.evaluated = true;
    311 
    312     var entry = moduleRecord.registryEntry;
     290    entry.evaluated = true;
    313291
    314292    // The contents of the [[RequestedModules]] is cloned into entry.dependencies.
    315293    var dependencies = entry.dependencies;
    316     for (var i = 0, length = dependencies.length; i < length; ++i) {
    317         var pair = dependencies[i];
    318         var requiredModuleRecord = pair.value;
    319         this.moduleEvaluation(requiredModuleRecord, fetcher);
    320     }
    321     this.evaluate(entry.key, moduleRecord, fetcher);
     294    for (var i = 0, length = dependencies.length; i < length; ++i)
     295        this.moduleEvaluation(dependencies[i], fetcher);
     296
     297    this.evaluate(entry.key, entry.module, fetcher);
    322298}
    323299
     
    344320    // For example, take the "jquery" and return the URL for the resource.
    345321    return this.resolve(moduleName, @undefined, fetcher).then((key) => {
    346         return this.requestSatisfy(key, parameters, fetcher);
     322        return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher);
    347323    }).then((entry) => {
    348324        return entry.key;
     
    359335
    360336    this.link(entry, fetcher);
    361     return this.moduleEvaluation(entry.module, fetcher);
     337    return this.moduleEvaluation(entry, fetcher);
    362338}
    363339
     
    375351    "use strict";
    376352
    377     return this.requestSatisfy(key, parameters, fetcher).then((entry) => {
     353    return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher).then((entry) => {
    378354        this.linkAndEvaluateModule(entry.key, fetcher);
    379355        return this.getModuleNamespaceObject(entry.module);
  • trunk/Source/JavaScriptCore/jsc.cpp

    r223237 r223331  
    16591659
    16601660    static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString*, JSValue, const SourceOrigin&);
    1661     static JSInternalPromise* moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
     1661    static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
    16621662    static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
    16631663    static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
     
    18411841}
    18421842
    1843 JSInternalPromise* GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)
     1843Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)
    18441844{
    18451845    VM& vm = globalObject->vm();
    1846     auto scope = DECLARE_CATCH_SCOPE(vm);
    1847 
    1848     JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
     1846    auto scope = DECLARE_THROW_SCOPE(vm);
     1847
    18491848    scope.releaseAssertNoException();
    18501849    const Identifier key = keyValue.toPropertyKey(exec);
    1851     if (UNLIKELY(scope.exception())) {
    1852         JSValue exception = scope.exception();
    1853         scope.clearException();
    1854         return deferred->reject(exec, exception);
    1855     }
    1856 
    1857     if (key.isSymbol()) {
    1858         auto result = deferred->resolve(exec, keyValue);
    1859         scope.releaseAssertNoException();
    1860         return result;
    1861     }
     1850    RETURN_IF_EXCEPTION(scope, { });
     1851
     1852    if (key.isSymbol())
     1853        return key;
     1854
    18621855    if (referrerValue.isUndefined()) {
    18631856        auto directoryName = currentWorkingDirectory();
    1864         if (!directoryName)
    1865             return deferred->reject(exec, createError(exec, ASCIILiteral("Could not resolve the current working directory.")));
    1866         auto result = deferred->resolve(exec, jsString(exec, resolvePath(directoryName.value(), ModuleName(key.impl()))));
    1867         scope.releaseAssertNoException();
    1868         return result;
     1857        if (!directoryName) {
     1858            throwException(exec, scope, createError(exec, ASCIILiteral("Could not resolve the current working directory.")));
     1859            return { };
     1860        }
     1861        return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
    18691862    }
    18701863
    18711864    const Identifier referrer = referrerValue.toPropertyKey(exec);
    1872     if (UNLIKELY(scope.exception())) {
    1873         JSValue exception = scope.exception();
    1874         scope.clearException();
    1875         return deferred->reject(exec, exception);
    1876     }
     1865    RETURN_IF_EXCEPTION(scope, { });
    18771866
    18781867    if (referrer.isSymbol()) {
    18791868        auto directoryName = currentWorkingDirectory();
    1880         if (!directoryName)
    1881             return deferred->reject(exec, createError(exec, ASCIILiteral("Could not resolve the current working directory.")));
    1882         auto result = deferred->resolve(exec, jsString(exec, resolvePath(directoryName.value(), ModuleName(key.impl()))));
    1883         scope.releaseAssertNoException();
    1884         return result;
     1869        if (!directoryName) {
     1870            throwException(exec, scope, createError(exec, ASCIILiteral("Could not resolve the current working directory.")));
     1871            return { };
     1872        }
     1873        return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
    18851874    }
    18861875
    18871876    // If the referrer exists, we assume that the referrer is the correct absolute path.
    18881877    auto directoryName = extractDirectoryName(referrer.impl());
    1889     if (!directoryName)
    1890         return deferred->reject(exec, createError(exec, makeString("Could not resolve the referrer name '", String(referrer.impl()), "'.")));
    1891     auto result = deferred->resolve(exec, jsString(exec, resolvePath(directoryName.value(), ModuleName(key.impl()))));
    1892     scope.releaseAssertNoException();
    1893     return result;
     1878    if (!directoryName) {
     1879        throwException(exec, scope, createError(exec, makeString("Could not resolve the referrer name '", String(referrer.impl()), "'.")));
     1880        return { };
     1881    }
     1882    return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
    18941883}
    18951884
  • trunk/Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp

    r221849 r223331  
    5555    Base::finishCreation(vm);
    5656    ASSERT(inherits(vm, info()));
    57     putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("registryEntry")), jsUndefined());
    58     putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("evaluated")), jsBoolean(false));
    5957
    6058    auto scope = DECLARE_THROW_SCOPE(vm);
     
    150148    auto scope = DECLARE_THROW_SCOPE(vm);
    151149    JSValue moduleNameValue = identifierToJSValue(exec, moduleName);
    152     JSValue pair = m_dependenciesMap->JSMap::get(exec, moduleNameValue);
     150    JSValue entry = m_dependenciesMap->JSMap::get(exec, moduleNameValue);
    153151    RETURN_IF_EXCEPTION(scope, nullptr);
    154152    scope.release();
    155     return jsCast<AbstractModuleRecord*>(pair.get(exec, Identifier::fromString(exec, "value")));
     153    return jsCast<AbstractModuleRecord*>(entry.get(exec, Identifier::fromString(exec, "module")));
    156154}
    157155
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h

    r223237 r223331  
    190190    ModuleLoaderImportModulePtr moduleLoaderImportModule;
    191191
    192     typedef JSInternalPromise* (*ModuleLoaderResolvePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
     192    typedef Identifier (*ModuleLoaderResolvePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
    193193    ModuleLoaderResolvePtr moduleLoaderResolve;
    194194
  • trunk/Source/JavaScriptCore/runtime/JSModuleLoader.cpp

    r223237 r223331  
    203203}
    204204
    205 JSInternalPromise* JSModuleLoader::resolve(ExecState* exec, JSValue name, JSValue referrer, JSValue scriptFetcher)
     205Identifier JSModuleLoader::resolveSync(ExecState* exec, JSValue name, JSValue referrer, JSValue scriptFetcher)
    206206{
    207207    if (Options::dumpModuleLoadingState())
     
    211211    if (globalObject->globalObjectMethodTable()->moduleLoaderResolve)
    212212        return globalObject->globalObjectMethodTable()->moduleLoaderResolve(globalObject, exec, this, name, referrer, scriptFetcher);
    213     JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
    214     deferred->resolve(exec, name);
    215     return deferred->promise();
     213    return name.toPropertyKey(exec);
     214}
     215
     216JSInternalPromise* JSModuleLoader::resolve(ExecState* exec, JSValue name, JSValue referrer, JSValue scriptFetcher)
     217{
     218    VM& vm = exec->vm();
     219    auto scope = DECLARE_CATCH_SCOPE(vm);
     220
     221    JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, exec->lexicalGlobalObject());
     222    scope.releaseAssertNoException();
     223    const Identifier moduleKey = resolveSync(exec, name, referrer, scriptFetcher);
     224    if (UNLIKELY(scope.exception())) {
     225        JSValue exception = scope.exception();
     226        scope.clearException();
     227        return deferred->reject(exec, exception);
     228    }
     229    auto result = deferred->resolve(exec, identifierToJSValue(vm, moduleKey));
     230    scope.releaseAssertNoException();
     231    return result;
    216232}
    217233
  • trunk/Source/JavaScriptCore/runtime/JSModuleLoader.h

    r223237 r223331  
    7373    JSInternalPromise* importModule(ExecState*, JSString* moduleName, JSValue parameters, const SourceOrigin& referrer);
    7474    JSInternalPromise* resolve(ExecState*, JSValue name, JSValue referrer, JSValue scriptFetcher);
     75    Identifier resolveSync(ExecState*, JSValue name, JSValue referrer, JSValue scriptFetcher);
    7576    JSInternalPromise* fetch(ExecState*, JSValue key, JSValue parameters, JSValue scriptFetcher);
    7677    JSObject* createImportMetaProperties(ExecState*, JSValue key, JSModuleRecord*, JSValue scriptFetcher);
  • trunk/Source/JavaScriptCore/runtime/ModuleLoaderPrototype.cpp

    r223237 r223331  
    2828
    2929#include "BuiltinNames.h"
     30#include "CatchScope.h"
    3031#include "CodeProfiling.h"
    3132#include "Error.h"
     
    5354static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeModuleDeclarationInstantiation(ExecState*);
    5455static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolve(ExecState*);
     56static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolveSync(ExecState*);
    5557static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeFetch(ExecState*);
    5658static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeGetModuleNamespaceObject(ExecState*);
     
    8789    requestedModules               moduleLoaderPrototypeRequestedModules               DontEnum|Function 1
    8890    resolve                        moduleLoaderPrototypeResolve                        DontEnum|Function 2
     91    resolveSync                    moduleLoaderPrototypeResolveSync                    DontEnum|Function 2
    8992    fetch                          moduleLoaderPrototypeFetch                          DontEnum|Function 3
    9093@end
     
    183186}
    184187
     188EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolveSync(ExecState* exec)
     189{
     190    VM& vm = exec->vm();
     191    auto scope = DECLARE_CATCH_SCOPE(vm);
     192
     193    JSModuleLoader* loader = jsDynamicCast<JSModuleLoader*>(vm, exec->thisValue());
     194    if (!loader)
     195        return JSValue::encode(jsUndefined());
     196    auto result = loader->resolveSync(exec, exec->argument(0), exec->argument(1), exec->argument(2));
     197    RETURN_IF_EXCEPTION(scope, encodedJSValue());
     198    return JSValue::encode(identifierToJSValue(vm, result));
     199}
     200
    185201EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeFetch(ExecState* exec)
    186202{
Note: See TracChangeset for help on using the changeset viewer.