vm: "afterEvaluate", evaluate() return a promise from the outer context · nodejs/node@44ce971

@@ -759,8 +759,48 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {

759759

MaybeLocal<Value> result;

760760

auto run = [&]() {

761761

MaybeLocal<Value> result = module->Evaluate(context);

762-

if (!result.IsEmpty() && microtask_queue)

762+763+

Local<Value> res;

764+

if (result.ToLocal(&res) && microtask_queue) {

765+

DCHECK(res->IsPromise());

766+767+

// To address https://github.com/nodejs/node/issues/59541 when the

768+

// module has its own separate microtask queue in microtaskMode

769+

// "afterEvaluate", we avoid returning a promise built inside the

770+

// module's own context.

771+

//

772+

// Instead, we build a promise in the outer context, which we resolve

773+

// with {result}, then we checkpoint the module's own queue, and finally

774+

// we return the outer-context promise.

775+

//

776+

// If we simply returned the inner promise {result} directly, per

777+

// https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob, the outer

778+

// context, when resolving a promise coming from a different context,

779+

// would need to enqueue a task (known as a thenable job task) onto the

780+

// queue of that different context (the module's context). But this queue

781+

// will normally not be checkpointed after evaluate() returns.

782+

//

783+

// This means that the execution flow in the outer context would

784+

// silently fall through at the statement (in lib/internal/vm/module.js):

785+

// await this[kWrap].evaluate(timeout, breakOnSigint)

786+

//

787+

// This is true for any promises created inside the module's context

788+

// and made available to the outer context, as the node:vm doc explains.

789+

//

790+

// We must handle this particular return value differently to make it

791+

// possible to await on the result of evaluate().

792+

Local<Context> outer_context = isolate->GetCurrentContext();

793+

Local<Promise::Resolver> resolver;

794+

if (!Promise::Resolver::New(outer_context).ToLocal(&resolver)) {

795+

return MaybeLocal<Value>();

796+

}

797+

if (resolver->Resolve(outer_context, res).IsNothing()) {

798+

return MaybeLocal<Value>();

799+

}

800+

result = resolver->GetPromise();

801+763802

microtask_queue->PerformCheckpoint(isolate);

803+

}

764804

return result;

765805

};

766806

if (break_on_sigint && timeout != -1) {