| // 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-constructor-gen.h' |
| #include 'src/builtins/builtins-promise-gen.h' |
| |
| namespace runtime { |
| extern transitioning runtime |
| DebugPushPromise(implicit context: Context)(JSAny): JSAny; |
| |
| extern transitioning runtime |
| DebugPopPromise(implicit context: Context)(): JSAny; |
| |
| extern transitioning runtime |
| PromiseHookInit(implicit context: Context)(Object, Object): JSAny; |
| } |
| |
| // https://tc39.es/ecma262/#sec-promise-constructor |
| namespace promise { |
| |
| extern runtime IncrementUseCounter(Context, Smi): void; |
| type UseCounterFeature extends int31 |
| constexpr 'v8::Isolate::UseCounterFeature'; |
| const kPromiseConstructorReturnedUndefined: constexpr UseCounterFeature |
| generates 'v8::Isolate::kPromiseConstructorReturnedUndefined'; |
| |
| extern macro |
| IsDebugActive(): bool; |
| |
| transitioning macro |
| HasAccessCheckFailed(implicit context: Context)( |
| nativeContext: NativeContext, promiseFun: JSAny, executor: JSAny): bool { |
| BranchIfAccessCheckFailed(nativeContext, promiseFun, executor) |
| otherwise return true; |
| return false; |
| } |
| |
| extern macro ConstructorBuiltinsAssembler::FastNewObject( |
| Context, JSFunction, JSReceiver): JSObject; |
| |
| extern macro |
| PromiseBuiltinsAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate(): bool; |
| |
| // https://tc39.es/ecma262/#sec-promise-executor |
| transitioning javascript builtin |
| PromiseConstructor( |
| js-implicit context: NativeContext, receiver: JSAny, |
| newTarget: JSAny)(executor: JSAny): JSAny { |
| // 1. If NewTarget is undefined, throw a TypeError exception. |
| if (newTarget == Undefined) { |
| ThrowTypeError(MessageTemplate::kNotAPromise, newTarget); |
| } |
| |
| // 2. If IsCallable(executor) is false, throw a TypeError exception. |
| if (!Is<Callable>(executor)) { |
| ThrowTypeError(MessageTemplate::kResolverNotAFunction, executor); |
| } |
| |
| const promiseFun = *NativeContextSlot(ContextSlot::PROMISE_FUNCTION_INDEX); |
| |
| // Silently fail if the stack looks fishy. |
| if (HasAccessCheckFailed(context, promiseFun, executor)) { |
| IncrementUseCounter( |
| context, SmiConstant(kPromiseConstructorReturnedUndefined)); |
| return Undefined; |
| } |
| |
| let result: JSPromise; |
| if (promiseFun == newTarget) { |
| result = NewJSPromise(); |
| } else { |
| result = UnsafeCast<JSPromise>( |
| FastNewObject(context, promiseFun, UnsafeCast<JSReceiver>(newTarget))); |
| PromiseInit(result); |
| if (IsPromiseHookEnabledOrHasAsyncEventDelegate()) { |
| runtime::PromiseHookInit(result, Undefined); |
| } |
| } |
| |
| const isDebugActive = IsDebugActive(); |
| if (isDebugActive) runtime::DebugPushPromise(result); |
| |
| const funcs = CreatePromiseResolvingFunctions(result, True, context); |
| const resolve = funcs.resolve; |
| const reject = funcs.reject; |
| try { |
| Call(context, UnsafeCast<Callable>(executor), Undefined, resolve, reject); |
| } catch (e) { |
| Call(context, reject, Undefined, e); |
| } |
| |
| if (isDebugActive) runtime::DebugPopPromise(); |
| return result; |
| } |
| |
| // Promise.prototype.catch ( onRejected ) |
| // https://tc39.es/ecma262/#sec-promise.prototype.catch |
| transitioning javascript builtin |
| PromisePrototypeCatch( |
| js-implicit context: Context, receiver: JSAny)(onRejected: JSAny): JSAny { |
| // 1. Let promise be the this value. |
| // 2. Return ? Invoke(promise, "then", « undefined, onRejected »). |
| const nativeContext = LoadNativeContext(context); |
| return UnsafeCast<JSAny>( |
| InvokeThen(nativeContext, receiver, Undefined, onRejected)); |
| } |
| } |