| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ca1942c commit 312b33a
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -630,8 +630,48 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) { | |||
| 630 | 630 | MaybeLocal<Value> result; | |
| 631 | 631 | auto run = [&]() { | |
| 632 | 632 | 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 | + | ||
| 634 | 673 | microtask_queue->PerformCheckpoint(isolate); | |
| 674 | + } | ||
| 635 | 675 | return result; | |
| 636 | 676 | }; | |
| 637 | 677 | if (break_on_sigint && timeout != -1) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,7 +14,7 @@ const microtaskMode = 'afterEvaluate'; | |||
| 14 | 14 | ||
| 15 | 15 | (async () => { | |
| 16 | 16 | const mustNotCall1 = common.mustNotCall(); | |
| 17 | - const mustNotCall2 = common.mustNotCall(); | ||
| 17 | + const mustCall1 = common.mustCall(); | ||
| 18 | 18 | ||
| 19 | 19 | const inner = {}; | |
| 20 | 20 | ||
@@ -28,7 +28,6 @@ const microtaskMode = 'afterEvaluate'; | |||
| 28 | 28 | await module.link(mustNotCall1); | |
| 29 | 29 | await module.evaluate(); | |
| 30 | 30 | ||
| 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()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments