GeezerVison is showing github.com — module: use symbol in WeakMap to manage host defined options · nodejs/node@a86a2e1
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 +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -69,8 +69,9 @@ import.meta.done(); | 69 | 69 | if (imports.length) { | 70 | 70 | reflect.imports = { __proto__: null }; | 71 | 71 | } | 72 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | 73 | | - setCallbackForWrap(m, { | | 72 | + const { registerModule } = require('internal/modules/esm/utils'); | | 73 | + registerModule(m, { | | 74 | + __proto__: null, | 74 | 75 | initializeImportMeta: (meta, wrap) => { | 75 | 76 | meta.exports = reflect.exports; | 76 | 77 | if (reflect.imports) { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/loader.js +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -210,9 +210,10 @@ class ModuleLoader { | 210 | 210 | ) { | 211 | 211 | const evalInstance = (url) => { | 212 | 212 | const { ModuleWrap } = internalBinding('module_wrap'); | 213 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | | 213 | + const { registerModule } = require('internal/modules/esm/utils'); | 214 | 214 | const module = new ModuleWrap(url, undefined, source, 0, 0); | 215 | | - setCallbackForWrap(module, { | | 215 | + registerModule(module, { | | 216 | + __proto__: null, | 216 | 217 | initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), | 217 | 218 | importModuleDynamically: (specifier, { url }, importAttributes) => { | 218 | 219 | return this.import(specifier, url, importAttributes); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -150,8 +150,9 @@ translators.set('module', async function moduleStrategy(url, source, isMain) { | 150 | 150 | maybeCacheSourceMap(url, source); | 151 | 151 | debug(`Translating StandardModule ${url}`); | 152 | 152 | const module = new ModuleWrap(url, undefined, source, 0, 0); | 153 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | 154 | | - setCallbackForWrap(module, { | | 153 | + const { registerModule } = require('internal/modules/esm/utils'); | | 154 | + registerModule(module, { | | 155 | + __proto__: null, | 155 | 156 | initializeImportMeta: (meta, wrap) => this.importMetaInitialize(meta, { url }), | 156 | 157 | importModuleDynamically, | 157 | 158 | }); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/utils.js +69-20Lines changed: 69 additions & 20 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 7 | 7 | ObjectFreeze, | 8 | 8 | } = primordials; | 9 | 9 |
| | 10 | +const { | | 11 | + privateSymbols: { | | 12 | + host_defined_option_symbol, | | 13 | + }, | | 14 | +} = internalBinding('util'); | 10 | 15 | const { | 11 | 16 | ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, | 12 | 17 | ERR_INVALID_ARG_VALUE, | @@ -21,16 +26,8 @@ const { | 21 | 26 | setImportModuleDynamicallyCallback, | 22 | 27 | setInitializeImportMetaObjectCallback, | 23 | 28 | } = internalBinding('module_wrap'); | 24 | | -const { | 25 | | - getModuleFromWrap, | 26 | | -} = require('internal/vm/module'); | 27 | 29 | const assert = require('internal/assert'); | 28 | 30 |
| 29 | | -const callbackMap = new SafeWeakMap(); | 30 | | -function setCallbackForWrap(wrap, data) { | 31 | | - callbackMap.set(wrap, data); | 32 | | -} | 33 | | - | 34 | 31 | let defaultConditions; | 35 | 32 | /** | 36 | 33 | * Returns the default conditions for ES module loading. | @@ -83,34 +80,86 @@ function getConditionsSet(conditions) { | 83 | 80 | return getDefaultConditionsSet(); | 84 | 81 | } | 85 | 82 |
| | 83 | +/** | | 84 | + * @callback ImportModuleDynamicallyCallback | | 85 | + * @param {string} specifier | | 86 | + * @param {ModuleWrap|ContextifyScript|Function|vm.Module} callbackReferrer | | 87 | + * @param {object} attributes | | 88 | + * @returns { Promise<void> } | | 89 | + */ | | 90 | + | | 91 | +/** | | 92 | + * @callback InitializeImportMetaCallback | | 93 | + * @param {object} meta | | 94 | + * @param {ModuleWrap|ContextifyScript|Function|vm.Module} callbackReferrer | | 95 | + */ | | 96 | + | | 97 | +/** | | 98 | + * @typedef {{ | | 99 | + * callbackReferrer: ModuleWrap|ContextifyScript|Function|vm.Module | | 100 | + * initializeImportMeta? : InitializeImportMetaCallback, | | 101 | + * importModuleDynamically? : ImportModuleDynamicallyCallback | | 102 | + * }} ModuleRegistry | | 103 | + */ | | 104 | + | | 105 | +/** | | 106 | + * @type {WeakMap<symbol, ModuleRegistry>} | | 107 | + */ | | 108 | +const moduleRegistries = new SafeWeakMap(); | | 109 | + | | 110 | +/** | | 111 | + * V8 would make sure that as long as import() can still be initiated from | | 112 | + * the referrer, the symbol referenced by |host_defined_option_symbol| should | | 113 | + * be alive, which in term would keep the settings object alive through the | | 114 | + * WeakMap, and in turn that keeps the referrer object alive, which would be | | 115 | + * passed into the callbacks. | | 116 | + * The reference goes like this: | | 117 | + * [v8::internal::Script] (via host defined options) ----1--> [idSymbol] | | 118 | + * [callbackReferrer] (via host_defined_option_symbol) ------2------^ | | | 119 | + * ^----------3---- (via WeakMap)------ | | 120 | + * 1+3 makes sure that as long as import() can still be initiated, the | | 121 | + * referrer wrap is still around and can be passed into the callbacks. | | 122 | + * 2 is only there so that we can get the id symbol to configure the | | 123 | + * weak map. | | 124 | + * @param {ModuleWrap|ContextifyScript|Function} referrer The referrer to | | 125 | + * get the id symbol from. This is different from callbackReferrer which | | 126 | + * could be set by the caller. | | 127 | + * @param {ModuleRegistry} registry | | 128 | + */ | | 129 | +function registerModule(referrer, registry) { | | 130 | + const idSymbol = referrer[host_defined_option_symbol]; | | 131 | + // To prevent it from being GC'ed. | | 132 | + registry.callbackReferrer ??= referrer; | | 133 | + moduleRegistries.set(idSymbol, registry); | | 134 | +} | | 135 | + | 86 | 136 | /** | 87 | 137 | * Defines the `import.meta` object for a given module. | 88 | | - * @param {object} wrap - Reference to the module. | | 138 | + * @param {symbol} symbol - Reference to the module. | 89 | 139 | * @param {Record<string, string | Function>} meta - The import.meta object to initialize. | 90 | 140 | */ | 91 | | -function initializeImportMetaObject(wrap, meta) { | 92 | | - if (callbackMap.has(wrap)) { | 93 | | - const { initializeImportMeta } = callbackMap.get(wrap); | | 141 | +function initializeImportMetaObject(symbol, meta) { | | 142 | + if (moduleRegistries.has(symbol)) { | | 143 | + const { initializeImportMeta, callbackReferrer } = moduleRegistries.get(symbol); | 94 | 144 | if (initializeImportMeta !== undefined) { | 95 | | - meta = initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap); | | 145 | + meta = initializeImportMeta(meta, callbackReferrer); | 96 | 146 | } | 97 | 147 | } | 98 | 148 | } | 99 | 149 |
| 100 | 150 | /** | 101 | 151 | * Asynchronously imports a module dynamically using a callback function. The native callback. | 102 | | - * @param {object} wrap - Reference to the module. | | 152 | + * @param {symbol} symbol - Reference to the module. | 103 | 153 | * @param {string} specifier - The module specifier string. | 104 | 154 | * @param {Record<string, string>} attributes - The import attributes object. | 105 | 155 | * @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} - The imported module object. | 106 | 156 | * @throws {ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING} - If the callback function is missing. | 107 | 157 | */ | 108 | | -async function importModuleDynamicallyCallback(wrap, specifier, attributes) { | 109 | | - if (callbackMap.has(wrap)) { | 110 | | - const { importModuleDynamically } = callbackMap.get(wrap); | | 158 | +async function importModuleDynamicallyCallback(symbol, specifier, attributes) { | | 159 | + if (moduleRegistries.has(symbol)) { | | 160 | + const { importModuleDynamically, callbackReferrer } = moduleRegistries.get(symbol); | 111 | 161 | if (importModuleDynamically !== undefined) { | 112 | | - return importModuleDynamically( | 113 | | - specifier, getModuleFromWrap(wrap) || wrap, attributes); | | 162 | + return importModuleDynamically(specifier, callbackReferrer, attributes); | 114 | 163 | } | 115 | 164 | } | 116 | 165 | throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING(); | @@ -176,7 +225,7 @@ async function initializeHooks() { | 176 | 225 | } | 177 | 226 |
| 178 | 227 | module.exports = { | 179 | | - setCallbackForWrap, | | 228 | + registerModule, | 180 | 229 | initializeESM, | 181 | 230 | initializeHooks, | 182 | 231 | getDefaultConditions, | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/vm.js +4-3Lines changed: 4 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -100,9 +100,10 @@ function internalCompileFunction(code, params, options) { | 100 | 100 | const { importModuleDynamicallyWrap } = require('internal/vm/module'); | 101 | 101 | const wrapped = importModuleDynamicallyWrap(importModuleDynamically); | 102 | 102 | const func = result.function; | 103 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | 104 | | - setCallbackForWrap(result.cacheKey, { | 105 | | - importModuleDynamically: (s, _k, i) => wrapped(s, func, i), | | 103 | + const { registerModule } = require('internal/modules/esm/utils'); | | 104 | + registerModule(func, { | | 105 | + __proto__: null, | | 106 | + importModuleDynamically: wrapped, | 106 | 107 | }); | 107 | 108 | } | 108 | 109 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/vm/module.js +9-7Lines changed: 9 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -12,7 +12,6 @@ const { | 12 | 12 | ObjectSetPrototypeOf, | 13 | 13 | ReflectApply, | 14 | 14 | SafePromiseAllReturnVoid, | 15 | | - SafeWeakMap, | 16 | 15 | Symbol, | 17 | 16 | SymbolToStringTag, | 18 | 17 | TypeError, | @@ -70,7 +69,6 @@ const STATUS_MAP = { | 70 | 69 |
| 71 | 70 | let globalModuleId = 0; | 72 | 71 | const defaultModuleName = 'vm:module'; | 73 | | -const wrapToModuleMap = new SafeWeakMap(); | 74 | 72 |
| 75 | 73 | const kWrap = Symbol('kWrap'); | 76 | 74 | const kContext = Symbol('kContext'); | @@ -121,25 +119,30 @@ class Module { | 121 | 119 | }); | 122 | 120 | } | 123 | 121 |
| | 122 | + let registry = { __proto__: null }; | 124 | 123 | if (sourceText !== undefined) { | 125 | 124 | this[kWrap] = new ModuleWrap(identifier, context, sourceText, | 126 | 125 | options.lineOffset, options.columnOffset, | 127 | 126 | options.cachedData); | 128 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | 129 | | - setCallbackForWrap(this[kWrap], { | | 127 | + registry = { | | 128 | + __proto__: null, | 130 | 129 | initializeImportMeta: options.initializeImportMeta, | 131 | 130 | importModuleDynamically: options.importModuleDynamically ? | 132 | 131 | importModuleDynamicallyWrap(options.importModuleDynamically) : | 133 | 132 | undefined, | 134 | | - }); | | 133 | + }; | 135 | 134 | } else { | 136 | 135 | assert(syntheticEvaluationSteps); | 137 | 136 | this[kWrap] = new ModuleWrap(identifier, context, | 138 | 137 | syntheticExportNames, | 139 | 138 | syntheticEvaluationSteps); | 140 | 139 | } | 141 | 140 |
| 142 | | - wrapToModuleMap.set(this[kWrap], this); | | 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); | 143 | 146 |
| 144 | 147 | this[kContext] = context; | 145 | 148 | } | @@ -446,5 +449,4 @@ module.exports = { | 446 | 449 | SourceTextModule, | 447 | 450 | SyntheticModule, | 448 | 451 | importModuleDynamicallyWrap, | 449 | | - getModuleFromWrap: (wrap) => wrapToModuleMap.get(wrap), | 450 | 452 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/vm.js +3-2Lines changed: 3 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -106,8 +106,9 @@ class Script extends ContextifyScript { | 106 | 106 | validateFunction(importModuleDynamically, | 107 | 107 | 'options.importModuleDynamically'); | 108 | 108 | const { importModuleDynamicallyWrap } = require('internal/vm/module'); | 109 | | - const { setCallbackForWrap } = require('internal/modules/esm/utils'); | 110 | | - setCallbackForWrap(this, { | | 109 | + const { registerModule } = require('internal/modules/esm/utils'); | | 110 | + registerModule(this, { | | 111 | + __proto__: null, | 111 | 112 | importModuleDynamically: | 112 | 113 | importModuleDynamicallyWrap(importModuleDynamically), | 113 | 114 | }); | |
Collapse file Copy file name to clipboardExpand all lines: src/env-inl.h -10Lines changed: 0 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -345,16 +345,6 @@ inline AliasedInt32Array& Environment::stream_base_state() { | 345 | 345 | return stream_base_state_; | 346 | 346 | } | 347 | 347 |
| 348 | | -inline uint32_t Environment::get_next_module_id() { | 349 | | - return module_id_counter_++; | 350 | | -} | 351 | | -inline uint32_t Environment::get_next_script_id() { | 352 | | - return script_id_counter_++; | 353 | | -} | 354 | | -inline uint32_t Environment::get_next_function_id() { | 355 | | - return function_id_counter_++; | 356 | | -} | 357 | | - | 358 | 348 | ShouldNotAbortOnUncaughtScope::ShouldNotAbortOnUncaughtScope( | 359 | 349 | Environment* env) | 360 | 350 | : env_(env) { | |
Collapse file Copy file name to clipboardExpand all lines: src/env.h -8Lines changed: 0 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -698,14 +698,6 @@ class Environment : public MemoryRetainer { | 698 | 698 | builtins::BuiltinLoader* builtin_loader(); | 699 | 699 |
| 700 | 700 | std::unordered_multimap<int, loader::ModuleWrap*> hash_to_module_map; | 701 | | - std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map; | 702 | | - std::unordered_map<uint32_t, contextify::ContextifyScript*> | 703 | | - id_to_script_map; | 704 | | - std::unordered_map<uint32_t, contextify::CompiledFnEntry*> id_to_function_map; | 705 | | - | 706 | | - inline uint32_t get_next_module_id(); | 707 | | - inline uint32_t get_next_script_id(); | 708 | | - inline uint32_t get_next_function_id(); | 709 | 701 |
| 710 | 702 | EnabledDebugList* enabled_debug_list() { return &enabled_debug_list_; } | 711 | 703 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/env_properties.h +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
| 21 | 21 | V(arrow_message_private_symbol, "node:arrowMessage") \ | 22 | 22 | V(contextify_context_private_symbol, "node:contextify:context") \ | 23 | 23 | V(decorated_private_symbol, "node:decorated") \ | | 24 | + V(host_defined_option_symbol, "node:host_defined_option_symbol") \ | 24 | 25 | V(napi_type_tag, "node:napi:type_tag") \ | 25 | 26 | V(napi_wrapper, "node:napi:wrapper") \ | 26 | 27 | V(untransferable_object_private_symbol, "node:untransferableObject") \ | | 337 | 338 | V(blocklist_constructor_template, v8::FunctionTemplate) \ | 338 | 339 | V(contextify_global_template, v8::ObjectTemplate) \ | 339 | 340 | V(contextify_wrapper_template, v8::ObjectTemplate) \ | 340 | | - V(compiled_fn_entry_template, v8::ObjectTemplate) \ | 341 | 341 | V(crypto_key_object_handle_constructor, v8::FunctionTemplate) \ | 342 | 342 | V(env_proxy_template, v8::ObjectTemplate) \ | 343 | 343 | V(env_proxy_ctor_template, v8::FunctionTemplate) \ | |
|