Skip to content

Commit fe66e9d

Browse files
joyeecheungrichardlau
authored andcommitted
vm: reject in importModuleDynamically without --experimental-vm-modules
Users cannot access any API that can be used to return a module or module namespace in this callback without --experimental-vm-modules anyway, so this would eventually lead to a rejection. This patch rejects in this case with our own error message and use a constant host-defined option for the rejection, so that scripts with the same source can still be compiled using the compilation cache if no `import()` is actually called in the script. PR-URL: #50137 Backport-PR-URL: #51004 Refs: #35375 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 052e095 commit fe66e9d

File tree

7 files changed

+81
-7
lines changed

7 files changed

+81
-7
lines changed

doc/api/errors.md

+6
Original file line numberDiff line numberDiff line change
@@ -2985,6 +2985,12 @@ An attempt was made to use something that was already closed.
29852985
While using the Performance Timing API (`perf_hooks`), no valid performance
29862986
entry types are found.
29872987

2988+
<a id="ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG"></a>
2989+
2990+
### `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`
2991+
2992+
A dynamic import callback was invoked without `--experimental-vm-modules`.
2993+
29882994
<a id="ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING"></a>
29892995

29902996
### `ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`

doc/api/vm.md

+19-5
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ changes:
9898
when `import()` is called. If this option is not specified, calls to
9999
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
100100
This option is part of the experimental modules API. We do not recommend
101-
using it in a production environment.
101+
using it in a production environment. If `--experimental-vm-modules` isn't
102+
set, this callback will be ignored and calls to `import()` will reject with
103+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
102104
* `specifier` {string} specifier passed to `import()`
103105
* `script` {vm.Script}
104106
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -765,6 +767,9 @@ changes:
765767
* `importModuleDynamically` {Function} Called during evaluation of this module
766768
when `import()` is called. If this option is not specified, calls to
767769
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
770+
If `--experimental-vm-modules` isn't set, this callback will be ignored
771+
and calls to `import()` will reject with
772+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
768773
* `specifier` {string} specifier passed to `import()`
769774
* `module` {vm.Module}
770775
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -1022,7 +1027,9 @@ changes:
10221027
when `import()` is called. If this option is not specified, calls to
10231028
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
10241029
This option is part of the experimental modules API, and should not be
1025-
considered stable.
1030+
considered stable. If `--experimental-vm-modules` isn't
1031+
set, this callback will be ignored and calls to `import()` will reject with
1032+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
10261033
* `specifier` {string} specifier passed to `import()`
10271034
* `function` {Function}
10281035
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -1246,7 +1253,9 @@ changes:
12461253
when `import()` is called. If this option is not specified, calls to
12471254
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
12481255
This option is part of the experimental modules API. We do not recommend
1249-
using it in a production environment.
1256+
using it in a production environment. If `--experimental-vm-modules` isn't
1257+
set, this callback will be ignored and calls to `import()` will reject with
1258+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
12501259
* `specifier` {string} specifier passed to `import()`
12511260
* `script` {vm.Script}
12521261
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -1345,7 +1354,9 @@ changes:
13451354
when `import()` is called. If this option is not specified, calls to
13461355
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
13471356
This option is part of the experimental modules API. We do not recommend
1348-
using it in a production environment.
1357+
using it in a production environment. If `--experimental-vm-modules` isn't
1358+
set, this callback will be ignored and calls to `import()` will reject with
1359+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
13491360
* `specifier` {string} specifier passed to `import()`
13501361
* `script` {vm.Script}
13511362
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -1425,7 +1436,9 @@ changes:
14251436
when `import()` is called. If this option is not specified, calls to
14261437
`import()` will reject with [`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`][].
14271438
This option is part of the experimental modules API. We do not recommend
1428-
using it in a production environment.
1439+
using it in a production environment. If `--experimental-vm-modules` isn't
1440+
set, this callback will be ignored and calls to `import()` will reject with
1441+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`][].
14291442
* `specifier` {string} specifier passed to `import()`
14301443
* `script` {vm.Script}
14311444
* `importAttributes` {Object} The `"assert"` value passed to the
@@ -1589,6 +1602,7 @@ are not controllable through the timeout either.
15891602
[Source Text Module Record]: https://tc39.es/ecma262/#sec-source-text-module-records
15901603
[Synthetic Module Record]: https://heycam.github.io/webidl/#synthetic-module-records
15911604
[V8 Embedder's Guide]: https://v8.dev/docs/embed#contexts
1605+
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG`]: errors.md#err_vm_dynamic_import_callback_missing_flag
15921606
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`]: errors.md#err_vm_dynamic_import_callback_missing
15931607
[`ERR_VM_MODULE_STATUS`]: errors.md#err_vm_module_status
15941608
[`Error`]: errors.md#class-error

lib/internal/errors.js

+3
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,9 @@ E('ERR_VALID_PERFORMANCE_ENTRY_TYPE',
17161716
'At least one valid performance entry type is required', Error);
17171717
E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING',
17181718
'A dynamic import callback was not specified.', TypeError);
1719+
E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG',
1720+
'A dynamic import callback was invoked without --experimental-vm-modules',
1721+
TypeError);
17191722
E('ERR_VM_MODULE_ALREADY_LINKED', 'Module has already been linked', Error);
17201723
E('ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA',
17211724
'Cached data cannot be created for a module which has been evaluated', Error);

lib/internal/modules/esm/utils.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ const {
1414
} = internalBinding('util');
1515
const {
1616
default_host_defined_options,
17+
vm_dynamic_import_missing_flag,
1718
} = internalBinding('symbols');
1819

1920
const {
21+
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG,
2022
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING,
2123
ERR_INVALID_ARG_VALUE,
2224
} = require('internal/errors').codes;
@@ -132,7 +134,8 @@ const moduleRegistries = new SafeWeakMap();
132134
*/
133135
function registerModule(referrer, registry) {
134136
const idSymbol = referrer[host_defined_option_symbol];
135-
if (idSymbol === default_host_defined_options) {
137+
if (idSymbol === default_host_defined_options ||
138+
idSymbol === vm_dynamic_import_missing_flag) {
136139
// The referrer is compiled without custom callbacks, so there is
137140
// no registry to hold on to. We'll throw
138141
// ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING when a callback is
@@ -173,6 +176,9 @@ async function importModuleDynamicallyCallback(symbol, specifier, attributes) {
173176
return importModuleDynamically(specifier, callbackReferrer, attributes);
174177
}
175178
}
179+
if (symbol === vm_dynamic_import_missing_flag) {
180+
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG();
181+
}
176182
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
177183
}
178184

lib/internal/vm.js

+16
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ const {
1515
} = ContextifyScript.prototype;
1616
const {
1717
default_host_defined_options,
18+
vm_dynamic_import_missing_flag,
1819
} = internalBinding('symbols');
1920
const {
2021
validateFunction,
2122
validateObject,
2223
} = require('internal/validators');
2324

25+
const {
26+
getOptionValue,
27+
} = require('internal/options');
28+
29+
2430
function isContext(object) {
2531
validateObject(object, 'object', { __proto__: null, allowArray: true });
2632

@@ -40,6 +46,16 @@ function getHostDefinedOptionId(importModuleDynamically, filename) {
4046
// compilation cache can be hit.
4147
return default_host_defined_options;
4248
}
49+
// We should've thrown here immediately when we introduced
50+
// --experimental-vm-modules and importModuleDynamically, but since
51+
// users are already using this callback to throw a similar error,
52+
// we also defer the error to the time when an actual import() is called
53+
// to avoid breaking them. To ensure that the isolate compilation
54+
// cache can still be hit, use a constant sentinel symbol here.
55+
if (!getOptionValue('--experimental-vm-modules')) {
56+
return vm_dynamic_import_missing_flag;
57+
}
58+
4359
return Symbol(filename);
4460
}
4561

src/env_properties.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
V(owner_symbol, "owner_symbol") \
4545
V(onpskexchange_symbol, "onpskexchange") \
4646
V(resource_symbol, "resource_symbol") \
47-
V(trigger_async_id_symbol, "trigger_async_id_symbol")
47+
V(trigger_async_id_symbol, "trigger_async_id_symbol") \
48+
V(vm_dynamic_import_missing_flag, "vm_dynamic_import_missing_flag")
4849

4950
// Strings are per-isolate primitives but Environment proxies them
5051
// for the sake of convenience. Strings should be ASCII-only.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const { Script, compileFunction } = require('vm');
5+
const assert = require('assert');
6+
7+
assert(
8+
!process.execArgv.includes('--experimental-vm-modules'),
9+
'This test must be run without --experimental-vm-modules');
10+
11+
assert.rejects(async () => {
12+
const script = new Script('import("fs")', {
13+
importModuleDynamically: common.mustNotCall(),
14+
});
15+
const imported = script.runInThisContext();
16+
await imported;
17+
}, {
18+
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG'
19+
}).then(common.mustCall());
20+
21+
assert.rejects(async () => {
22+
const imported = compileFunction('return import("fs")', [], {
23+
importModuleDynamically: common.mustNotCall(),
24+
})();
25+
await imported;
26+
}, {
27+
code: 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG'
28+
}).then(common.mustCall());

0 commit comments

Comments
 (0)