Skip to content

Commit 26df3fd

Browse files
sygCommit Bot
authored andcommitted
[Promise.allSettled] Fix [[AlreadyCalled]] checking in element closures
Bug: chromium:1105318 Change-Id: I7b1c57b7ff7beaaa53c19a270d5a8c36b11baf17 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2301082 Reviewed-by: Sathya Gunasekaran <[email protected]> Commit-Queue: Shu-yu Guo <[email protected]> Cr-Commit-Position: refs/heads/master@{#68903}
1 parent 8b338b2 commit 26df3fd

1 file changed

Lines changed: 26 additions & 8 deletions

File tree

src/builtins/promise-all-element-closure.tq

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ const kPropertyArrayHashFieldMax: constexpr int31
8282

8383
transitioning macro PromiseAllResolveElementClosure<F: type>(
8484
implicit context: Context)(
85-
value: JSAny, function: JSFunction, wrapResultFunctor: F): JSAny {
85+
value: JSAny, function: JSFunction, wrapResultFunctor: F,
86+
hasResolveAndRejectClosures: constexpr bool): JSAny {
8687
// We use the {function}s context as the marker to remember whether this
8788
// resolve element closure was already called. It points to the resolve
8889
// element context (which is a FunctionContext) until it was called the
@@ -98,10 +99,6 @@ transitioning macro PromiseAllResolveElementClosure<F: type>(
9899
const nativeContext = LoadNativeContext(context);
99100
function.context = nativeContext;
100101

101-
// Update the value depending on whether Promise.all or
102-
// Promise.allSettled is called.
103-
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
104-
105102
// Determine the index from the {function}.
106103
assert(kPropertyArrayNoHashSentinel == 0);
107104
const identityHash =
@@ -123,6 +120,27 @@ transitioning macro PromiseAllResolveElementClosure<F: type>(
123120
context.elements[PromiseAllResolveElementContextSlots::
124121
kPromiseAllResolveElementValuesSlot] = values;
125122
}
123+
124+
// Promise.allSettled, for each input element, has both a resolve and a reject
125+
// closure that share an [[AlreadyCalled]] boolean. That is, the input element
126+
// can only be settled once: after resolve is called, reject returns early,
127+
// and vice versa. Using {function}'s context as the marker only tracks
128+
// per-closure instead of per-element. When the second resolve/reject closure
129+
// is called on the same index, values.object[index] will already exist and
130+
// will not be the hole value. In that case, return early. Everything up to
131+
// this point is not yet observable to user code. This is not a problem for
132+
// Promise.all since Promise.all has a single resolve closure (no reject) per
133+
// element.
134+
if (hasResolveAndRejectClosures) {
135+
if (values.objects[index] != TheHole) deferred {
136+
return Undefined;
137+
}
138+
}
139+
140+
// Update the value depending on whether Promise.all or
141+
// Promise.allSettled is called.
142+
const updatedValue = wrapResultFunctor.Call(nativeContext, value);
143+
126144
values.objects[index] = updatedValue;
127145

128146
remainingElementsCount = remainingElementsCount - 1;
@@ -148,22 +166,22 @@ PromiseAllResolveElementClosure(
148166
js-implicit context: Context, receiver: JSAny,
149167
target: JSFunction)(value: JSAny): JSAny {
150168
return PromiseAllResolveElementClosure(
151-
value, target, PromiseAllWrapResultAsFulfilledFunctor{});
169+
value, target, PromiseAllWrapResultAsFulfilledFunctor{}, false);
152170
}
153171

154172
transitioning javascript builtin
155173
PromiseAllSettledResolveElementClosure(
156174
js-implicit context: Context, receiver: JSAny,
157175
target: JSFunction)(value: JSAny): JSAny {
158176
return PromiseAllResolveElementClosure(
159-
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{});
177+
value, target, PromiseAllSettledWrapResultAsFulfilledFunctor{}, true);
160178
}
161179

162180
transitioning javascript builtin
163181
PromiseAllSettledRejectElementClosure(
164182
js-implicit context: Context, receiver: JSAny,
165183
target: JSFunction)(value: JSAny): JSAny {
166184
return PromiseAllResolveElementClosure(
167-
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{});
185+
value, target, PromiseAllSettledWrapResultAsRejectedFunctor{}, true);
168186
}
169187
}

0 commit comments

Comments
 (0)