GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — vm: "afterEvaluate", evaluate() return a promise from the outer context · nodejs/node@312b33a

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • src
    • module_wrap.cc
  • test/parallel
    • test-vm-module-after-evaluate.js
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/module_wrap.cc‎

Copy file name to clipboardExpand all lines: src/module_wrap.cc
+41-1Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,48 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
630630
MaybeLocal<Value> result;
631631
auto run = [&]() {
632632
MaybeLocal<Value> result = module->Evaluate(context);
633-
if (!result.IsEmpty() && microtask_queue)
633+
634+
Local<Value> res;
635+
if (result.ToLocal(&res) && microtask_queue) {
636+
DCHECK(res->IsPromise());
637+
638+
// To address https://github.com/nodejs/node/issues/59541 when the
639+
// module has its own separate microtask queue in microtaskMode
640+
// "afterEvaluate", we avoid returning a promise built inside the
641+
// module's own context.
642+
//
643+
// Instead, we build a promise in the outer context, which we resolve
644+
// with {result}, then we checkpoint the module's own queue, and finally
645+
// we return the outer-context promise.
646+
//
647+
// If we simply returned the inner promise {result} directly, per
648+
// https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob, the outer
649+
// context, when resolving a promise coming from a different context,
650+
// would need to enqueue a task (known as a thenable job task) onto the
651+
// queue of that different context (the module's context). But this queue
652+
// will normally not be checkpointed after evaluate() returns.
653+
//
654+
// This means that the execution flow in the outer context would
655+
// silently fall through at the statement (in lib/internal/vm/module.js):
656+
// await this[kWrap].evaluate(timeout, breakOnSigint)
657+
//
658+
// This is true for any promises created inside the module's context
659+
// and made available to the outer context, as the node:vm doc explains.
660+
//
661+
// We must handle this particular return value differently to make it
662+
// possible to await on the result of evaluate().
663+
Local<Context> outer_context = isolate->GetCurrentContext();
664+
Local<Promise::Resolver> resolver;
665+
if (!Promise::Resolver::New(outer_context).ToLocal(&resolver)) {
666+
return MaybeLocal<Value>();
667+
}
668+
if (resolver->Resolve(outer_context, res).IsNothing()) {
669+
return MaybeLocal<Value>();
670+
}
671+
result = resolver->GetPromise();
672+
634673
microtask_queue->PerformCheckpoint(isolate);
674+
}
635675
return result;
636676
};
637677
if (break_on_sigint && timeout != -1) {
Collapse file

‎test/parallel/test-vm-module-after-evaluate.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-vm-module-after-evaluate.js
+4-5Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const microtaskMode = 'afterEvaluate';
1414

1515
(async () => {
1616
const mustNotCall1 = common.mustNotCall();
17-
const mustNotCall2 = common.mustNotCall();
17+
const mustCall1 = common.mustCall();
1818

1919
const inner = {};
2020

@@ -28,7 +28,6 @@ const microtaskMode = 'afterEvaluate';
2828
await module.link(mustNotCall1);
2929
await module.evaluate();
3030

31-
// This is Issue 59541, the next statement is not executed, of course
32-
// it should be.
33-
mustNotCall2();
34-
})().then(common.mustNotCall());
31+
// Prior to the fix for Issue 59541, the next statement was never executed.
32+
mustCall1();
33+
})().then(common.mustCall());

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.