Skip to content

Commit 1b0d5f8

Browse files
authored
Promise.withResolvers() returned object had resolve and reject functions swapped (#1983)
1 parent bbbd8ac commit 1b0d5f8

File tree

2 files changed

+90
-43
lines changed

2 files changed

+90
-43
lines changed

Jint.Tests/Runtime/PromiseTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,56 @@ public void ManualPromise_HasCorrectStackTrace()
497497

498498
Assert.Equal("at <anonymous>:1:56", logMessage?.Trim());
499499
}
500+
501+
[Fact]
502+
public void WithResolvers_calling_resolve_resolves_promise()
503+
{
504+
// Arrange
505+
using var engine = new Engine();
506+
List<string> logMessages = new();
507+
engine.SetValue("log", logMessages.Add);
508+
509+
// Act
510+
engine.Execute("""
511+
const p = Promise.withResolvers();
512+
const next = p.promise
513+
.then(() => log('resolved'))
514+
.catch(() => log('rejected'));
515+
516+
log('start');
517+
p.resolve();
518+
log('end');
519+
""");
520+
engine.RunAvailableContinuations();
521+
522+
// Assert
523+
List<string> expected = new() { "start", "end", "resolved" };
524+
Assert.Equal(expected, logMessages);
525+
}
526+
527+
[Fact]
528+
public void WithResolvers_calling_reject_rejects_promise()
529+
{
530+
// Arrange
531+
using var engine = new Engine();
532+
List<string> logMessages = new();
533+
engine.SetValue("log", logMessages.Add);
534+
535+
// Act
536+
engine.Execute("""
537+
const p = Promise.withResolvers();
538+
const next = p.promise
539+
.then(() => log('resolved'))
540+
.catch(() => log('rejected'));
541+
542+
log('start');
543+
p.reject();
544+
log('end');
545+
""");
546+
engine.RunAvailableContinuations();
547+
548+
// Assert
549+
List<string> expected = new() { "start", "end", "rejected" };
550+
Assert.Equal(expected, logMessages);
551+
}
500552
}

Jint/Native/Promise/PromiseConstructor.cs

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ internal sealed record PromiseCapability(
1313
JsValue PromiseInstance,
1414
ICallable Resolve,
1515
ICallable Reject,
16-
JsValue RejectObj,
17-
JsValue ResolveObj
18-
);
16+
JsValue ResolveObj,
17+
JsValue RejectObj);
1918

2019
internal sealed class PromiseConstructor : Constructor
2120
{
@@ -139,11 +138,11 @@ private JsValue PromiseResolve(JsValue thisObject, JsValue x)
139138
}
140139
}
141140

142-
var (instance, resolve, _, _, _) = NewPromiseCapability(_engine, thisObject);
141+
var capability = NewPromiseCapability(_engine, thisObject);
143142

144-
resolve.Call(Undefined, new[] { x });
143+
capability.Resolve.Call(Undefined, new[] { x });
145144

146-
return instance;
145+
return capability.PromiseInstance;
147146
}
148147

149148
/// <summary>
@@ -163,11 +162,11 @@ private JsValue Reject(JsValue thisObject, JsValue[] arguments)
163162

164163
var r = arguments.At(0);
165164

166-
var (instance, _, reject, _, _) = NewPromiseCapability(_engine, thisObject);
165+
var capability = NewPromiseCapability(_engine, thisObject);
167166

168-
reject.Call(Undefined, new[] { r });
167+
capability.Reject.Call(Undefined, new[] { r });
169168

170-
return instance;
169+
return capability.PromiseInstance;
171170
}
172171

173172
/// <summary>
@@ -255,8 +254,6 @@ private JsValue All(JsValue thisObject, JsValue[] arguments)
255254
if (!TryGetPromiseCapabilityAndIterator(thisObject, arguments, "Promise.all", out var capability, out var promiseResolve, out var iterator))
256255
return capability.PromiseInstance;
257256

258-
var (resultingPromise, resolve, reject, _, rejectObj) = capability;
259-
260257
var results = new List<JsValue>();
261258
bool doneIterating = false;
262259

@@ -269,7 +266,7 @@ void ResolveIfFinished()
269266
if (results.TrueForAll(static x => x is not null) && doneIterating)
270267
{
271268
var array = _realm.Intrinsics.Array.ConstructFast(results);
272-
resolve.Call(Undefined, new JsValue[] { array });
269+
capability.Resolve.Call(Undefined, new JsValue[] { array });
273270
}
274271
}
275272

@@ -296,8 +293,8 @@ void ResolveIfFinished()
296293
}
297294
catch (JavaScriptException e)
298295
{
299-
reject.Call(Undefined, new[] { e.Error });
300-
return resultingPromise;
296+
capability.Reject.Call(Undefined, new[] { e.Error });
297+
return capability.PromiseInstance;
301298
}
302299

303300
// note that null here is important
@@ -325,7 +322,7 @@ void ResolveIfFinished()
325322
return Undefined;
326323
}, 1, PropertyFlag.Configurable);
327324

328-
thenFunc.Call(item, new JsValue[] { onSuccess, rejectObj });
325+
thenFunc.Call(item, new JsValue[] { onSuccess, capability.RejectObj });
329326
}
330327
else
331328
{
@@ -338,11 +335,11 @@ void ResolveIfFinished()
338335
catch (JavaScriptException e)
339336
{
340337
iterator.Close(CompletionType.Throw);
341-
reject.Call(Undefined, new[] { e.Error });
342-
return resultingPromise;
338+
capability.Reject.Call(Undefined, new[] { e.Error });
339+
return capability.PromiseInstance;
343340
}
344341

345-
return resultingPromise;
342+
return capability.PromiseInstance;
346343
}
347344

348345
// https://tc39.es/ecma262/#sec-promise.allsettled
@@ -351,8 +348,6 @@ private JsValue AllSettled(JsValue thisObject, JsValue[] arguments)
351348
if (!TryGetPromiseCapabilityAndIterator(thisObject, arguments, "Promise.allSettled", out var capability, out var promiseResolve, out var iterator))
352349
return capability.PromiseInstance;
353350

354-
var (resultingPromise, resolve, reject, _, rejectObj) = capability;
355-
356351
var results = new List<JsValue>();
357352
bool doneIterating = false;
358353

@@ -365,7 +360,7 @@ void ResolveIfFinished()
365360
if (results.TrueForAll(static x => x is not null) && doneIterating)
366361
{
367362
var array = _realm.Intrinsics.Array.ConstructFast(results);
368-
resolve.Call(Undefined, new JsValue[] { array });
363+
capability.Resolve.Call(Undefined, new JsValue[] { array });
369364
}
370365
}
371366

@@ -392,8 +387,8 @@ void ResolveIfFinished()
392387
}
393388
catch (JavaScriptException e)
394389
{
395-
reject.Call(Undefined, new[] { e.Error });
396-
return resultingPromise;
390+
capability.Reject.Call(Undefined, new[] { e.Error });
391+
return capability.PromiseInstance;
397392
}
398393

399394
// note that null here is important
@@ -456,11 +451,11 @@ void ResolveIfFinished()
456451
catch (JavaScriptException e)
457452
{
458453
iterator.Close(CompletionType.Throw);
459-
reject.Call(Undefined, new[] { e.Error });
460-
return resultingPromise;
454+
capability.Reject.Call(Undefined, new[] { e.Error });
455+
return capability.PromiseInstance;
461456
}
462457

463-
return resultingPromise;
458+
return capability.PromiseInstance;
464459
}
465460

466461
// https://tc39.es/ecma262/#sec-promise.any
@@ -471,8 +466,6 @@ private JsValue Any(JsValue thisObject, JsValue[] arguments)
471466
return capability.PromiseInstance;
472467
}
473468

474-
var (resultingPromise, resolve, reject, resolveObj, _) = capability;
475-
476469
var errors = new List<JsValue>();
477470
var doneIterating = false;
478471

