| // Copyright 2019 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. |
| |
| #include 'src/builtins/builtins-promise-gen.h' |
| |
| namespace promise { |
| |
| // https://tc39.es/ecma262/#sec-promise.race |
| transitioning javascript builtin |
| PromiseRace( |
| js-implicit context: Context, receiver: JSAny)(iterable: JSAny): JSAny { |
| const receiver = Cast<JSReceiver>(receiver) |
| otherwise ThrowTypeError(MessageTemplate::kCalledOnNonObject, 'Promise.race'); |
| |
| const nativeContext = LoadNativeContext(context); |
| |
| // Let promiseCapability be ? NewPromiseCapability(C). |
| // Don't fire debugEvent so that forwarding the rejection through all does |
| // not trigger redundant ExceptionEvents |
| const capability = NewPromiseCapability(receiver, False); |
| const resolve = capability.resolve; |
| const reject = capability.reject; |
| const promise = capability.promise; |
| |
| // NewPromiseCapability guarantees that receiver is Constructor. |
| assert(Is<Constructor>(receiver)); |
| const constructor = UnsafeCast<Constructor>(receiver); |
| |
| // For catch prediction, don't treat the .then calls as handling it; |
| // instead, recurse outwards. |
| if (IsDebugActive()) deferred { |
| SetPropertyStrict(context, reject, kPromiseForwardingHandlerSymbol, True); |
| } |
| |
| try { |
| let promiseResolveFunction: JSAny; |
| let i: iterator::IteratorRecord; |
| try { |
| // Let promiseResolve be GetPromiseResolve(C). |
| // IfAbruptRejectPromise(promiseResolve, promiseCapability). |
| promiseResolveFunction = GetPromiseResolve(nativeContext, constructor); |
| |
| // Let iterator be GetIterator(iterable). |
| // IfAbruptRejectPromise(iterator, promiseCapability). |
| i = iterator::GetIterator(iterable); |
| } catch (e) deferred { |
| goto Reject(e); |
| } |
| |
| // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). |
| try { |
| const fastIteratorResultMap = *NativeContextSlot( |
| nativeContext, ContextSlot::ITERATOR_RESULT_MAP_INDEX); |
| while (true) { |
| let nextValue: JSAny; |
| try { |
| // Let next be IteratorStep(iteratorRecord.[[Iterator]]). |
| // If next is an abrupt completion, set iteratorRecord.[[Done]] to |
| // true. ReturnIfAbrupt(next). |
| const next: JSReceiver = iterator::IteratorStep( |
| i, fastIteratorResultMap) otherwise return promise; |
| |
| // Let nextValue be IteratorValue(next). |
| // If nextValue is an abrupt completion, set iteratorRecord.[[Done]] |
| // to true. |
| // ReturnIfAbrupt(nextValue). |
| nextValue = iterator::IteratorValue(next, fastIteratorResultMap); |
| } catch (e) { |
| goto Reject(e); |
| } |
| // Let nextPromise be ? Call(constructor, _promiseResolve_, « |
| // nextValue »). |
| const nextPromise = |
| CallResolve(constructor, promiseResolveFunction, nextValue); |
| |
| // Perform ? Invoke(nextPromise, "then", « resolveElement, |
| // resultCapability.[[Reject]] »). |
| const then = GetProperty(nextPromise, kThenString); |
| const thenResult = Call( |
| context, then, nextPromise, UnsafeCast<JSAny>(resolve), |
| UnsafeCast<JSAny>(reject)); |
| |
| // For catch prediction, mark that rejections here are semantically |
| // handled by the combined Promise. |
| if (IsDebugActive() && !Is<JSPromise>(promise)) deferred { |
| SetPropertyStrict( |
| context, thenResult, kPromiseHandledBySymbol, promise); |
| } |
| } |
| } catch (e) deferred { |
| iterator::IteratorCloseOnException(i); |
| goto Reject(e); |
| } |
| } label Reject(exception: Object) deferred { |
| Call( |
| context, UnsafeCast<JSAny>(reject), Undefined, |
| UnsafeCast<JSAny>(exception)); |
| return promise; |
| } |
| unreachable; |
| } |
| } |