[Promise.any] Fix: if "then" rejects immediately, do the right thing

In this case, we'll already have values in "errors" in PerformPromiseAny
step 8.d.

Bug: v8:9808
Change-Id: I5bb0cba41887f4bbdab3bb15e8f52dd94acec9c9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2204277
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67884}
This commit is contained in:
Marja Hölttä 2020-05-19 09:05:25 +02:00 committed by Commit Bot
parent 73bf6079a0
commit e90c5ddb02
3 changed files with 50 additions and 24 deletions

View File

@ -48,7 +48,7 @@ class PromiseBuiltins {
kPromiseAnyRejectElementCapabilitySlot,
// errors array from Promise.any
kPromiseAnyRejectElementErrorsArraySlot,
kPromiseAnyRejectElementErrorsSlot,
kPromiseAnyRejectElementLength
};

View File

@ -9,7 +9,7 @@ extern enum PromiseAnyRejectElementContextSlots extends int31
constexpr 'PromiseBuiltins::PromiseAnyRejectElementContextSlots' {
kPromiseAnyRejectElementRemainingSlot,
kPromiseAnyRejectElementCapabilitySlot,
kPromiseAnyRejectElementErrorsArraySlot,
kPromiseAnyRejectElementErrorsSlot,
kPromiseAnyRejectElementLength
}
@ -35,9 +35,8 @@ transitioning macro CreatePromiseAnyRejectElementContext(
kPromiseAnyRejectElementRemainingSlot] = SmiConstant(1);
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementCapabilitySlot] = capability;
// Will be set when handling the first rejection.
rejectContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] = Undefined;
kPromiseAnyRejectElementErrorsSlot] = kEmptyFixedArray;
return rejectContext;
}
@ -91,6 +90,11 @@ PromiseAnyRejectElementClosure(
assert(identityHash > 0);
const index = identityHash - 1;
// 6. Let errors be F.[[Errors]].
let errors =
UnsafeCast<FixedArray>(context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsSlot]);
// 7. Let promiseCapability be F.[[Capability]].
// 8. Let remainingElementsCount be F.[[RemainingElements]].
@ -98,23 +102,14 @@ PromiseAnyRejectElementClosure(
UnsafeCast<Smi>(context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementRemainingSlot]);
// 6. Let errors be F.[[Errors]].
let errorsArray: FixedArray;
if (context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] == Undefined) {
// This is the first rejection function to be called.
errorsArray =
AllocateZeroedFixedArray(Convert<intptr>(remainingElementsCount));
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot] = errorsArray;
} else {
errorsArray = UnsafeCast<FixedArray>(
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsArraySlot]);
}
// 9. Set errors[index] to x.
errorsArray.objects[index] = value;
const newCapacity = IntPtrMax(SmiUntag(remainingElementsCount), index + 1);
if (newCapacity > errors.length_intptr) deferred {
errors = ExtractFixedArray(errors, 0, errors.length_intptr, newCapacity);
context[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsSlot] = errors;
}
errors.objects[index] = value;
// 10. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
@ -127,7 +122,7 @@ PromiseAnyRejectElementClosure(
// a. Let error be a newly created AggregateError object.
// b. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(errorsArray);
const error = ConstructAggregateError(errors);
// c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
const capability = UnsafeCast<PromiseCapability>(
context[PromiseAnyRejectElementContextSlots::
@ -298,7 +293,14 @@ Reject(Object) {
if (remainingElementsCount == 0) deferred {
// 1. Let error be a newly created AggregateError object.
// 2. Set error.[[AggregateErrors]] to errors.
const error = ConstructAggregateError(kEmptyFixedArray);
// We may already have elements in "errors" - this happens when the
// Thenable calls the reject callback immediately.
const errors = UnsafeCast<FixedArray>(
rejectElementContext[PromiseAnyRejectElementContextSlots::
kPromiseAnyRejectElementErrorsSlot]);
const error = ConstructAggregateError(errors);
// 3. Return ThrowCompletion(error).
goto Reject(error);
}
@ -359,10 +361,10 @@ PromiseAny(
}
transitioning macro ConstructAggregateError(implicit context: Context)(
errorsArray: FixedArray): JSObject {
errors: FixedArray): JSObject {
const obj: JSAggregateError = error::ConstructInternalAggregateErrorHelper(
context, SmiConstant(MessageTemplate::kAllPromisesRejected));
obj.errors = errorsArray;
obj.errors = errors;
return obj;
}

View File

@ -92,3 +92,27 @@ load('test/mjsunit/test-async.js');
})();
});
})();
// Test that we return a proper array even if (custom) "then" invokes the
// reject callbacks right away.
(function() {
class MyPromise extends Promise {
constructor(executor, id) {
super(executor);
this.id = id;
}
then(resolve, reject) {
if (this.id) return reject(this.id);
return super.then(resolve, reject)
}
};
const a = new MyPromise(() => {}, 'a');
const b = new MyPromise(() => {}, 'b');
testAsync(assert => {
assert.plan(1);
MyPromise.any([a, b]).then(
assert.unreachable,
(e) => { assert.equals(['a', 'b'], e.errors) });
});
})();