◐ Shell
clean mode source ↗

module: use symbol in WeakMap to manage host defined options · nodejs/node@a86a2e1

@@ -7,6 +7,11 @@ const {

77

ObjectFreeze,

88

} = primordials;

9910+

const {

11+

privateSymbols: {

12+

host_defined_option_symbol,

13+

},

14+

} = internalBinding('util');

1015

const {

1116

ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,

1217

ERR_INVALID_ARG_VALUE,

@@ -21,16 +26,8 @@ const {

2126

setImportModuleDynamicallyCallback,

2227

setInitializeImportMetaObjectCallback,

2328

} = internalBinding('module_wrap');

24-

const {

25-

getModuleFromWrap,

26-

} = require('internal/vm/module');

2729

const assert = require('internal/assert');

283029-

const callbackMap = new SafeWeakMap();

30-

function setCallbackForWrap(wrap, data) {

31-

callbackMap.set(wrap, data);

32-

}

33-3431

let defaultConditions;

3532

/**

3633

* Returns the default conditions for ES module loading.

@@ -83,34 +80,86 @@ function getConditionsSet(conditions) {

8380

return getDefaultConditionsSet();

8481

}

858283+

/**

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+86136

/**

87137

* 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.

89139

* @param {Record<string, string | Function>} meta - The import.meta object to initialize.

90140

*/

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);

94144

if (initializeImportMeta !== undefined) {

95-

meta = initializeImportMeta(meta, getModuleFromWrap(wrap) || wrap);

145+

meta = initializeImportMeta(meta, callbackReferrer);

96146

}

97147

}

98148

}

99149100150

/**

101151

* 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.

103153

* @param {string} specifier - The module specifier string.

104154

* @param {Record<string, string>} attributes - The import attributes object.

105155

* @returns {Promise<import('internal/modules/esm/loader.js').ModuleExports>} - The imported module object.

106156

* @throws {ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING} - If the callback function is missing.

107157

*/

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);

111161

if (importModuleDynamically !== undefined) {

112-

return importModuleDynamically(

113-

specifier, getModuleFromWrap(wrap) || wrap, attributes);

162+

return importModuleDynamically(specifier, callbackReferrer, attributes);

114163

}

115164

}

116165

throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();

@@ -176,7 +225,7 @@ async function initializeHooks() {

176225

}

177226178227

module.exports = {

179-

setCallbackForWrap,

228+

registerModule,

180229

initializeESM,

181230

initializeHooks,

182231

getDefaultConditions,