| // 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 { |
| |
| extern macro PromiseForwardingHandlerSymbolConstant(): Symbol; |
| const kPromiseForwardingHandlerSymbol: Symbol = |
| PromiseForwardingHandlerSymbolConstant(); |
| extern macro PromiseHandledBySymbolConstant(): Symbol; |
| const kPromiseHandledBySymbol: Symbol = PromiseHandledBySymbolConstant(); |
| extern macro ResolveStringConstant(): String; |
| const kResolveString: String = ResolveStringConstant(); |
| extern macro SetPropertyStrict(Context, Object, Object, Object): Object; |
| extern macro IsPromiseResolveProtectorCellInvalid(): bool; |
| |
| macro IsPromiseResolveLookupChainIntact(implicit context: Context)( |
| nativeContext: NativeContext, constructor: JSReceiver): bool { |
| if (IsForceSlowPath()) return false; |
| const promiseFun = UnsafeCast<JSFunction>( |
| nativeContext[NativeContextSlot::PROMISE_FUNCTION_INDEX]); |
| return promiseFun == constructor && !IsPromiseResolveProtectorCellInvalid(); |
| } |
| |
| // 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'); |
| |
| // 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; |
| |
| // 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 iterator be GetIterator(iterable). |
| // IfAbruptRejectPromise(iterator, promiseCapability). |
| let i: iterator::IteratorRecord; |
| try { |
| i = iterator::GetIterator(iterable); |
| } catch (e) deferred { |
| goto Reject(e); |
| } |
| |
| // Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability). |
| try { |
| // We can skip the "resolve" lookup on {constructor} if it's the |
| // Promise constructor and the Promise.resolve protector is intact, |
| // as that guards the lookup path for the "resolve" property on the |
| // Promise constructor. |
| const nativeContext = LoadNativeContext(context); |
| let promiseResolveFunction: JSAny = Undefined; |
| if (!IsPromiseResolveLookupChainIntact(nativeContext, receiver)) |
| deferred { |
| // 3. Let _promiseResolve_ be ? Get(_constructor_, `"resolve"`). |
| const resolve = GetProperty(receiver, kResolveString); |
| |
| // 4. If IsCallable(_promiseResolve_) is *false*, throw a |
| // *TypeError* exception. |
| promiseResolveFunction = Cast<Callable>(resolve) |
| otherwise ThrowTypeError( |
| MessageTemplate::kCalledNonCallable, 'resolve'); |
| } |
| |
| const fastIteratorResultMap = UnsafeCast<Map>( |
| nativeContext[NativeContextSlot::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( |
| UnsafeCast<Constructor>(receiver), 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, e) otherwise Reject; |
| } |
| } |
| label Reject(exception: Object) deferred { |
| Call( |
| context, UnsafeCast<JSAny>(reject), Undefined, |
| UnsafeCast<JSAny>(exception)); |
| return promise; |
| } |
| unreachable; |
| } |
| } |