blob: 7e56a08c8443ad31dd527b566473d62dff061d6e [file] [log] [blame]
// 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;
}
}