GeezerVison is showing github.com — module: centralize SourceTextModule compilation for builtin loader · nodejs/node@4dae68c
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/create_dynamic_module.js +2-3Lines changed: 2 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -55,8 +55,8 @@ ${ArrayPrototypeJoin(ArrayPrototypeMap(imports, createImport), '\n')} | 55 | 55 | ${ArrayPrototypeJoin(ArrayPrototypeMap(exports, createExport), '\n')} | 56 | 56 | import.meta.done(); | 57 | 57 | `; | 58 | | - const { ModuleWrap } = internalBinding('module_wrap'); | 59 | | - const m = new ModuleWrap(`${url}`, undefined, source, 0, 0); | | 58 | + const { registerModule, compileSourceTextModule } = require('internal/modules/esm/utils'); | | 59 | + const m = compileSourceTextModule(`${url}`, source); | 60 | 60 |
| 61 | 61 | const readyfns = new SafeSet(); | 62 | 62 | /** @type {DynamicModuleReflect} */ | @@ -68,7 +68,6 @@ import.meta.done(); | 68 | 68 | if (imports.length) { | 69 | 69 | reflect.imports = { __proto__: null }; | 70 | 70 | } | 71 | | - const { registerModule } = require('internal/modules/esm/utils'); | 72 | 71 | registerModule(m, { | 73 | 72 | __proto__: null, | 74 | 73 | initializeImportMeta: (meta, wrap) => { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/loader.js +3-31Lines changed: 3 additions & 31 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -25,13 +25,10 @@ const { getOptionValue } = require('internal/options'); | 25 | 25 | const { isURL, pathToFileURL, URL } = require('internal/url'); | 26 | 26 | const { emitExperimentalWarning, kEmptyObject } = require('internal/util'); | 27 | 27 | const { | 28 | | - registerModule, | | 28 | + compileSourceTextModule, | 29 | 29 | getDefaultConditions, | 30 | 30 | } = require('internal/modules/esm/utils'); | 31 | 31 | const { kImplicitAssertType } = require('internal/modules/esm/assert'); | 32 | | -const { | 33 | | - maybeCacheSourceMap, | 34 | | -} = require('internal/source_map/source_map_cache'); | 35 | 32 | const { canParse } = internalBinding('url'); | 36 | 33 | const { ModuleWrap } = internalBinding('module_wrap'); | 37 | 34 | let defaultResolve, defaultLoad, defaultLoadSync, importMetaInitializer; | @@ -193,16 +190,7 @@ class ModuleLoader { | 193 | 190 |
| 194 | 191 | async eval(source, url) { | 195 | 192 | const evalInstance = (url) => { | 196 | | - const module = new ModuleWrap(url, undefined, source, 0, 0); | 197 | | - registerModule(module, { | 198 | | - __proto__: null, | 199 | | - initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), | 200 | | - importModuleDynamically: (specifier, { url }, importAttributes) => { | 201 | | - return this.import(specifier, url, importAttributes); | 202 | | - }, | 203 | | - }); | 204 | | - | 205 | | - return module; | | 193 | + return compileSourceTextModule(url, source, this); | 206 | 194 | }; | 207 | 195 | const { ModuleJob } = require('internal/modules/esm/module_job'); | 208 | 196 | const job = new ModuleJob( | @@ -273,26 +261,10 @@ class ModuleLoader { | 273 | 261 | if (job !== undefined) { | 274 | 262 | return job.module.getNamespaceSync(); | 275 | 263 | } | 276 | | - | 277 | 264 | // TODO(joyeecheung): refactor this so that we pre-parse in C++ and hit the | 278 | 265 | // cache here, or use a carrier object to carry the compiled module script | 279 | 266 | // into the constructor to ensure cache hit. | 280 | | - const wrap = new ModuleWrap(url, undefined, source, 0, 0); | 281 | | - // Cache the source map for the module if present. | 282 | | - if (wrap.sourceMapURL) { | 283 | | - maybeCacheSourceMap(url, source, null, false, undefined, wrap.sourceMapURL); | 284 | | - } | 285 | | - const { registerModule } = require('internal/modules/esm/utils'); | 286 | | - // TODO(joyeecheung): refactor so that the default options are shared across | 287 | | - // the built-in loaders. | 288 | | - registerModule(wrap, { | 289 | | - __proto__: null, | 290 | | - initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), | 291 | | - importModuleDynamically: (specifier, wrap, importAttributes) => { | 292 | | - return this.import(specifier, url, importAttributes); | 293 | | - }, | 294 | | - }); | 295 | | - | | 267 | + const wrap = compileSourceTextModule(url, source, this); | 296 | 268 | const inspectBrk = (isMain && getOptionValue('--inspect-brk')); | 297 | 269 |
| 298 | 270 | const { ModuleJobSync } = require('internal/modules/esm/module_job'); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js +2-24Lines changed: 2 additions & 24 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -156,35 +156,13 @@ function errPath(url) { | 156 | 156 | return url; | 157 | 157 | } | 158 | 158 |
| 159 | | -/** | 160 | | - * Dynamically imports a module using the ESM loader. | 161 | | - * @param {string} specifier - The module specifier to import. | 162 | | - * @param {object} options - An object containing options for the import. | 163 | | - * @param {string} options.url - The URL of the module requesting the import. | 164 | | - * @param {Record<string, string>} [attributes] - An object containing attributes for the import. | 165 | | - * @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} The imported module. | 166 | | - */ | 167 | | -async function importModuleDynamically(specifier, { url }, attributes) { | 168 | | - const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); | 169 | | - return cascadedLoader.import(specifier, url, attributes); | 170 | | -} | 171 | | - | 172 | 159 | // Strategy for loading a standard JavaScript module. | 173 | 160 | translators.set('module', function moduleStrategy(url, source, isMain) { | 174 | 161 | assertBufferSource(source, true, 'load'); | 175 | 162 | source = stringify(source); | 176 | 163 | debug(`Translating StandardModule ${url}`); | 177 | | - const module = new ModuleWrap(url, undefined, source, 0, 0); | 178 | | - // Cache the source map for the module if present. | 179 | | - if (module.sourceMapURL) { | 180 | | - maybeCacheSourceMap(url, source, null, false, undefined, module.sourceMapURL); | 181 | | - } | 182 | | - const { registerModule } = require('internal/modules/esm/utils'); | 183 | | - registerModule(module, { | 184 | | - __proto__: null, | 185 | | - initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), | 186 | | - importModuleDynamically, | 187 | | - }); | | 164 | + const { compileSourceTextModule } = require('internal/modules/esm/utils'); | | 165 | + const module = compileSourceTextModule(url, source, this); | 188 | 166 | return module; | 189 | 167 | }); | 190 | 168 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/utils.js +71-10Lines changed: 71 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -13,12 +13,18 @@ const { | 13 | 13 | }, | 14 | 14 | } = internalBinding('util'); | 15 | 15 | const { | | 16 | + source_text_module_default_hdo, | 16 | 17 | vm_dynamic_import_default_internal, | 17 | 18 | vm_dynamic_import_main_context_default, | 18 | 19 | vm_dynamic_import_missing_flag, | 19 | 20 | vm_dynamic_import_no_callback, | 20 | 21 | } = internalBinding('symbols'); | 21 | 22 |
| | 23 | +const { ModuleWrap } = internalBinding('module_wrap'); | | 24 | +const { | | 25 | + maybeCacheSourceMap, | | 26 | +} = require('internal/source_map/source_map_cache'); | | 27 | + | 22 | 28 | const { | 23 | 29 | ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG, | 24 | 30 | ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, | @@ -167,28 +173,55 @@ function registerModule(referrer, registry) { | 167 | 173 | moduleRegistries.set(idSymbol, registry); | 168 | 174 | } | 169 | 175 |
| | 176 | +/** | | 177 | + * Proxy the import meta handling to the default loader for source text modules. | | 178 | + * @param {Record<string, string | Function>} meta - The import.meta object to initialize. | | 179 | + * @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced. | | 180 | + */ | | 181 | +function defaultInitializeImportMetaForModule(meta, wrap) { | | 182 | + const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); | | 183 | + return cascadedLoader.importMetaInitialize(meta, { url: wrap.url }); | | 184 | +} | | 185 | + | 170 | 186 | /** | 171 | 187 | * Defines the `import.meta` object for a given module. | 172 | 188 | * @param {symbol} symbol - Reference to the module. | 173 | 189 | * @param {Record<string, string | Function>} meta - The import.meta object to initialize. | | 190 | + * @param {ModuleWrap} wrap - The ModuleWrap of the SourceTextModule where `import.meta` is referenced. | 174 | 191 | */ | 175 | | -function initializeImportMetaObject(symbol, meta) { | 176 | | - if (moduleRegistries.has(symbol)) { | 177 | | - const { initializeImportMeta, callbackReferrer } = moduleRegistries.get(symbol); | 178 | | - if (initializeImportMeta !== undefined) { | 179 | | - meta = initializeImportMeta(meta, callbackReferrer); | 180 | | - } | | 192 | +function initializeImportMetaObject(symbol, meta, wrap) { | | 193 | + if (symbol === source_text_module_default_hdo) { | | 194 | + defaultInitializeImportMetaForModule(meta, wrap); | | 195 | + return; | | 196 | + } | | 197 | + const data = moduleRegistries.get(symbol); | | 198 | + assert(data, `import.meta registry not found for ${wrap.url}`); | | 199 | + const { initializeImportMeta, callbackReferrer } = data; | | 200 | + if (initializeImportMeta !== undefined) { | | 201 | + meta = initializeImportMeta(meta, callbackReferrer); | 181 | 202 | } | 182 | 203 | } | 183 | 204 |
| 184 | 205 | /** | 185 | | - * Proxy the dynamic import to the default loader. | | 206 | + * Proxy the dynamic import handling to the default loader for source text modules. | | 207 | + * @param {string} specifier - The module specifier string. | | 208 | + * @param {Record<string, string>} attributes - The import attributes object. | | 209 | + * @param {string|null|undefined} referrerName - name of the referrer. | | 210 | + * @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} - The imported module object. | | 211 | + */ | | 212 | +function defaultImportModuleDynamicallyForModule(specifier, attributes, referrerName) { | | 213 | + const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); | | 214 | + return cascadedLoader.import(specifier, referrerName, attributes); | | 215 | +} | | 216 | + | | 217 | +/** | | 218 | + * Proxy the dynamic import to the default loader for classic scripts. | 186 | 219 | * @param {string} specifier - The module specifier string. | 187 | 220 | * @param {Record<string, string>} attributes - The import attributes object. | 188 | 221 | * @param {string|null|undefined} referrerName - name of the referrer. | 189 | 222 | * @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} - The imported module object. | 190 | 223 | */ | 191 | | -function defaultImportModuleDynamically(specifier, attributes, referrerName) { | | 224 | +function defaultImportModuleDynamicallyForScript(specifier, attributes, referrerName) { | 192 | 225 | const parentURL = normalizeReferrerURL(referrerName); | 193 | 226 | const cascadedLoader = require('internal/modules/esm/loader').getOrInitializeCascadedLoader(); | 194 | 227 | return cascadedLoader.import(specifier, parentURL, attributes); | @@ -208,12 +241,16 @@ async function importModuleDynamicallyCallback(referrerSymbol, specifier, attrib | 208 | 241 | // and fall back to the default loader. | 209 | 242 | if (referrerSymbol === vm_dynamic_import_main_context_default) { | 210 | 243 | emitExperimentalWarning('vm.USE_MAIN_CONTEXT_DEFAULT_LOADER'); | 211 | | - return defaultImportModuleDynamically(specifier, attributes, referrerName); | | 244 | + return defaultImportModuleDynamicallyForScript(specifier, attributes, referrerName); | 212 | 245 | } | 213 | 246 | // For script compiled internally that should use the default loader to handle dynamic | 214 | 247 | // import, proxy the request to the default loader without the warning. | 215 | 248 | if (referrerSymbol === vm_dynamic_import_default_internal) { | 216 | | - return defaultImportModuleDynamically(specifier, attributes, referrerName); | | 249 | + return defaultImportModuleDynamicallyForScript(specifier, attributes, referrerName); | | 250 | + } | | 251 | + // For SourceTextModules compiled internally, proxy the request to the default loader. | | 252 | + if (referrerSymbol === source_text_module_default_hdo) { | | 253 | + return defaultImportModuleDynamicallyForModule(specifier, attributes, referrerName); | 217 | 254 | } | 218 | 255 |
| 219 | 256 | if (moduleRegistries.has(referrerSymbol)) { | @@ -288,6 +325,29 @@ async function initializeHooks() { | 288 | 325 | return { __proto__: null, hooks, preloadScripts }; | 289 | 326 | } | 290 | 327 |
| | 328 | +/** | | 329 | + * Compile a SourceTextModule for the built-in ESM loader. Register it for default | | 330 | + * source map and import.meta and dynamic import() handling if cascadedLoader is provided. | | 331 | + * @param {string} url URL of the module. | | 332 | + * @param {string} source Source code of the module. | | 333 | + * @param {typeof import('./loader.js').ModuleLoader|undefined} cascadedLoader If provided, | | 334 | + * register the module for default handling. | | 335 | + * @returns {ModuleWrap} | | 336 | + */ | | 337 | +function compileSourceTextModule(url, source, cascadedLoader) { | | 338 | + const hostDefinedOption = cascadedLoader ? source_text_module_default_hdo : undefined; | | 339 | + const wrap = new ModuleWrap(url, undefined, source, 0, 0, hostDefinedOption); | | 340 | + | | 341 | + if (!cascadedLoader) { | | 342 | + return wrap; | | 343 | + } | | 344 | + // Cache the source map for the module if present. | | 345 | + if (wrap.sourceMapURL) { | | 346 | + maybeCacheSourceMap(url, source, null, false, undefined, wrap.sourceMapURL); | | 347 | + } | | 348 | + return wrap; | | 349 | +} | | 350 | + | 291 | 351 | module.exports = { | 292 | 352 | registerModule, | 293 | 353 | initializeESM, | @@ -296,4 +356,5 @@ module.exports = { | 296 | 356 | getConditionsSet, | 297 | 357 | loaderWorkerId: 'internal/modules/esm/worker', | 298 | 358 | forceDefaultLoader, | | 359 | + compileSourceTextModule, | 299 | 360 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/vm/module.js +5-6Lines changed: 5 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -131,19 +131,18 @@ class Module { | 131 | 131 | importModuleDynamicallyWrap(options.importModuleDynamically) : | 132 | 132 | undefined, | 133 | 133 | }; | | 134 | + // This will take precedence over the referrer as the object being | | 135 | + // passed into the callbacks. | | 136 | + registry.callbackReferrer = this; | | 137 | + const { registerModule } = require('internal/modules/esm/utils'); | | 138 | + registerModule(this[kWrap], registry); | 134 | 139 | } else { | 135 | 140 | assert(syntheticEvaluationSteps); | 136 | 141 | this[kWrap] = new ModuleWrap(identifier, context, | 137 | 142 | syntheticExportNames, | 138 | 143 | syntheticEvaluationSteps); | 139 | 144 | } | 140 | 145 |
| 141 | | - // This will take precedence over the referrer as the object being | 142 | | - // passed into the callbacks. | 143 | | - registry.callbackReferrer = this; | 144 | | - const { registerModule } = require('internal/modules/esm/utils'); | 145 | | - registerModule(this[kWrap], registry); | 146 | | - | 147 | 146 | this[kContext] = context; | 148 | 147 | } | 149 | 148 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/env_properties.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 45 | 45 | V(onpskexchange_symbol, "onpskexchange") \ | 46 | 46 | V(resource_symbol, "resource_symbol") \ | 47 | 47 | V(trigger_async_id_symbol, "trigger_async_id_symbol") \ | | 48 | + V(source_text_module_default_hdo, "source_text_module_default_hdo") \ | 48 | 49 | V(vm_dynamic_import_default_internal, "vm_dynamic_import_default_internal") \ | 49 | 50 | V(vm_dynamic_import_main_context_default, \ | 50 | 51 | "vm_dynamic_import_main_context_default") \ | |
Collapse file Copy file name to clipboardExpand all lines: src/module_wrap.cc +25-17Lines changed: 25 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -102,8 +102,10 @@ ModuleWrap* ModuleWrap::GetFromModule(Environment* env, | 102 | 102 | return nullptr; | 103 | 103 | } | 104 | 104 |
| 105 | | -// new ModuleWrap(url, context, source, lineOffset, columnOffset) | 106 | | -// new ModuleWrap(url, context, exportNames, syntheticExecutionFunction) | | 105 | +// new ModuleWrap(url, context, source, lineOffset, columnOffset, cachedData) | | 106 | +// new ModuleWrap(url, context, source, lineOffset, columOffset, | | 107 | +// hostDefinedOption) new ModuleWrap(url, context, exportNames, | | 108 | +// syntheticExecutionFunction) | 107 | 109 | void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | 108 | 110 | CHECK(args.IsConstructCall()); | 109 | 111 | CHECK_GE(args.Length(), 3); | @@ -132,22 +134,36 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | 132 | 134 | int column_offset = 0; | 133 | 135 |
| 134 | 136 | bool synthetic = args[2]->IsArray(); | | 137 | + | | 138 | + Local<PrimitiveArray> host_defined_options = | | 139 | + PrimitiveArray::New(isolate, HostDefinedOptions::kLength); | | 140 | + Local<Symbol> id_symbol; | 135 | 141 | if (synthetic) { | 136 | 142 | // new ModuleWrap(url, context, exportNames, syntheticExecutionFunction) | 137 | 143 | CHECK(args[3]->IsFunction()); | 138 | 144 | } else { | 139 | 145 | // new ModuleWrap(url, context, source, lineOffset, columOffset, cachedData) | | 146 | + // new ModuleWrap(url, context, source, lineOffset, columOffset, | | 147 | + // hostDefinedOption) | 140 | 148 | CHECK(args[2]->IsString()); | 141 | 149 | CHECK(args[3]->IsNumber()); | 142 | 150 | line_offset = args[3].As<Int32>()->Value(); | 143 | 151 | CHECK(args[4]->IsNumber()); | 144 | 152 | column_offset = args[4].As<Int32>()->Value(); | 145 | | - } | | 153 | + if (args[5]->IsSymbol()) { | | 154 | + id_symbol = args[5].As<Symbol>(); | | 155 | + } else { | | 156 | + id_symbol = Symbol::New(isolate, url); | | 157 | + } | | 158 | + host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol); | 146 | 159 |
| 147 | | - Local<PrimitiveArray> host_defined_options = | 148 | | - PrimitiveArray::New(isolate, HostDefinedOptions::kLength); | 149 | | - Local<Symbol> id_symbol = Symbol::New(isolate, url); | 150 | | - host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol); | | 160 | + if (that->SetPrivate(context, | | 161 | + realm->isolate_data()->host_defined_option_symbol(), | | 162 | + id_symbol) | | 163 | + .IsNothing()) { | | 164 | + return; | | 165 | + } | | 166 | + } | 151 | 167 |
| 152 | 168 | ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env()); | 153 | 169 | TryCatchScope try_catch(realm->env()); | @@ -173,8 +189,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | 173 | 189 | SyntheticModuleEvaluationStepsCallback); | 174 | 190 | } else { | 175 | 191 | ScriptCompiler::CachedData* cached_data = nullptr; | 176 | | - if (!args[5]->IsUndefined()) { | 177 | | - CHECK(args[5]->IsArrayBufferView()); | | 192 | + if (args[5]->IsArrayBufferView()) { | 178 | 193 | Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>(); | 179 | 194 | uint8_t* data = | 180 | 195 | static_cast<uint8_t*>(cached_data_buf->Buffer()->Data()); | @@ -237,13 +252,6 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | 237 | 252 | return; | 238 | 253 | } | 239 | 254 |
| 240 | | - if (that->SetPrivate(context, | 241 | | - realm->isolate_data()->host_defined_option_symbol(), | 242 | | - id_symbol) | 243 | | - .IsNothing()) { | 244 | | - return; | 245 | | - } | 246 | | - | 247 | 255 | // Use the extras object as an object whose GetCreationContext() will be the | 248 | 256 | // original `context`, since the `Context` itself strictly speaking cannot | 249 | 257 | // be stored in an internal field. | @@ -837,7 +845,7 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback( | 837 | 845 | return; | 838 | 846 | } | 839 | 847 | DCHECK(id->IsSymbol()); | 840 | | - Local<Value> args[] = {id, meta}; | | 848 | + Local<Value> args[] = {id, meta, wrap}; | 841 | 849 | TryCatchScope try_catch(env); | 842 | 850 | USE(callback->Call( | 843 | 851 | context, Undefined(realm->isolate()), arraysize(args), args)); | |
|