Commit b772d31
committed
vm: "afterEvaluate", make module.evaluate() return a promise from the outer context
Consider the default context A with a microtask queue QA, and a
context B with its own microtask queue QB.
Context B is constructed with vm.createContext(..., {microtaskMode:
"afterEvaluate"}). The evaluation in context B can be performed via
vm.Script or vm.SourceTextModule.
The standard (https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob)
dictates that, when resolving a {promise} with {resolution}, from any
context, the {then} method on {promise} should be called within a task
enqueued on the microtask queue from the context associated with {then}.
Specifically, after evaluating a script or module in context B, any
promises created within B, if later resolved within A, will result in a
task to be enqueued back onto QB, even long after we are done evaluating
any code within B.
This creates a challenge for users of node:vm in "afterEvaluate" mode.
In ContextifyScript::EvalMachine() and in ModuleWrap::Evaluate(), we
only drain the microtask queue QB a single time after running the script
or evaluating the module. After that point, the queue will not be
drained unless another script or module is evaluated in the same
context.
In the following scenario, prior to this patch, the log statement will
not be printed:
const microtaskMode = "afterEvaluate";
const context = vm.createContext({}, {microtaskMode});
const source = "";
const module = new vm.SourceTextModule(source, {context});
await module.link(() => null);
await module.evaluate();
console.log("NOT PRINTED");
To resolve the promise returned by evaluate(), which technically is the
top-level capability for {module}, a promise created within B, V8 will
enqueue a task on QB. But since this is after the PerformCheckpoint()
call in ModuleWrap::Evaluate(), the task in QB is never run. In the
meantime, since QA is empty, the Node process simply exits (with a
warning about the unsettled promise, if it happened to be a top-level
await).
While being unable to do `await module.evaluate()` is clearly a problem,
more generally, it is intended that in "afterEvaluate" mode, promises
created in the inner context cannot make progress if, and until, the
microtask queue of the inner context is checkpointed.
Therefore, to address this issue, the fix is narrow:
When the module has its own microtask queue, i.e. in "afterEvaluate"
mode, the inner-context promise returned by
v8::SourceTextModule::Evaluate() is first resolved to an outer-context
promise, then we checkpoint the microtask queue of the inner context,
then we return the outer-context promise we just built.
This ensures that in the statement `await module.evaluate()`, the
promise returned by `evaluate()` can be resolved within the outer
context, without involving the microtask queue in the inner context.
Fixes: #59541
Refs: https://issues.chromium.org/issues/441679231
Refs: https://groups.google.com/g/v8-dev/c/YIeRg8CUNS8/m/rEQdFuNZAAAJ1 parent f30edab commit b772d31
File tree
2 files changed
+44
-5
lines changed- src
- test/parallel
2 files changed
+44
-5
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
735 | 735 | | |
736 | 736 | | |
737 | 737 | | |
738 | | - | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
739 | 778 | | |
| 779 | + | |
740 | 780 | | |
741 | 781 | | |
742 | 782 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | | - | |
33 | | - | |
| 31 | + | |
| 32 | + | |
34 | 33 | | |
0 commit comments