Changeset 223331 in webkit for trunk/Source/JavaScriptCore
- Timestamp:
- Oct 15, 2017, 6:55:16 PM (8 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r223321 r223331 1 2017-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 1 44 2017-10-14 Devin Rousso <[email protected]> 2 45 -
trunk/Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js
r223237 r223331 100 100 linkError: @undefined, 101 101 linkSucceeded: true, 102 evaluated: false, 102 103 }; 103 104 } … … 141 142 // Loader. 142 143 143 function requestFetch( key, parameters, fetcher)144 function requestFetch(entry, parameters, fetcher) 144 145 { 145 146 // https://whatwg.github.io/loader/#request-fetch … … 147 148 "use strict"; 148 149 149 var entry = this.ensureRegistered(key);150 150 if (entry.fetch) 151 151 return entry.fetch; … … 157 157 // For example, JavaScriptCore shell can provide the hook fetching the resource 158 158 // 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) => { 160 160 @setStateToMax(entry, @ModuleInstantiate); 161 161 return source; … … 165 165 } 166 166 167 function requestInstantiate( key, parameters, fetcher)167 function requestInstantiate(entry, parameters, fetcher) 168 168 { 169 169 // https://whatwg.github.io/loader/#request-instantiate … … 171 171 "use strict"; 172 172 173 var entry = this.ensureRegistered(key);174 173 if (entry.instantiate) 175 174 return entry.instantiate; 176 175 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); 179 179 180 180 // FIXME: Described in the draft, … … 185 185 // https://github.com/whatwg/loader/pull/67 186 186 187 var dependencies = [];188 187 var dependenciesMap = moduleRecord.dependenciesMap; 189 moduleRecord.registryEntry = entry;190 188 var requestedModules = this.requestedModules(moduleRecord); 189 var dependencies = @newArrayWithSize(requestedModules.length); 191 190 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); 199 196 } 200 197 entry.dependencies = dependencies; … … 208 205 } 209 206 210 function requestSatisfy( key, parameters, fetcher)207 function requestSatisfy(entry, parameters, fetcher) 211 208 { 212 209 // https://whatwg.github.io/loader/#satisfy-instance … … 214 211 "use strict"; 215 212 216 var entry = this.ensureRegistered(key);217 213 if (entry.satisfy) 218 214 return entry.satisfy; 219 215 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); 222 218 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 { 249 234 // 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); 256 238 } 257 239 … … 285 267 // without constructing the dependency graph by calling dependencyGraph. 286 268 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); 291 271 292 272 this.moduleDeclarationInstantiation(entry.module, entry.key, fetcher); … … 300 280 // Module semantics. 301 281 302 function moduleEvaluation( moduleRecord, fetcher)282 function moduleEvaluation(entry, fetcher) 303 283 { 304 284 // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation … … 306 286 "use strict"; 307 287 308 if ( moduleRecord.evaluated)288 if (entry.evaluated) 309 289 return; 310 moduleRecord.evaluated = true; 311 312 var entry = moduleRecord.registryEntry; 290 entry.evaluated = true; 313 291 314 292 // The contents of the [[RequestedModules]] is cloned into entry.dependencies. 315 293 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); 322 298 } 323 299 … … 344 320 // For example, take the "jquery" and return the URL for the resource. 345 321 return this.resolve(moduleName, @undefined, fetcher).then((key) => { 346 return this.requestSatisfy( key, parameters, fetcher);322 return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher); 347 323 }).then((entry) => { 348 324 return entry.key; … … 359 335 360 336 this.link(entry, fetcher); 361 return this.moduleEvaluation(entry .module, fetcher);337 return this.moduleEvaluation(entry, fetcher); 362 338 } 363 339 … … 375 351 "use strict"; 376 352 377 return this.requestSatisfy( key, parameters, fetcher).then((entry) => {353 return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher).then((entry) => { 378 354 this.linkAndEvaluateModule(entry.key, fetcher); 379 355 return this.getModuleNamespaceObject(entry.module); -
trunk/Source/JavaScriptCore/jsc.cpp
r223237 r223331 1659 1659 1660 1660 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); 1662 1662 static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); 1663 1663 static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue); … … 1841 1841 } 1842 1842 1843 JSInternalPromise*GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)1843 Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue) 1844 1844 { 1845 1845 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 1849 1848 scope.releaseAssertNoException(); 1850 1849 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 1862 1855 if (referrerValue.isUndefined()) { 1863 1856 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()))); 1869 1862 } 1870 1863 1871 1864 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, { }); 1877 1866 1878 1867 if (referrer.isSymbol()) { 1879 1868 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()))); 1885 1874 } 1886 1875 1887 1876 // If the referrer exists, we assume that the referrer is the correct absolute path. 1888 1877 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()))); 1894 1883 } 1895 1884 -
trunk/Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp
r221849 r223331 55 55 Base::finishCreation(vm); 56 56 ASSERT(inherits(vm, info())); 57 putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("registryEntry")), jsUndefined());58 putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("evaluated")), jsBoolean(false));59 57 60 58 auto scope = DECLARE_THROW_SCOPE(vm); … … 150 148 auto scope = DECLARE_THROW_SCOPE(vm); 151 149 JSValue moduleNameValue = identifierToJSValue(exec, moduleName); 152 JSValue pair= m_dependenciesMap->JSMap::get(exec, moduleNameValue);150 JSValue entry = m_dependenciesMap->JSMap::get(exec, moduleNameValue); 153 151 RETURN_IF_EXCEPTION(scope, nullptr); 154 152 scope.release(); 155 return jsCast<AbstractModuleRecord*>( pair.get(exec, Identifier::fromString(exec, "value")));153 return jsCast<AbstractModuleRecord*>(entry.get(exec, Identifier::fromString(exec, "module"))); 156 154 } 157 155 -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h
r223237 r223331 190 190 ModuleLoaderImportModulePtr moduleLoaderImportModule; 191 191 192 typedef JSInternalPromise*(*ModuleLoaderResolvePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);192 typedef Identifier (*ModuleLoaderResolvePtr)(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue); 193 193 ModuleLoaderResolvePtr moduleLoaderResolve; 194 194 -
trunk/Source/JavaScriptCore/runtime/JSModuleLoader.cpp
r223237 r223331 203 203 } 204 204 205 JSInternalPromise* JSModuleLoader::resolve(ExecState* exec, JSValue name, JSValue referrer, JSValue scriptFetcher)205 Identifier JSModuleLoader::resolveSync(ExecState* exec, JSValue name, JSValue referrer, JSValue scriptFetcher) 206 206 { 207 207 if (Options::dumpModuleLoadingState()) … … 211 211 if (globalObject->globalObjectMethodTable()->moduleLoaderResolve) 212 212 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 216 JSInternalPromise* 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; 216 232 } 217 233 -
trunk/Source/JavaScriptCore/runtime/JSModuleLoader.h
r223237 r223331 73 73 JSInternalPromise* importModule(ExecState*, JSString* moduleName, JSValue parameters, const SourceOrigin& referrer); 74 74 JSInternalPromise* resolve(ExecState*, JSValue name, JSValue referrer, JSValue scriptFetcher); 75 Identifier resolveSync(ExecState*, JSValue name, JSValue referrer, JSValue scriptFetcher); 75 76 JSInternalPromise* fetch(ExecState*, JSValue key, JSValue parameters, JSValue scriptFetcher); 76 77 JSObject* createImportMetaProperties(ExecState*, JSValue key, JSModuleRecord*, JSValue scriptFetcher); -
trunk/Source/JavaScriptCore/runtime/ModuleLoaderPrototype.cpp
r223237 r223331 28 28 29 29 #include "BuiltinNames.h" 30 #include "CatchScope.h" 30 31 #include "CodeProfiling.h" 31 32 #include "Error.h" … … 53 54 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeModuleDeclarationInstantiation(ExecState*); 54 55 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolve(ExecState*); 56 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeResolveSync(ExecState*); 55 57 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeFetch(ExecState*); 56 58 static EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeGetModuleNamespaceObject(ExecState*); … … 87 89 requestedModules moduleLoaderPrototypeRequestedModules DontEnum|Function 1 88 90 resolve moduleLoaderPrototypeResolve DontEnum|Function 2 91 resolveSync moduleLoaderPrototypeResolveSync DontEnum|Function 2 89 92 fetch moduleLoaderPrototypeFetch DontEnum|Function 3 90 93 @end … … 183 186 } 184 187 188 EncodedJSValue 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 185 201 EncodedJSValue JSC_HOST_CALL moduleLoaderPrototypeFetch(ExecState* exec) 186 202 {
Note:
See TracChangeset
for help on using the changeset viewer.