Skip to content

Commit aa657f0

Browse files
legendecastargos
authored andcommitted
test: split indirect eval import tests
Split indirect eval import tests as they depends on the JS stack to resolve the referrer. PR-URL: #58637 Reviewed-By: Jacob Smith <[email protected]> Reviewed-By: Luigi Pinca <[email protected]>
1 parent 76e3c8a commit aa657f0

File tree

4 files changed

+90
-30
lines changed

4 files changed

+90
-30
lines changed

test/es-module/test-esm-dynamic-import.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ function expectFsNamespace(result) {
3535
}
3636

3737
// For direct use of import expressions inside of CJS or ES modules, including
38-
// via eval, all kinds of specifiers should work without issue.
38+
// via direct/indirect eval, all kinds of specifiers should work without issue.
3939
(function testScriptOrModuleImport() {
40-
// Importing another file, both direct & via eval
40+
// Importing another file, both direct & via direct eval
4141
// expectOkNamespace(import(relativePath));
4242
expectOkNamespace(eval(`import("${relativePath}")`));
4343
expectOkNamespace(eval(`import("${relativePath}")`));
4444
expectOkNamespace(eval(`import(${JSON.stringify(targetURL)})`));
4545

46-
// Importing a built-in, both direct & via eval
46+
// Importing a built-in, both direct & via direct eval
4747
expectFsNamespace(import('fs'));
4848
expectFsNamespace(eval('import("fs")'));
4949
expectFsNamespace(eval('import("fs")'));
@@ -70,6 +70,8 @@ function expectFsNamespace(result) {
7070
// be treated as a file: URL.
7171
expectOkNamespace(import(targetURL.pathname));
7272

73+
// Import with an indirect eval. In this case, the referrer is null and
74+
// defaults to the realm record.
7375
// If the referrer is a realm record, there is no way to resolve the
7476
// specifier.
7577
// TODO(legendecas): https://github.com/tc39/ecma262/pull/3195
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
/**
3+
* This test verifies that dynamic import in an indirect eval without JS stacks.
4+
* In this case, the referrer for the dynamic import will be null and the main
5+
* context default loader in createContext() resolves to cwd.
6+
*
7+
* Caveat: this test can be unstable if the loader internals are changed and performs
8+
* microtasks with a JS stack (e.g. with CallbackScope). In this case, the
9+
* referrer will be resolved to the top JS stack frame `node:internal/process/task_queues.js`.
10+
* This is due to the implementation detail of how V8 finds the referrer for a dynamic import
11+
* call.
12+
*/
13+
14+
const common = require('../common');
15+
16+
// Can't process.chdir() in worker.
17+
const { isMainThread } = require('worker_threads');
18+
19+
if (!isMainThread) {
20+
common.skip('This test only works on a main thread');
21+
}
22+
23+
const tmpdir = require('../common/tmpdir');
24+
const fixtures = require('../common/fixtures');
25+
const fs = require('node:fs');
26+
const {
27+
Script,
28+
createContext,
29+
constants: { USE_MAIN_CONTEXT_DEFAULT_LOADER },
30+
} = require('node:vm');
31+
const assert = require('node:assert');
32+
33+
common.expectWarning('ExperimentalWarning',
34+
'vm.USE_MAIN_CONTEXT_DEFAULT_LOADER is an experimental feature and might change at any time');
35+
assert(
36+
!process.execArgv.includes('--experimental-vm-modules'),
37+
'This test must be run without --experimental-vm-modules');
38+
assert.strictEqual(typeof USE_MAIN_CONTEXT_DEFAULT_LOADER, 'symbol');
39+
40+
async function main() {
41+
tmpdir.refresh();
42+
process.chdir(tmpdir.path);
43+
44+
//
45+
{
46+
const options = {
47+
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
48+
};
49+
const ctx = createContext({}, options);
50+
const s = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
51+
importModuleDynamically: common.mustNotCall(),
52+
});
53+
await assert.rejects(s.runInContext(ctx), { code: 'ERR_MODULE_NOT_FOUND' });
54+
}
55+
56+
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
57+
fs.copyFileSync(moduleUrl, tmpdir.resolve('message.mjs'));
58+
{
59+
const options = {
60+
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
61+
};
62+
const ctx = createContext({}, options);
63+
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
64+
const namespace = await import(moduleUrl.href);
65+
const script = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
66+
importModuleDynamically: common.mustNotCall(),
67+
});
68+
const result = await script.runInContext(ctx);
69+
assert.deepStrictEqual(result, namespace);
70+
}
71+
}
72+
73+
main().catch(common.mustNotCall());

test/es-module/test-vm-main-context-default-loader.js

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ const fs = require('fs');
1616
const {
1717
compileFunction,
1818
Script,
19-
createContext,
2019
constants: { USE_MAIN_CONTEXT_DEFAULT_LOADER },
2120
} = require('vm');
2221
const assert = require('assert');
@@ -112,34 +111,8 @@ async function main() {
112111
await testNotFoundErrors(undefinedOptions);
113112
await testNotFoundErrors(nonPathOptions);
114113

115-
// createContext() with null referrer also resolves to cwd.
116-
{
117-
const options = {
118-
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
119-
};
120-
const ctx = createContext({}, options);
121-
const s = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
122-
importModuleDynamically: common.mustNotCall(),
123-
});
124-
await assert.rejects(s.runInContext(ctx), { code: 'ERR_MODULE_NOT_FOUND' });
125-
}
126-
127114
await testLoader(undefinedOptions);
128115
await testLoader(nonPathOptions);
129-
130-
{
131-
const options = {
132-
importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER,
133-
};
134-
const ctx = createContext({}, options);
135-
const moduleUrl = fixtures.fileURL('es-modules', 'message.mjs');
136-
const namespace = await import(moduleUrl.href);
137-
const script = new Script('Promise.resolve("import(\'./message.mjs\')").then(eval)', {
138-
importModuleDynamically: common.mustNotCall(),
139-
});
140-
const result = await script.runInContext(ctx);
141-
assert.deepStrictEqual(result, namespace);
142-
}
143116
}
144117
}
145118

test/parallel/test-vm-module-referrer-realm.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ import * as common from '../common/index.mjs';
33
import assert from 'node:assert';
44
import { Script, SourceTextModule, createContext } from 'node:vm';
55

6+
/**
7+
* This test verifies that dynamic import in an indirect eval without JS stacks.
8+
* In this case, the referrer for the dynamic import will be null and the
9+
* per-context importModuleDynamically callback will be invoked.
10+
*
11+
* Caveat: this test can be unstable if the loader internals are changed and performs
12+
* microtasks with a JS stack (e.g. with CallbackScope). In this case, the
13+
* referrer will be resolved to the top JS stack frame `node:internal/process/task_queues.js`.
14+
* This is due to the implementation detail of how V8 finds the referrer for a dynamic import
15+
* call.
16+
*/
17+
618
async function test() {
719
const foo = new SourceTextModule('export const a = 1;');
820
await foo.link(common.mustNotCall());

0 commit comments

Comments
 (0)