diff options
Diffstat (limited to 'deps/v8/src/builtins/builtins-promise-gen.cc')
-rw-r--r-- | deps/v8/src/builtins/builtins-promise-gen.cc | 350 |
1 files changed, 225 insertions, 125 deletions
diff --git a/deps/v8/src/builtins/builtins-promise-gen.cc b/deps/v8/src/builtins/builtins-promise-gen.cc index 1d43217999..39d81ce9dd 100644 --- a/deps/v8/src/builtins/builtins-promise-gen.cc +++ b/deps/v8/src/builtins/builtins-promise-gen.cc @@ -6,17 +6,20 @@ #include "src/builtins/builtins-constructor-gen.h" #include "src/builtins/builtins-iterator-gen.h" +#include "src/builtins/builtins-promise.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/code-factory.h" #include "src/code-stub-assembler.h" #include "src/objects-inl.h" #include "src/objects/js-promise.h" +#include "src/objects/smi.h" namespace v8 { namespace internal { using compiler::Node; +using IteratorRecord = IteratorBuiltinsAssembler::IteratorRecord; Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { Node* const native_context = LoadNativeContext(context); @@ -37,12 +40,12 @@ Node* PromiseBuiltinsAssembler::AllocateJSPromise(Node* context) { void PromiseBuiltinsAssembler::PromiseInit(Node* promise) { STATIC_ASSERT(v8::Promise::kPending == 0); StoreObjectFieldNoWriteBarrier(promise, JSPromise::kReactionsOrResultOffset, - SmiConstant(Smi::kZero)); + SmiConstant(Smi::zero())); StoreObjectFieldNoWriteBarrier(promise, JSPromise::kFlagsOffset, - SmiConstant(Smi::kZero)); - for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { - int offset = JSPromise::kSize + i * kPointerSize; - StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::kZero)); + SmiConstant(Smi::zero())); + for (int offset = JSPromise::kSize; + offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) { + StoreObjectFieldNoWriteBarrier(promise, offset, SmiConstant(Smi::zero())); } } @@ -74,8 +77,8 @@ Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise( STATIC_ASSERT(JSPromise::kStatusShift == 0); StoreObjectFieldNoWriteBarrier(instance, JSPromise::kFlagsOffset, SmiConstant(status)); - for (int i = 0; i < v8::Promise::kEmbedderFieldCount; i++) { - int offset = JSPromise::kSize + i * kPointerSize; + for (int offset = JSPromise::kSize; + offset < JSPromise::kSizeWithEmbedderFields; offset += kTaggedSize) { StoreObjectFieldNoWriteBarrier(instance, offset, SmiConstant(0)); } @@ -114,7 +117,7 @@ TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); Node* const constructor = Parameter(Descriptor::kConstructor); Node* const debug_event = Parameter(Descriptor::kDebugEvent); - Node* const native_context = LoadNativeContext(context); + TNode<Context> const native_context = LoadNativeContext(context); Label if_not_constructor(this, Label::kDeferred), if_notcallable(this, Label::kDeferred), if_fast_promise_capability(this), @@ -164,11 +167,10 @@ TF_BUILTIN(NewPromiseCapability, PromiseBuiltinsAssembler) { native_context, Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN); Node* function_map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); - Node* executor = AllocateFunctionWithMapAndContext( - function_map, executor_info, executor_context); + TNode<JSFunction> executor = CAST(AllocateFunctionWithMapAndContext( + function_map, executor_info, executor_context)); - Node* promise = ConstructJS(CodeFactory::Construct(isolate()), - native_context, constructor, executor); + Node* promise = Construct(native_context, CAST(constructor), executor); StoreObjectField(capability, PromiseCapability::kPromiseOffset, promise); Node* resolve = @@ -204,19 +206,22 @@ Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementContext( CSA_ASSERT(this, IsNativeContext(native_context)); // TODO(bmeurer): Manually fold this into a single allocation. - Node* const array_map = LoadContextElement( - native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX); - Node* const values_array = AllocateJSArray(PACKED_ELEMENTS, array_map, - IntPtrConstant(0), SmiConstant(0)); + TNode<Map> array_map = CAST(LoadContextElement( + native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX)); + TNode<JSArray> values_array = AllocateJSArray( + PACKED_ELEMENTS, array_map, IntPtrConstant(0), SmiConstant(0)); - Node* const context = - CreatePromiseContext(native_context, kPromiseAllResolveElementLength); + Node* const context = CreatePromiseContext( + native_context, PromiseBuiltins::kPromiseAllResolveElementLength); StoreContextElementNoWriteBarrier( - context, kPromiseAllResolveElementRemainingSlot, SmiConstant(1)); + context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot, + SmiConstant(1)); StoreContextElementNoWriteBarrier( - context, kPromiseAllResolveElementCapabilitySlot, promise_capability); + context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot, + promise_capability); StoreContextElementNoWriteBarrier( - context, kPromiseAllResolveElementValuesArraySlot, values_array); + context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot, + values_array); return context; } @@ -244,20 +249,22 @@ Node* PromiseBuiltinsAssembler::CreatePromiseAllResolveElementFunction( Node* PromiseBuiltinsAssembler::CreatePromiseResolvingFunctionsContext( Node* promise, Node* debug_event, Node* native_context) { - Node* const context = - CreatePromiseContext(native_context, kPromiseContextLength); - StoreContextElementNoWriteBarrier(context, kPromiseSlot, promise); - StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, - FalseConstant()); - StoreContextElementNoWriteBarrier(context, kDebugEventSlot, debug_event); + Node* const context = CreatePromiseContext( + native_context, PromiseBuiltins::kPromiseContextLength); + StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kPromiseSlot, + promise); + StoreContextElementNoWriteBarrier( + context, PromiseBuiltins::kAlreadyResolvedSlot, FalseConstant()); + StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kDebugEventSlot, + debug_event); return context; } Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext( Node* promise_capability, Node* native_context) { - int kContextLength = kCapabilitiesContextLength; + int kContextLength = PromiseBuiltins::kCapabilitiesContextLength; Node* context = CreatePromiseContext(native_context, kContextLength); - StoreContextElementNoWriteBarrier(context, kCapabilitySlot, + StoreContextElementNoWriteBarrier(context, PromiseBuiltins::kCapabilitySlot, promise_capability); return context; } @@ -318,8 +325,11 @@ void PromiseBuiltinsAssembler::PerformPromiseThen( Word32Or(IsCallable(on_fulfilled), IsUndefined(on_fulfilled))); CSA_ASSERT(this, Word32Or(IsCallable(on_rejected), IsUndefined(on_rejected))); CSA_ASSERT(this, TaggedIsNotSmi(result_promise_or_capability)); - CSA_ASSERT(this, Word32Or(IsJSPromise(result_promise_or_capability), - IsPromiseCapability(result_promise_or_capability))); + CSA_ASSERT( + this, + Word32Or(Word32Or(IsJSPromise(result_promise_or_capability), + IsPromiseCapability(result_promise_or_capability)), + IsUndefined(result_promise_or_capability))); Label if_pending(this), if_notpending(this), done(this); Node* const status = PromiseStatus(promise); @@ -373,7 +383,7 @@ void PromiseBuiltinsAssembler::PerformPromiseThen( Node* microtask = AllocatePromiseReactionJobTask( var_map.value(), context, argument, var_handler.value(), result_promise_or_capability); - CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), microtask); + CallBuiltin(Builtins::kEnqueueMicrotask, context, microtask); Goto(&done); } @@ -390,7 +400,8 @@ TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) { Node* const result_promise = Parameter(Descriptor::kResultPromise); CSA_ASSERT(this, TaggedIsNotSmi(result_promise)); - CSA_ASSERT(this, IsJSPromise(result_promise)); + CSA_ASSERT( + this, Word32Or(IsJSPromise(result_promise), IsUndefined(result_promise))); PerformPromiseThen(context, promise, on_fulfilled, on_rejected, result_promise); @@ -466,7 +477,7 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions( { VARIABLE(var_current, MachineRepresentation::kTagged, reactions); VARIABLE(var_reversed, MachineRepresentation::kTagged, - SmiConstant(Smi::kZero)); + SmiConstant(Smi::zero())); Label loop(this, {&var_current, &var_reversed}), done_loop(this); Goto(&loop); @@ -500,7 +511,8 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions( // Morph {current} from a PromiseReaction into a PromiseReactionJobTask // and schedule that on the microtask queue. We try to minimize the number // of stores here to avoid screwing up the store buffer. - STATIC_ASSERT(PromiseReaction::kSize == PromiseReactionJobTask::kSize); + STATIC_ASSERT(static_cast<int>(PromiseReaction::kSize) == + static_cast<int>(PromiseReactionJobTask::kSize)); if (type == PromiseReaction::kFulfill) { StoreMapNoWriteBarrier(current, RootIndex::kPromiseFulfillReactionJobTaskMap); @@ -508,10 +520,13 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions( argument); StoreObjectField(current, PromiseReactionJobTask::kContextOffset, context); - STATIC_ASSERT(PromiseReaction::kFulfillHandlerOffset == - PromiseReactionJobTask::kHandlerOffset); - STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset == - PromiseReactionJobTask::kPromiseOrCapabilityOffset); + STATIC_ASSERT( + static_cast<int>(PromiseReaction::kFulfillHandlerOffset) == + static_cast<int>(PromiseReactionJobTask::kHandlerOffset)); + STATIC_ASSERT( + static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) == + static_cast<int>( + PromiseReactionJobTask::kPromiseOrCapabilityOffset)); } else { Node* handler = LoadObjectField(current, PromiseReaction::kRejectHandlerOffset); @@ -523,10 +538,12 @@ Node* PromiseBuiltinsAssembler::TriggerPromiseReactions( context); StoreObjectField(current, PromiseReactionJobTask::kHandlerOffset, handler); - STATIC_ASSERT(PromiseReaction::kPromiseOrCapabilityOffset == - PromiseReactionJobTask::kPromiseOrCapabilityOffset); + STATIC_ASSERT( + static_cast<int>(PromiseReaction::kPromiseOrCapabilityOffset) == + static_cast<int>( + PromiseReactionJobTask::kPromiseOrCapabilityOffset)); } - CallBuiltin(Builtins::kEnqueueMicrotask, NoContextConstant(), current); + CallBuiltin(Builtins::kEnqueueMicrotask, context, current); Goto(&loop); } BIND(&done_loop); @@ -633,6 +650,14 @@ void PromiseBuiltinsAssembler::BranchIfPromiseResolveLookupChainIntact( Branch(IsPromiseResolveProtectorCellInvalid(), if_slow, if_fast); } +void PromiseBuiltinsAssembler::GotoIfNotPromiseResolveLookupChainIntact( + Node* native_context, Node* constructor, Label* if_slow) { + Label if_fast(this); + BranchIfPromiseResolveLookupChainIntact(native_context, constructor, &if_fast, + if_slow); + BIND(&if_fast); +} + void PromiseBuiltinsAssembler::BranchIfPromiseSpeciesLookupChainIntact( Node* native_context, Node* promise_map, Label* if_fast, Label* if_slow) { CSA_ASSERT(this, IsNativeContext(native_context)); @@ -737,22 +762,24 @@ TF_BUILTIN(PromiseCapabilityDefaultReject, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); // 2. Let promise be F.[[Promise]]. - Node* const promise = LoadContextElement(context, kPromiseSlot); + Node* const promise = + LoadContextElement(context, PromiseBuiltins::kPromiseSlot); // 3. Let alreadyResolved be F.[[AlreadyResolved]]. Label if_already_resolved(this, Label::kDeferred); Node* const already_resolved = - LoadContextElement(context, kAlreadyResolvedSlot); + LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot); // 4. If alreadyResolved.[[Value]] is true, return undefined. GotoIf(IsTrue(already_resolved), &if_already_resolved); // 5. Set alreadyResolved.[[Value]] to true. - StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, - TrueConstant()); + StoreContextElementNoWriteBarrier( + context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant()); // 6. Return RejectPromise(promise, reason). - Node* const debug_event = LoadContextElement(context, kDebugEventSlot); + Node* const debug_event = + LoadContextElement(context, PromiseBuiltins::kDebugEventSlot); Return(CallBuiltin(Builtins::kRejectPromise, context, promise, reason, debug_event)); @@ -769,19 +796,20 @@ TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); // 2. Let promise be F.[[Promise]]. - Node* const promise = LoadContextElement(context, kPromiseSlot); + Node* const promise = + LoadContextElement(context, PromiseBuiltins::kPromiseSlot); // 3. Let alreadyResolved be F.[[AlreadyResolved]]. Label if_already_resolved(this, Label::kDeferred); Node* const already_resolved = - LoadContextElement(context, kAlreadyResolvedSlot); + LoadContextElement(context, PromiseBuiltins::kAlreadyResolvedSlot); // 4. If alreadyResolved.[[Value]] is true, return undefined. GotoIf(IsTrue(already_resolved), &if_already_resolved); // 5. Set alreadyResolved.[[Value]] to true. - StoreContextElementNoWriteBarrier(context, kAlreadyResolvedSlot, - TrueConstant()); + StoreContextElementNoWriteBarrier( + context, PromiseBuiltins::kAlreadyResolvedSlot, TrueConstant()); // The rest of the logic (and the catch prediction) is // encapsulated in the dedicated ResolvePromise builtin. @@ -1082,8 +1110,8 @@ TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) { GotoIfNot(WordEqual(then, promise_then), &if_slow); Node* const thenable_map = LoadMap(thenable); GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow); - GotoIf(IsPromiseHookEnabled(), &if_slow); - GotoIf(IsDebugActive(), &if_slow); + GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), + &if_slow); BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map, &if_fast, &if_slow); @@ -1154,11 +1182,14 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, CSA_ASSERT(this, TaggedIsNotSmi(handler)); CSA_ASSERT(this, Word32Or(IsUndefined(handler), IsCallable(handler))); CSA_ASSERT(this, TaggedIsNotSmi(promise_or_capability)); - CSA_ASSERT(this, Word32Or(IsJSPromise(promise_or_capability), - IsPromiseCapability(promise_or_capability))); + CSA_ASSERT(this, + Word32Or(Word32Or(IsJSPromise(promise_or_capability), + IsPromiseCapability(promise_or_capability)), + IsUndefined(promise_or_capability))); VARIABLE(var_handler_result, MachineRepresentation::kTagged, argument); - Label if_handler_callable(this), if_fulfill(this), if_reject(this); + Label if_handler_callable(this), if_fulfill(this), if_reject(this), + if_internal(this); Branch(IsUndefined(handler), type == PromiseReaction::kFulfill ? &if_fulfill : &if_reject, &if_handler_callable); @@ -1170,7 +1201,16 @@ void PromiseBuiltinsAssembler::PromiseReactionJob(Node* context, Node* argument, context, handler, UndefinedConstant(), argument); GotoIfException(result, &if_reject, &var_handler_result); var_handler_result.Bind(result); - Goto(&if_fulfill); + Branch(IsUndefined(promise_or_capability), &if_internal, &if_fulfill); + } + + BIND(&if_internal); + { + // There's no [[Capability]] for this promise reaction job, which + // means that this is a specification-internal operation (aka await) + // where the result does not matter (see the specification change in + // https://github.com/tc39/ecma262/pull/1146 for details). + Return(UndefinedConstant()); } BIND(&if_fulfill); @@ -1371,7 +1411,8 @@ TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) { Node* const reject = Parameter(Descriptor::kReject); Node* const context = Parameter(Descriptor::kContext); - Node* const capability = LoadContextElement(context, kCapabilitySlot); + Node* const capability = + LoadContextElement(context, PromiseBuiltins::kCapabilitySlot); Label if_alreadyinvoked(this, Label::kDeferred); GotoIfNot(IsUndefined( @@ -1439,12 +1480,12 @@ TF_BUILTIN(PromiseReject, PromiseBuiltinsAssembler) { std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( Node* on_finally, Node* constructor, Node* native_context) { - Node* const promise_context = - CreatePromiseContext(native_context, kPromiseFinallyContextLength); - StoreContextElementNoWriteBarrier(promise_context, kOnFinallySlot, - on_finally); - StoreContextElementNoWriteBarrier(promise_context, kConstructorSlot, - constructor); + Node* const promise_context = CreatePromiseContext( + native_context, PromiseBuiltins::kPromiseFinallyContextLength); + StoreContextElementNoWriteBarrier( + promise_context, PromiseBuiltins::kOnFinallySlot, on_finally); + StoreContextElementNoWriteBarrier( + promise_context, PromiseBuiltins::kConstructorSlot, constructor); Node* const map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); Node* const then_finally_info = LoadContextElement( @@ -1461,15 +1502,16 @@ std::pair<Node*, Node*> PromiseBuiltinsAssembler::CreatePromiseFinallyFunctions( TF_BUILTIN(PromiseValueThunkFinally, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); - Node* const value = LoadContextElement(context, kValueSlot); + Node* const value = LoadContextElement(context, PromiseBuiltins::kValueSlot); Return(value); } Node* PromiseBuiltinsAssembler::CreateValueThunkFunction(Node* value, Node* native_context) { Node* const value_thunk_context = CreatePromiseContext( - native_context, kPromiseValueThunkOrReasonContextLength); - StoreContextElementNoWriteBarrier(value_thunk_context, kValueSlot, value); + native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength); + StoreContextElementNoWriteBarrier(value_thunk_context, + PromiseBuiltins::kValueSlot, value); Node* const map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); Node* const value_thunk_info = LoadContextElement( @@ -1486,7 +1528,8 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); // 1. Let onFinally be F.[[OnFinally]]. - Node* const on_finally = LoadContextElement(context, kOnFinallySlot); + Node* const on_finally = + LoadContextElement(context, PromiseBuiltins::kOnFinallySlot); // 2. Assert: IsCallable(onFinally) is true. CSA_ASSERT(this, IsCallable(on_finally)); @@ -1497,7 +1540,8 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { context, on_finally, UndefinedConstant()); // 4. Let C be F.[[Constructor]]. - Node* const constructor = LoadContextElement(context, kConstructorSlot); + Node* const constructor = + LoadContextElement(context, PromiseBuiltins::kConstructorSlot); // 5. Assert: IsConstructor(C) is true. CSA_ASSERT(this, IsConstructor(constructor)); @@ -1517,7 +1561,7 @@ TF_BUILTIN(PromiseThenFinally, PromiseBuiltinsAssembler) { TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); - Node* const reason = LoadContextElement(context, kValueSlot); + Node* const reason = LoadContextElement(context, PromiseBuiltins::kValueSlot); CallRuntime(Runtime::kThrow, context, reason); Unreachable(); } @@ -1525,8 +1569,9 @@ TF_BUILTIN(PromiseThrowerFinally, PromiseBuiltinsAssembler) { Node* PromiseBuiltinsAssembler::CreateThrowerFunction(Node* reason, Node* native_context) { Node* const thrower_context = CreatePromiseContext( - native_context, kPromiseValueThunkOrReasonContextLength); - StoreContextElementNoWriteBarrier(thrower_context, kValueSlot, reason); + native_context, PromiseBuiltins::kPromiseValueThunkOrReasonContextLength); + StoreContextElementNoWriteBarrier(thrower_context, + PromiseBuiltins::kValueSlot, reason); Node* const map = LoadContextElement( native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX); Node* const thrower_info = LoadContextElement( @@ -1543,7 +1588,8 @@ TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { Node* const context = Parameter(Descriptor::kContext); // 1. Let onFinally be F.[[OnFinally]]. - Node* const on_finally = LoadContextElement(context, kOnFinallySlot); + Node* const on_finally = + LoadContextElement(context, PromiseBuiltins::kOnFinallySlot); // 2. Assert: IsCallable(onFinally) is true. CSA_ASSERT(this, IsCallable(on_finally)); @@ -1554,7 +1600,8 @@ TF_BUILTIN(PromiseCatchFinally, PromiseBuiltinsAssembler) { context, on_finally, UndefinedConstant()); // 4. Let C be F.[[Constructor]]. - Node* const constructor = LoadContextElement(context, kConstructorSlot); + Node* const constructor = + LoadContextElement(context, PromiseBuiltins::kConstructorSlot); // 5. Assert: IsConstructor(C) is true. CSA_ASSERT(this, IsConstructor(constructor)); @@ -1689,8 +1736,8 @@ TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) { // the runtime handle this operation, which greatly reduces // the complexity here and also avoids a couple of back and // forth between JavaScript and C++ land. - GotoIf(IsPromiseHookEnabled(), &if_runtime); - GotoIf(IsDebugActive(), &if_runtime); + GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), + &if_runtime); // 7. If promise.[[PromiseIsHandled]] is false, perform // HostPromiseRejectionTracker(promise, "reject"). @@ -1737,8 +1784,8 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { // the runtime handle this operation, which greatly reduces // the complexity here and also avoids a couple of back and // forth between JavaScript and C++ land. - GotoIf(IsPromiseHookEnabled(), &if_runtime); - GotoIf(IsDebugActive(), &if_runtime); + GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), + &if_runtime); // 6. If SameValue(resolution, promise) is true, then // We can use pointer comparison here, since the {promise} is guaranteed @@ -1837,13 +1884,12 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( Variable* var_exception) { IteratorBuiltinsAssembler iter_assembler(state()); - Node* const instrumenting = IsDebugActive(); Node* const native_context = LoadNativeContext(context); // For catch prediction, don't treat the .then calls as handling it; // instead, recurse outwards. SetForwardingHandlerIfTrue( - native_context, instrumenting, + native_context, IsDebugActive(), LoadObjectField(capability, PromiseCapability::kRejectOffset)); Node* const resolve_element_context = @@ -1873,22 +1919,22 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( native_context, next, fast_iterator_result_map, if_exception, var_exception); - // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »). - Node* const next_promise = - InvokeResolve(native_context, constructor, next_value, &close_iterator, - var_exception); - // Check if we reached the limit. TNode<Smi> const index = var_index.value(); GotoIf(SmiEqual(index, SmiConstant(PropertyArray::HashField::kMax)), &too_many_elements); + // Set index to index + 1. + var_index = SmiAdd(index, SmiConstant(1)); + // Set remainingElementsCount.[[Value]] to // remainingElementsCount.[[Value]] + 1. TNode<Smi> const remaining_elements_count = CAST(LoadContextElement( - resolve_element_context, kPromiseAllResolveElementRemainingSlot)); + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementRemainingSlot)); StoreContextElementNoWriteBarrier( - resolve_element_context, kPromiseAllResolveElementRemainingSlot, + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementRemainingSlot, SmiAdd(remaining_elements_count, SmiConstant(1))); // Let resolveElement be CreateBuiltinFunction(steps, @@ -1905,28 +1951,77 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( Node* const resolve_element_fun = CreatePromiseAllResolveElementFunction( resolve_element_context, index, native_context); - // Perform ? Invoke(nextPromise, "then", « resolveElement, - // resultCapability.[[Reject]] »). - Node* const then = - GetProperty(native_context, next_promise, factory()->then_string()); - GotoIfException(then, &close_iterator, var_exception); + // We can skip the "resolve" lookup on the {constructor} as well as the + // "then" lookup on the result of the "resolve" call, and immediately + // chain continuation onto the {next_value} if: + // + // (a) The {constructor} is the intrinsic %Promise% function, and + // looking up "resolve" on {constructor} yields the initial + // Promise.resolve() builtin, and + // (b) the promise @@species protector cell is valid, meaning that + // no one messed with the Symbol.species property on any + // intrinsic promise or on the Promise.prototype, and + // (c) the {next_value} is a JSPromise whose [[Prototype]] field + // contains the intrinsic %PromisePrototype%, and + // (d) we're not running with async_hooks or DevTools enabled. + // + // In that case we also don't need to allocate a chained promise for + // the PromiseReaction (aka we can pass undefined to PerformPromiseThen), + // since this is only necessary for DevTools and PromiseHooks. + Label if_fast(this), if_slow(this); + GotoIfNotPromiseResolveLookupChainIntact(native_context, constructor, + &if_slow); + GotoIf(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), + &if_slow); + GotoIf(IsPromiseSpeciesProtectorCellInvalid(), &if_slow); + GotoIf(TaggedIsSmi(next_value), &if_slow); + Node* const next_value_map = LoadMap(next_value); + BranchIfPromiseThenLookupChainIntact(native_context, next_value_map, + &if_fast, &if_slow); - Node* const then_call = CallJS( - CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined), - native_context, then, next_promise, resolve_element_fun, - LoadObjectField(capability, PromiseCapability::kRejectOffset)); - GotoIfException(then_call, &close_iterator, var_exception); + BIND(&if_fast); + { + // Register the PromiseReaction immediately on the {next_value}, not + // passing any chained promise since neither async_hooks nor DevTools + // are enabled, so there's no use of the resulting promise. + PerformPromiseThen( + native_context, next_value, resolve_element_fun, + LoadObjectField(capability, PromiseCapability::kRejectOffset), + UndefinedConstant()); + Goto(&loop); + } - // For catch prediction, mark that rejections here are semantically - // handled by the combined Promise. - SetPromiseHandledByIfTrue(native_context, instrumenting, then_call, [=]() { - // Load promiseCapability.[[Promise]] - return LoadObjectField(capability, PromiseCapability::kPromiseOffset); - }); + BIND(&if_slow); + { + // Let nextPromise be ? Invoke(constructor, "resolve", « nextValue »). + Node* const next_promise = + InvokeResolve(native_context, constructor, next_value, + &close_iterator, var_exception); - // Set index to index + 1. - var_index = SmiAdd(index, SmiConstant(1)); - Goto(&loop); + // Perform ? Invoke(nextPromise, "then", « resolveElement, + // resultCapability.[[Reject]] »). + Node* const then = + GetProperty(native_context, next_promise, factory()->then_string()); + GotoIfException(then, &close_iterator, var_exception); + + Node* const then_call = + CallJS(CodeFactory::Call(isolate(), + ConvertReceiverMode::kNotNullOrUndefined), + native_context, then, next_promise, resolve_element_fun, + LoadObjectField(capability, PromiseCapability::kRejectOffset)); + GotoIfException(then_call, &close_iterator, var_exception); + + // For catch prediction, mark that rejections here are semantically + // handled by the combined Promise. + SetPromiseHandledByIfTrue( + native_context, IsDebugActive(), then_call, [=]() { + // Load promiseCapability.[[Promise]] + return LoadObjectField(capability, + PromiseCapability::kPromiseOffset); + }); + + Goto(&loop); + } } BIND(&too_many_elements); @@ -1960,11 +2055,13 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( // Set remainingElementsCount.[[Value]] to // remainingElementsCount.[[Value]] - 1. TNode<Smi> remaining_elements_count = CAST(LoadContextElement( - resolve_element_context, kPromiseAllResolveElementRemainingSlot)); + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementRemainingSlot)); remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1)); - StoreContextElementNoWriteBarrier(resolve_element_context, - kPromiseAllResolveElementRemainingSlot, - remaining_elements_count); + StoreContextElementNoWriteBarrier( + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementRemainingSlot, + remaining_elements_count); GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise); @@ -1973,7 +2070,8 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( // fancy Thenable that calls the resolve callback immediately, so we need // to handle that correctly here. Node* const values_array = LoadContextElement( - resolve_element_context, kPromiseAllResolveElementValuesArraySlot); + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot); Node* const old_elements = LoadElements(values_array); TNode<Smi> const old_capacity = LoadFixedArrayBaseLength(old_elements); TNode<Smi> const new_capacity = var_index.value(); @@ -1996,7 +2094,8 @@ Node* PromiseBuiltinsAssembler::PerformPromiseAll( Node* const resolve = LoadObjectField(capability, PromiseCapability::kResolveOffset); Node* const values_array = LoadContextElement( - resolve_element_context, kPromiseAllResolveElementValuesArraySlot); + resolve_element_context, + PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot); Node* const resolve_call = CallJS( CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), native_context, resolve, UndefinedConstant(), values_array); @@ -2079,8 +2178,10 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) { // first time, in which case we make it point to the native context here // to mark this resolve element closure as done. GotoIf(IsNativeContext(context), &already_called); - CSA_ASSERT(this, SmiEqual(LoadFixedArrayBaseLength(context), - SmiConstant(kPromiseAllResolveElementLength))); + CSA_ASSERT( + this, + SmiEqual(LoadObjectField<Smi>(context, Context::kLengthOffset), + SmiConstant(PromiseBuiltins::kPromiseAllResolveElementLength))); TNode<Context> native_context = LoadNativeContext(context); StoreObjectField(function, JSFunction::kContextOffset, native_context); @@ -2093,8 +2194,8 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) { TNode<IntPtrT> index = IntPtrSub(identity_hash, IntPtrConstant(1)); // Check if we need to grow the [[ValuesArray]] to store {value} at {index}. - TNode<JSArray> values_array = CAST( - LoadContextElement(context, kPromiseAllResolveElementValuesArraySlot)); + TNode<JSArray> values_array = CAST(LoadContextElement( + context, PromiseBuiltins::kPromiseAllResolveElementValuesArraySlot)); TNode<FixedArray> elements = CAST(LoadElements(values_array)); TNode<IntPtrT> values_length = LoadAndUntagObjectField(values_array, JSArray::kLengthOffset); @@ -2153,17 +2254,18 @@ TF_BUILTIN(PromiseAllResolveElementClosure, PromiseBuiltinsAssembler) { } BIND(&done); - TNode<Smi> remaining_elements_count = - CAST(LoadContextElement(context, kPromiseAllResolveElementRemainingSlot)); + TNode<Smi> remaining_elements_count = CAST(LoadContextElement( + context, PromiseBuiltins::kPromiseAllResolveElementRemainingSlot)); remaining_elements_count = SmiSub(remaining_elements_count, SmiConstant(1)); - StoreContextElement(context, kPromiseAllResolveElementRemainingSlot, + StoreContextElement(context, + PromiseBuiltins::kPromiseAllResolveElementRemainingSlot, remaining_elements_count); GotoIf(SmiEqual(remaining_elements_count, SmiConstant(0)), &resolve_promise); Return(UndefinedConstant()); BIND(&resolve_promise); - TNode<PromiseCapability> capability = CAST( - LoadContextElement(context, kPromiseAllResolveElementCapabilitySlot)); + TNode<PromiseCapability> capability = CAST(LoadContextElement( + context, PromiseBuiltins::kPromiseAllResolveElementCapabilitySlot)); TNode<Object> resolve = LoadObjectField(capability, PromiseCapability::kResolveOffset); CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined), @@ -2200,14 +2302,12 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) { Node* const reject = LoadObjectField(capability, PromiseCapability::kRejectOffset); - Node* const instrumenting = IsDebugActive(); - Label close_iterator(this, Label::kDeferred); Label reject_promise(this, Label::kDeferred); // For catch prediction, don't treat the .then calls as handling it; // instead, recurse outwards. - SetForwardingHandlerIfTrue(context, instrumenting, reject); + SetForwardingHandlerIfTrue(context, IsDebugActive(), reject); // Let iterator be GetIterator(iterable). // IfAbruptRejectPromise(iterator, promiseCapability). @@ -2259,7 +2359,7 @@ TF_BUILTIN(PromiseRace, PromiseBuiltinsAssembler) { // For catch prediction, mark that rejections here are semantically // handled by the combined Promise. - SetPromiseHandledByIfTrue(context, instrumenting, then_call, [=]() { + SetPromiseHandledByIfTrue(context, IsDebugActive(), then_call, [=]() { // Load promiseCapability.[[Promise]] return LoadObjectField(capability, PromiseCapability::kPromiseOffset); }); |