| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ce8435b commit 44ce971
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -759,8 +759,48 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) { | |||
| 759 | 759 | MaybeLocal<Value> result; | |
| 760 | 760 | auto run = [&]() { | |
| 761 | 761 | 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 | + | ||
| 763 | 802 | microtask_queue->PerformCheckpoint(isolate); | |
| 803 | + } | ||
| 764 | 804 | return result; | |
| 765 | 805 | }; | |
| 766 | 806 | 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