Skip to content

Commit 6e586a1

Browse files
legendecastargos
authored andcommitted
vm: expose hasTopLevelAwait on SourceTextModule
Expose `hasTopLevelAwait` and `hasAsyncGraph` on `vm.SourceTextModule`. `hasAsyncGraph` requires the module to be instantiated first. PR-URL: #59865 Fixes: #59656 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
1 parent b0c1e67 commit 6e586a1

File tree

4 files changed

+169
-0
lines changed

4 files changed

+169
-0
lines changed

doc/api/vm.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,36 @@ to disallow any changes to it.
920920
Corresponds to the `[[RequestedModules]]` field of [Cyclic Module Record][]s in
921921
the ECMAScript specification.
922922
923+
### `sourceTextModule.hasAsyncGraph()`
924+
925+
<!-- YAML
926+
added: REPLACEME
927+
-->
928+
929+
* Returns: {boolean}
930+
931+
Iterates over the dependency graph and returns `true` if any module in its
932+
dependencies or this module itself contains top-level `await` expressions,
933+
otherwise returns `false`.
934+
935+
The search may be slow if the graph is big enough.
936+
937+
This requires the module to be instantiated first. If the module is not
938+
instantiated yet, an error will be thrown.
939+
940+
### `sourceTextModule.hasTopLevelAwait()`
941+
942+
<!-- YAML
943+
added: REPLACEME
944+
-->
945+
946+
* Returns: {boolean}
947+
948+
Returns whether the module itself contains any top-level `await` expressions.
949+
950+
This corresponds to the field `[[HasTLA]]` in [Cyclic Module Record][] in the
951+
ECMAScript specification.
952+
923953
### `sourceTextModule.instantiate()`
924954
925955
<!-- YAML

lib/internal/vm/module.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,19 @@ class SourceTextModule extends Module {
429429
return super.error;
430430
}
431431

432+
hasAsyncGraph() {
433+
validateThisInternalField(this, kWrap, 'SourceTextModule');
434+
if (this[kWrap].getStatus() < kInstantiated) {
435+
throw new ERR_VM_MODULE_STATUS('must be instantiated');
436+
}
437+
return this[kWrap].hasAsyncGraph;
438+
}
439+
440+
hasTopLevelAwait() {
441+
validateThisInternalField(this, kWrap, 'SourceTextModule');
442+
return this[kWrap].hasTopLevelAwait;
443+
}
444+
432445
createCachedData() {
433446
const { status } = this;
434447
if (status === 'evaluating' ||
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
// Flags: --experimental-vm-modules
4+
5+
require('../common');
6+
7+
const assert = require('assert');
8+
9+
const { SourceTextModule } = require('vm');
10+
const test = require('node:test');
11+
12+
test('module is not instantiated yet', () => {
13+
const foo = new SourceTextModule(`
14+
export const foo = 4
15+
export default 5;
16+
`);
17+
assert.throws(() => foo.hasAsyncGraph(), {
18+
code: 'ERR_VM_MODULE_STATUS',
19+
});
20+
});
21+
22+
test('simple module with top-level await', () => {
23+
const foo = new SourceTextModule(`
24+
export const foo = 4
25+
export default 5;
26+
27+
await 0;
28+
`);
29+
foo.linkRequests([]);
30+
foo.instantiate();
31+
32+
assert.strictEqual(foo.hasAsyncGraph(), true);
33+
});
34+
35+
test('simple module with non top-level await', () => {
36+
const foo = new SourceTextModule(`
37+
export const foo = 4
38+
export default 5;
39+
40+
export async function f() {
41+
await 0;
42+
}
43+
`);
44+
foo.linkRequests([]);
45+
foo.instantiate();
46+
47+
assert.strictEqual(foo.hasAsyncGraph(), false);
48+
});
49+
50+
test('module with a dependency containing top-level await', () => {
51+
const foo = new SourceTextModule(`
52+
export const foo = 4
53+
export default 5;
54+
55+
await 0;
56+
`);
57+
foo.linkRequests([]);
58+
59+
const bar = new SourceTextModule(`
60+
export { foo } from 'foo';
61+
`);
62+
bar.linkRequests([foo]);
63+
bar.instantiate();
64+
65+
assert.strictEqual(foo.hasAsyncGraph(), true);
66+
assert.strictEqual(bar.hasAsyncGraph(), true);
67+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict';
2+
3+
// Flags: --experimental-vm-modules
4+
5+
require('../common');
6+
7+
const assert = require('assert');
8+
9+
const { SourceTextModule } = require('vm');
10+
const test = require('node:test');
11+
12+
test('simple module', () => {
13+
const foo = new SourceTextModule(`
14+
export const foo = 4
15+
export default 5;
16+
`);
17+
assert.strictEqual(foo.hasTopLevelAwait(), false);
18+
});
19+
20+
test('simple module with top-level await', () => {
21+
const foo = new SourceTextModule(`
22+
export const foo = 4
23+
export default 5;
24+
25+
await 0;
26+
`);
27+
assert.strictEqual(foo.hasTopLevelAwait(), true);
28+
});
29+
30+
test('simple module with non top-level await', () => {
31+
const foo = new SourceTextModule(`
32+
export const foo = 4
33+
export default 5;
34+
35+
export async function f() {
36+
await 0;
37+
}
38+
`);
39+
assert.strictEqual(foo.hasTopLevelAwait(), false);
40+
});
41+
42+
test('module with a dependency containing top-level await', () => {
43+
const foo = new SourceTextModule(`
44+
export const foo = 4
45+
export default 5;
46+
47+
await 0;
48+
`);
49+
foo.linkRequests([]);
50+
51+
const bar = new SourceTextModule(`
52+
export { foo } from 'foo';
53+
`);
54+
bar.linkRequests([foo]);
55+
bar.instantiate();
56+
57+
assert.strictEqual(foo.hasTopLevelAwait(), true);
58+
assert.strictEqual(bar.hasTopLevelAwait(), false);
59+
});

0 commit comments

Comments
 (0)