@@ -487,7 +480,7 @@ void RejectIfAllRejected()
487480
{
488481
var array = _realm.Intrinsics.Array.ConstructFast(errors);
489482

490-
reject.Call(Undefined, new JsValue[] { Construct(_realm.Intrinsics.AggregateError, new JsValue[] { array }) });
483+
capability.Reject.Call(Undefined, new JsValue[] { Construct(_realm.Intrinsics.AggregateError, new JsValue[] { array }) });
491484
}
492485
}
493486

@@ -547,7 +540,7 @@ void RejectIfAllRejected()
547540
return Undefined;
548541
}, 1, PropertyFlag.Configurable);
549542

550-
thenFunc.Call(item, new JsValue[] { resolveObj, onError });
543+
thenFunc.Call(item, new JsValue[] { capability.ResolveObj, onError });
551544
}
552545
else
553546
{
@@ -560,11 +553,11 @@ void RejectIfAllRejected()
560553
catch (JavaScriptException e)
561554
{
562555
iterator.Close(CompletionType.Throw);
563-
reject.Call(Undefined, new[] { e.Error });
564-
return resultingPromise;
556+
capability.Reject.Call(Undefined, new[] { e.Error });
557+
return capability.PromiseInstance;
565558
}
566559

567-
return resultingPromise;
560+
return capability.PromiseInstance;
568561
}
569562

570563
// https://tc39.es/ecma262/#sec-promise.race
@@ -573,9 +566,6 @@ private JsValue Race(JsValue thisObject, JsValue[] arguments)
573566
if (!TryGetPromiseCapabilityAndIterator(thisObject, arguments, "Promise.race", out var capability, out var promiseResolve, out var iterator))
574567
return capability.PromiseInstance;
575568

576-
var (resultingPromise, resolve, reject, _, rejectObj) = capability;
577-
578-
579569
// 7. Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability, promiseResolve).
580570
// https://tc39.es/ecma262/#sec-performpromiserace
581571
try
@@ -594,16 +584,16 @@ private JsValue Race(JsValue thisObject, JsValue[] arguments)
594584
}
595585
catch (JavaScriptException e)
596586
{
597-
reject.Call(Undefined, new[] { e.Error });
598-
return resultingPromise;
587+
capability.Reject.Call(Undefined, new[] { e.Error });
588+
return capability.PromiseInstance;
599589
}
600590

601591
// h. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
602592
var nextPromise = promiseResolve.Call(thisObject, new JsValue[] { nextValue });
603593

604594
// i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).
605595

606-
_engine.Invoke(nextPromise, "then", new[] { (JsValue) resolve, rejectObj });
596+
_engine.Invoke(nextPromise, "then", new[] { (JsValue) capability.Resolve, capability.RejectObj });
607597
} while (true);
608598
}
609599
catch (JavaScriptException e)
@@ -612,13 +602,13 @@ private JsValue Race(JsValue thisObject, JsValue[] arguments)
612602
// a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
613603
// b. IfAbruptRejectPromise(result, promiseCapability).
614604
iterator.Close(CompletionType.Throw);
615-
reject.Call(Undefined, new[] { e.Error });
616-
return resultingPromise;
605+
capability.Reject.Call(Undefined, new[] { e.Error });
606+
return capability.PromiseInstance;
617607
}
618608

619609
// 9. Return Completion(result).
620610
// Note that PerformPromiseRace returns a Promise instance in success case
621-
return resultingPromise;
611+
return capability.PromiseInstance;
622612
}
623613

624614

@@ -715,6 +705,11 @@ JsValue Executor(JsValue thisObject, JsValue[] arguments)
715705
ExceptionHelper.ThrowTypeError(engine.Realm, "reject is not a function");
716706
}
717707

718-
return new PromiseCapability(instance, resolve, reject, resolveArg, rejectArg);
708+
return new PromiseCapability(
709+
PromiseInstance: instance,
710+
Resolve: resolve,
711+
Reject: reject,
712+
RejectObj: rejectArg,
713+
ResolveObj: resolveArg);
719714
}
720715
}

0 commit comments

Comments
 (0)