Commit b369e89f authored by Marja Hölttä's avatar Marja Hölttä Committed by Commit Bot

[Promise.all] Fix: call IteratorClose if Promise.resolve is not callable

PerformPromiseAll doesn't set iteratorRecord.[[Done]] to true if
Promise.resolve is not callable. This makes Promise.all call
IteratorClose.

BUG=v8:10452

Change-Id: Icbe17416a733f68ef09f1c610d715f544c2a3b8a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2164789Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67416}
parent af45cf6d
...@@ -142,20 +142,18 @@ namespace promise { ...@@ -142,20 +142,18 @@ namespace promise {
// as that guards the lookup path for the "resolve" property on the // as that guards the lookup path for the "resolve" property on the
// Promise constructor. // Promise constructor.
let promiseResolveFunction: JSAny = Undefined; let promiseResolveFunction: JSAny = Undefined;
try {
try { try {
if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) { if (!IsPromiseResolveLookupChainIntact(nativeContext, constructor)) {
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
let promiseResolve: JSAny; let promiseResolve: JSAny;
try {
// 5. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`).
promiseResolve = GetProperty(constructor, kResolveString); promiseResolve = GetProperty(constructor, kResolveString);
} catch (e) deferred {
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
}
// 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError* // 6. If IsCallable(_promiseResolve_) is *false*, throw a *TypeError*
// exception. // exception.
promiseResolveFunction = Cast<Callable>(promiseResolve) promiseResolveFunction =
otherwise ThrowTypeError( Cast<Callable>(promiseResolve) otherwise ThrowTypeError(
MessageTemplate::kCalledNonCallable, 'resolve'); MessageTemplate::kCalledNonCallable, 'resolve');
} }
...@@ -163,7 +161,7 @@ namespace promise { ...@@ -163,7 +161,7 @@ namespace promise {
nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]); nativeContext[NativeContextSlot::ITERATOR_RESULT_MAP_INDEX]);
while (true) { while (true) {
let nextValue: JSAny; let nextValue: JSAny;
try {
// Let next be IteratorStep(iteratorRecord.[[Iterator]]). // Let next be IteratorStep(iteratorRecord.[[Iterator]]).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to // If next is an abrupt completion, set iteratorRecord.[[Done]] to
// true. ReturnIfAbrupt(next). // true. ReturnIfAbrupt(next).
...@@ -175,28 +173,28 @@ namespace promise { ...@@ -175,28 +173,28 @@ namespace promise {
// to true. // to true.
// ReturnIfAbrupt(nextValue). // ReturnIfAbrupt(nextValue).
nextValue = iterator::IteratorValue(next, fastIteratorResultMap); nextValue = iterator::IteratorValue(next, fastIteratorResultMap);
} catch (e) {
goto Reject(e);
}
// Check if we reached the limit. // Check if we reached the limit.
if (index == kPropertyArrayHashFieldMax) { if (index == kPropertyArrayHashFieldMax) {
// If there are too many elements (currently more than 2**21-1), // If there are too many elements (currently more than 2**21-1),
// raise a RangeError here (which is caught directly and turned into // raise a RangeError here (which is caught below and turned into
// a rejection) of the resulting promise. We could gracefully handle // a rejection of the resulting promise). We could gracefully handle
// this case as well and support more than this number of elements // this case as well and support more than this number of elements
// by going to a separate function and pass the larger indices via a // by going to a separate function and pass the larger indices via a
// separate context, but it doesn't seem likely that we need this, // separate context, but it doesn't seem likely that we need this,
// and it's unclear how the rest of the system deals with 2**21 live // and it's unclear how the rest of the system deals with 2**21 live
// Promises anyways. // Promises anyway.
try {
ThrowRangeError(MessageTemplate::kTooManyElementsInPromiseAll); ThrowRangeError(MessageTemplate::kTooManyElementsInPromiseAll);
} catch (e) deferred {
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
}
} }
// Set remainingElementsCount.[[Value]] to // Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] + 1. // remainingElementsCount.[[Value]] + 1.
const remainingElementsCount = UnsafeCast<Smi>( const remainingElementsCount =
resolveElementContext[PromiseAllResolveElementContextSlots:: UnsafeCast<Smi>(resolveElementContext
[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot]); kPromiseAllResolveElementRemainingSlot]);
resolveElementContext[PromiseAllResolveElementContextSlots:: resolveElementContext[PromiseAllResolveElementContextSlots::
kPromiseAllResolveElementRemainingSlot] = kPromiseAllResolveElementRemainingSlot] =
...@@ -242,7 +240,6 @@ namespace promise { ...@@ -242,7 +240,6 @@ namespace promise {
IsPromiseSpeciesProtectorCellInvalid() || Is<Smi>(nextValue) || IsPromiseSpeciesProtectorCellInvalid() || Is<Smi>(nextValue) ||
!IsPromiseThenLookupChainIntact( !IsPromiseThenLookupChainIntact(
nativeContext, UnsafeCast<HeapObject>(nextValue).map)) { nativeContext, UnsafeCast<HeapObject>(nextValue).map)) {
try {
// Let nextPromise be ? Call(constructor, _promiseResolve_, « // Let nextPromise be ? Call(constructor, _promiseResolve_, «
// nextValue »). // nextValue »).
const nextPromise = CallResolve( const nextPromise = CallResolve(
...@@ -262,9 +259,6 @@ namespace promise { ...@@ -262,9 +259,6 @@ namespace promise {
SetPropertyStrict( SetPropertyStrict(
context, thenResult, kPromiseHandledBySymbol, promise); context, thenResult, kPromiseHandledBySymbol, promise);
} }
} catch (e) deferred {
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
}
} else { } else {
PerformPromiseThenImpl( PerformPromiseThenImpl(
UnsafeCast<JSPromise>(nextValue), resolveElementFun, UnsafeCast<JSPromise>(nextValue), resolveElementFun,
...@@ -274,6 +268,9 @@ namespace promise { ...@@ -274,6 +268,9 @@ namespace promise {
// Set index to index + 1. // Set index to index + 1.
index += 1; index += 1;
} }
} catch (e) deferred {
iterator::IteratorCloseOnException(iter, e) otherwise Reject;
}
} }
label Done {} label Done {}
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
load('test/mjsunit/test-async.js');
// Promise.all should call IteratorClose if Promise.resolve is not callable.
let returnCount = 0;
let iter = {
[Symbol.iterator]() {
return {
return() {
returnCount++;
}
};
}
};
Promise.resolve = "certainly not callable";
testAsync(assert => {
assert.plan(2);
Promise.all(iter).then(assert.unreachable, reason => {
assert.equals(true, reason instanceof TypeError);
assert.equals(1, returnCount);
});
});
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
load('test/mjsunit/test-async.js');
// Promise.allSettled should call IteratorClose if Promise.resolve is not callable.
let returnCount = 0;
let iter = {
[Symbol.iterator]() {
return {
return() {
returnCount++;
}
};
}
};
Promise.resolve = "certainly not callable";
testAsync(assert => {
assert.plan(2);
Promise.allSettled(iter).then(assert.unreachable, reason => {
assert.equals(true, reason instanceof TypeError);
assert.equals(1, returnCount);
});
});
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
load('test/mjsunit/test-async.js');
// Promise.race should call IteratorClose if Promise.resolve is not callable.
let returnCount = 0;
let iter = {
[Symbol.iterator]() {
return {
return() {
returnCount++;
}
};
}
};
Promise.resolve = "certainly not callable";
testAsync(assert => {
assert.plan(2);
Promise.race(iter).then(assert.unreachable, reason => {
assert.equals(true, reason instanceof TypeError);
assert.equals(1, returnCount);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment