Commit 3f5a60ac authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Use TF_BUILTIN

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2583753002
Cr-Commit-Position: refs/heads/master@{#41770}
parent 8341b8ba
...@@ -11,345 +11,142 @@ ...@@ -11,345 +11,142 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
// ES#sec-promise-reject-functions typedef compiler::Node Node;
// Promise Reject Functions typedef CodeStubAssembler::ParameterMode ParameterMode;
BUILTIN(PromiseRejectClosure) { typedef compiler::CodeAssemblerState CodeAssemblerState;
HandleScope scope(isolate);
Handle<Context> context(isolate->context(), isolate); class PromiseBuiltinsAssembler : public CodeStubAssembler {
public:
explicit PromiseBuiltinsAssembler(CodeAssemblerState* state)
: CodeStubAssembler(state) {}
if (PromiseUtils::HasAlreadyVisited(context)) { protected:
return isolate->heap()->undefined_value(); Node* ThrowIfNotJSReceiver(Node* context, Node* value,
} MessageTemplate::Template msg_template);
PromiseUtils::SetAlreadyVisited(context); Node* SpeciesConstructor(Node* context, Node* object,
Handle<Object> value = args.atOrUndefined(isolate, 1); Node* default_constructor);
Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate);
Handle<Object> debug_event =
handle(PromiseUtils::GetDebugEvent(context), isolate);
MaybeHandle<Object> maybe_result;
Handle<Object> argv[] = {promise, value, debug_event};
RETURN_FAILURE_ON_EXCEPTION(
isolate, Execution::Call(isolate, isolate->promise_internal_reject(),
isolate->factory()->undefined_value(),
arraysize(argv), argv));
return isolate->heap()->undefined_value();
}
// ES#sec-createresolvingfunctions
// CreateResolvingFunctions ( promise )
BUILTIN(CreateResolvingFunctions) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
Handle<JSObject> promise = args.at<JSObject>(1);
Handle<Object> debug_event = args.at(2);
Handle<JSFunction> resolve, reject;
PromiseUtils::CreateResolvingFunctions(isolate, promise, debug_event,
&resolve, &reject);
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
result->set(0, *resolve);
result->set(1, *reject);
return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2,
NOT_TENURED);
}
void Builtins::Generate_PromiseConstructor( Node* PromiseHasHandler(Node* promise);
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef CodeStubAssembler::Variable Variable;
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* const executor = a.Parameter(1); void PromiseSetHasHandler(Node* promise);
Node* const new_target = a.Parameter(2);
Node* const context = a.Parameter(4);
Isolate* isolate = a.isolate();
Label if_targetisundefined(&a, Label::kDeferred); void AppendPromiseCallback(int offset, compiler::Node* promise,
compiler::Node* value);
a.GotoIf(a.IsUndefined(new_target), &if_targetisundefined); Node* InternalPerformPromiseThen(Node* context, Node* promise,
Node* on_resolve, Node* on_reject,
Node* deferred);
Label if_notcallable(&a, Label::kDeferred); void InternalResolvePromise(Node* context, Node* promise, Node* result,
Label* out);
a.GotoIf(a.TaggedIsSmi(executor), &if_notcallable); void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
};
Node* const executor_map = a.LoadMap(executor); Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver(
a.GotoUnless(a.IsCallableMap(executor_map), &if_notcallable); Node* context, Node* value, MessageTemplate::Template msg_template) {
Label out(this), throw_exception(this, Label::kDeferred);
Variable var_value_map(this, MachineRepresentation::kTagged);
Node* const native_context = a.LoadNativeContext(context); GotoIf(TaggedIsSmi(value), &throw_exception);
Node* const promise_fun =
a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const is_debug_active = a.IsDebugActive();
Label if_targetisnotmodified(&a), if_targetismodified(&a, Label::kDeferred),
run_executor(&a), debug_push(&a, Label::kDeferred), init(&a);
a.Branch(a.WordEqual(promise_fun, new_target), &if_targetisnotmodified,
&if_targetismodified);
Variable var_result(&a, MachineRepresentation::kTagged),
var_reject_call(&a, MachineRepresentation::kTagged),
var_reason(&a, MachineRepresentation::kTagged);
a.Bind(&if_targetisnotmodified);
{
Node* const instance = a.AllocateJSPromise(context);
var_result.Bind(instance);
a.Goto(&init);
}
a.Bind(&if_targetismodified);
{
Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate);
Node* const instance =
a.CallStub(fast_new_object_stub, context, promise_fun, new_target);
var_result.Bind(instance);
a.Goto(&init);
}
a.Bind(&init);
{
a.PromiseInit(var_result.value());
a.Branch(is_debug_active, &debug_push, &run_executor);
}
a.Bind(&debug_push);
{
a.CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
a.Goto(&run_executor);
}
a.Bind(&run_executor);
{
Label out(&a), if_rejectpromise(&a), debug_pop(&a, Label::kDeferred);
// TODO(gsathya): Move this to TF.
Node* const resolving_functions = a.CallRuntime(
Runtime::kCreateResolvingFunctions, context, var_result.value());
Node* const resolve =
a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(0));
Node* const reject =
a.LoadFixedArrayElement(resolving_functions, a.IntPtrConstant(1));
Callable call_callable = CodeFactory::Call(isolate);
Node* const maybe_exception =
a.CallJS(call_callable, context, executor, a.UndefinedConstant(),
resolve, reject);
a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
a.Branch(is_debug_active, &debug_pop, &out);
a.Bind(&if_rejectpromise);
{
Callable call_callable = CodeFactory::Call(isolate);
a.CallJS(call_callable, context, reject, a.UndefinedConstant(),
var_reason.value());
a.Branch(is_debug_active, &debug_pop, &out);
}
a.Bind(&debug_pop);
{
a.CallRuntime(Runtime::kDebugPopPromise, context);
a.Goto(&out);
}
a.Bind(&out);
a.Return(var_result.value());
}
// 1. If NewTarget is undefined, throw a TypeError exception.
a.Bind(&if_targetisundefined);
{
Node* const message_id = a.SmiConstant(MessageTemplate::kNotAPromise);
a.CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target);
a.Return(a.UndefinedConstant()); // Never reached.
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
a.Bind(&if_notcallable);
{
Node* const message_id =
a.SmiConstant(MessageTemplate::kResolverNotAFunction);
a.CallRuntime(Runtime::kThrowTypeError, context, message_id, executor);
a.Return(a.UndefinedConstant()); // Never reached.
}
}
void Builtins::Generate_PromiseInternalConstructor(
compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
CodeStubAssembler a(state);
Node* const context = a.Parameter(3);
Node* const instance = a.AllocateJSPromise(context);
a.PromiseInit(instance);
a.Return(instance);
}
void Builtins::Generate_PromiseCreateAndSet(
compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
CodeStubAssembler a(state);
Node* const status = a.Parameter(1);
Node* const result = a.Parameter(2);
Node* const context = a.Parameter(5);
Node* const instance = a.AllocateJSPromise(context);
a.PromiseSet(instance, status, result);
a.Return(instance);
}
namespace {
compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate,
compiler::Node* context,
compiler::Node* value,
MessageTemplate::Template msg_template) {
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Label out(a), throw_exception(a, Label::kDeferred);
Variable var_value_map(a, MachineRepresentation::kTagged);
a->GotoIf(a->TaggedIsSmi(value), &throw_exception);
// Load the instance type of the {value}. // Load the instance type of the {value}.
var_value_map.Bind(a->LoadMap(value)); var_value_map.Bind(LoadMap(value));
Node* const value_instance_type = Node* const value_instance_type = LoadMapInstanceType(var_value_map.value());
a->LoadMapInstanceType(var_value_map.value());
a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception);
&throw_exception);
// The {value} is not a compatible receiver for this method. // The {value} is not a compatible receiver for this method.
a->Bind(&throw_exception); Bind(&throw_exception);
{ {
Node* const message_id = a->SmiConstant(msg_template); Node* const message_id = SmiConstant(msg_template);
a->CallRuntime(Runtime::kThrowTypeError, context, message_id); CallRuntime(Runtime::kThrowTypeError, context, message_id);
var_value_map.Bind(a->UndefinedConstant()); var_value_map.Bind(UndefinedConstant());
a->Goto(&out); // Never reached. Goto(&out); // Never reached.
} }
a->Bind(&out); Bind(&out);
return var_value_map.value(); return var_value_map.value();
} }
} // namespace Node* PromiseBuiltinsAssembler::PromiseHasHandler(Node* promise) {
Node* const flags = LoadObjectField(promise, JSPromise::kFlagsOffset);
void Builtins::Generate_IsPromise(compiler::CodeAssemblerState* state) { return IsSetWord(SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
Node* const maybe_promise = a.Parameter(1);
Label if_notpromise(&a, Label::kDeferred);
a.GotoIf(a.TaggedIsSmi(maybe_promise), &if_notpromise);
Node* const result = a.SelectBooleanConstant(
a.HasInstanceType(maybe_promise, JS_PROMISE_TYPE));
a.Return(result);
a.Bind(&if_notpromise);
a.Return(a.FalseConstant());
}
namespace {
compiler::Node* PromiseHasHandler(CodeStubAssembler* a,
compiler::Node* promise) {
typedef compiler::Node Node;
Node* const flags = a->LoadObjectField(promise, JSPromise::kFlagsOffset);
return a->IsSetWord(a->SmiUntag(flags), 1 << JSPromise::kHasHandlerBit);
} }
void PromiseSetHasHandler(CodeStubAssembler* a, compiler::Node* promise) { void PromiseBuiltinsAssembler::PromiseSetHasHandler(Node* promise) {
typedef compiler::Node Node;
Node* const flags = Node* const flags =
a->SmiUntag(a->LoadObjectField(promise, JSPromise::kFlagsOffset)); SmiUntag(LoadObjectField(promise, JSPromise::kFlagsOffset));
Node* const new_flags = Node* const new_flags =
a->WordOr(flags, a->IntPtrConstant(1 << JSPromise::kHasHandlerBit)); WordOr(flags, IntPtrConstant(1 << JSPromise::kHasHandlerBit));
a->StoreObjectField(promise, JSPromise::kFlagsOffset, a->SmiTag(new_flags)); StoreObjectField(promise, JSPromise::kFlagsOffset, SmiTag(new_flags));
} }
compiler::Node* SpeciesConstructor(CodeStubAssembler* a, Isolate* isolate, Node* PromiseBuiltinsAssembler::SpeciesConstructor(Node* context, Node* object,
compiler::Node* context, Node* default_constructor) {
compiler::Node* object, Isolate* isolate = this->isolate();
compiler::Node* default_constructor) { Variable var_result(this, MachineRepresentation::kTagged);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Variable var_result(a, MachineRepresentation::kTagged);
var_result.Bind(default_constructor); var_result.Bind(default_constructor);
// 2. Let C be ? Get(O, "constructor"). // 2. Let C be ? Get(O, "constructor").
Node* const constructor_str = Node* const constructor_str =
a->HeapConstant(isolate->factory()->constructor_string()); HeapConstant(isolate->factory()->constructor_string());
Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const constructor = Node* const constructor =
a->CallStub(getproperty_callable, context, object, constructor_str); CallStub(getproperty_callable, context, object, constructor_str);
// 3. If C is undefined, return defaultConstructor. // 3. If C is undefined, return defaultConstructor.
Label out(a); Label out(this);
a->GotoIf(a->IsUndefined(constructor), &out); GotoIf(IsUndefined(constructor), &out);
// 4. If Type(C) is not Object, throw a TypeError exception. // 4. If Type(C) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(a, a->isolate(), context, constructor, ThrowIfNotJSReceiver(context, constructor,
MessageTemplate::kConstructorNotReceiver); MessageTemplate::kConstructorNotReceiver);
// 5. Let S be ? Get(C, @@species). // 5. Let S be ? Get(C, @@species).
Node* const species_symbol = Node* const species_symbol =
a->HeapConstant(isolate->factory()->species_symbol()); HeapConstant(isolate->factory()->species_symbol());
Node* const species = Node* const species =
a->CallStub(getproperty_callable, context, constructor, species_symbol); CallStub(getproperty_callable, context, constructor, species_symbol);
// 6. If S is either undefined or null, return defaultConstructor. // 6. If S is either undefined or null, return defaultConstructor.
a->GotoIf(a->IsUndefined(species), &out); GotoIf(IsUndefined(species), &out);
a->GotoIf(a->WordEqual(species, a->NullConstant()), &out); GotoIf(WordEqual(species, NullConstant()), &out);
// 7. If IsConstructor(S) is true, return S. // 7. If IsConstructor(S) is true, return S.
Label throw_error(a); Label throw_error(this);
Node* species_bitfield = a->LoadMapBitField(a->LoadMap(species)); Node* species_bitfield = LoadMapBitField(LoadMap(species));
a->GotoUnless( GotoUnless(Word32Equal(Word32And(species_bitfield,
a->Word32Equal(a->Word32And(species_bitfield, Int32Constant((1 << Map::kIsConstructor))),
a->Int32Constant((1 << Map::kIsConstructor))), Int32Constant(1 << Map::kIsConstructor)),
a->Int32Constant(1 << Map::kIsConstructor)),
&throw_error); &throw_error);
var_result.Bind(species); var_result.Bind(species);
a->Goto(&out); Goto(&out);
// 8. Throw a TypeError exception. // 8. Throw a TypeError exception.
a->Bind(&throw_error); Bind(&throw_error);
{ {
Node* const message_id = Node* const message_id =
a->SmiConstant(MessageTemplate::kSpeciesNotConstructor); SmiConstant(MessageTemplate::kSpeciesNotConstructor);
a->CallRuntime(Runtime::kThrowTypeError, context, message_id); CallRuntime(Runtime::kThrowTypeError, context, message_id);
a->Goto(&out); Goto(&out);
} }
a->Bind(&out); Bind(&out);
return var_result.value(); return var_result.value();
} }
void AppendPromiseCallback(CodeStubAssembler* a, int offset, void PromiseBuiltinsAssembler::AppendPromiseCallback(int offset, Node* promise,
compiler::Node* promise, compiler::Node* value) { Node* value) {
typedef compiler::Node Node; Node* elements = LoadObjectField(promise, offset);
Node* length = LoadFixedArrayBaseLength(elements);
CodeStubAssembler::ParameterMode mode = OptimalParameterMode();
length = TaggedToParameter(length, mode);
Node* elements = a->LoadObjectField(promise, offset); Node* delta = IntPtrOrSmiConstant(1, mode);
Node* length = a->LoadFixedArrayBaseLength(elements); Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode);
CodeStubAssembler::ParameterMode mode = a->OptimalParameterMode();
length = a->TaggedToParameter(length, mode);
Node* delta = a->IntPtrOrSmiConstant(1, mode);
Node* new_capacity = a->IntPtrOrSmiAdd(length, delta, mode);
const ElementsKind kind = FAST_ELEMENTS; const ElementsKind kind = FAST_ELEMENTS;
const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
...@@ -357,636 +154,779 @@ void AppendPromiseCallback(CodeStubAssembler* a, int offset, ...@@ -357,636 +154,779 @@ void AppendPromiseCallback(CodeStubAssembler* a, int offset,
CodeStubAssembler::kAllowLargeObjectAllocation; CodeStubAssembler::kAllowLargeObjectAllocation;
int additional_offset = 0; int additional_offset = 0;
Node* new_elements = a->AllocateFixedArray(kind, new_capacity, mode, flags); Node* new_elements = AllocateFixedArray(kind, new_capacity, mode, flags);
a->CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode, CopyFixedArrayElements(kind, elements, new_elements, length, barrier_mode,
mode); mode);
a->StoreFixedArrayElement(new_elements, length, value, barrier_mode, StoreFixedArrayElement(new_elements, length, value, barrier_mode,
additional_offset, mode); additional_offset, mode);
a->StoreObjectField(promise, offset, new_elements); StoreObjectField(promise, offset, new_elements);
} }
compiler::Node* InternalPerformPromiseThen(CodeStubAssembler* a, Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(Node* context,
compiler::Node* context, Node* promise,
compiler::Node* promise, Node* on_resolve,
compiler::Node* on_resolve, Node* on_reject,
compiler::Node* on_reject, Node* deferred) {
compiler::Node* deferred) { Node* const native_context = LoadNativeContext(context);
typedef CodeStubAssembler::Variable Variable;
typedef CodeStubAssembler::Label Label; Variable var_on_resolve(this, MachineRepresentation::kTagged),
typedef compiler::Node Node; var_on_reject(this, MachineRepresentation::kTagged);
Isolate* isolate = a->isolate();
Node* const native_context = a->LoadNativeContext(context);
Variable var_on_resolve(a, MachineRepresentation::kTagged),
var_on_reject(a, MachineRepresentation::kTagged);
var_on_resolve.Bind(on_resolve); var_on_resolve.Bind(on_resolve);
var_on_reject.Bind(on_reject); var_on_reject.Bind(on_reject);
Label out(a), if_onresolvenotcallable(a), onrejectcheck(a), Label out(this), if_onresolvenotcallable(this), onrejectcheck(this),
append_callbacks(a); append_callbacks(this);
a->GotoIf(a->TaggedIsSmi(on_resolve), &if_onresolvenotcallable); GotoIf(TaggedIsSmi(on_resolve), &if_onresolvenotcallable);
Node* const on_resolve_map = a->LoadMap(on_resolve); Node* const on_resolve_map = LoadMap(on_resolve);
a->Branch(a->IsCallableMap(on_resolve_map), &onrejectcheck, Branch(IsCallableMap(on_resolve_map), &onrejectcheck,
&if_onresolvenotcallable); &if_onresolvenotcallable);
a->Bind(&if_onresolvenotcallable); Bind(&if_onresolvenotcallable);
{ {
var_on_resolve.Bind(a->LoadContextElement( var_on_resolve.Bind(LoadContextElement(
native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX)); native_context, Context::PROMISE_ID_RESOLVE_HANDLER_INDEX));
a->Goto(&onrejectcheck); Goto(&onrejectcheck);
} }
a->Bind(&onrejectcheck); Bind(&onrejectcheck);
{ {
Label if_onrejectnotcallable(a); Label if_onrejectnotcallable(this);
a->GotoIf(a->TaggedIsSmi(on_reject), &if_onrejectnotcallable); GotoIf(TaggedIsSmi(on_reject), &if_onrejectnotcallable);
Node* const on_reject_map = a->LoadMap(on_reject); Node* const on_reject_map = LoadMap(on_reject);
a->Branch(a->IsCallableMap(on_reject_map), &append_callbacks, Branch(IsCallableMap(on_reject_map), &append_callbacks,
&if_onrejectnotcallable); &if_onrejectnotcallable);
a->Bind(&if_onrejectnotcallable); Bind(&if_onrejectnotcallable);
{ {
var_on_reject.Bind(a->LoadContextElement( var_on_reject.Bind(LoadContextElement(
native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX)); native_context, Context::PROMISE_ID_REJECT_HANDLER_INDEX));
a->Goto(&append_callbacks); Goto(&append_callbacks);
} }
} }
a->Bind(&append_callbacks); Bind(&append_callbacks);
{ {
Label fulfilled_check(a); Label fulfilled_check(this);
Node* const status = a->LoadObjectField(promise, JSPromise::kStatusOffset); Node* const status = LoadObjectField(promise, JSPromise::kStatusOffset);
a->GotoUnless(a->SmiEqual(status, a->SmiConstant(kPromisePending)), GotoUnless(SmiEqual(status, SmiConstant(kPromisePending)),
&fulfilled_check); &fulfilled_check);
Node* const existing_deferred = Node* const existing_deferred =
a->LoadObjectField(promise, JSPromise::kDeferredOffset); LoadObjectField(promise, JSPromise::kDeferredOffset);
Label if_noexistingcallbacks(a), if_existingcallbacks(a); Label if_noexistingcallbacks(this), if_existingcallbacks(this);
a->Branch(a->IsUndefined(existing_deferred), &if_noexistingcallbacks, Branch(IsUndefined(existing_deferred), &if_noexistingcallbacks,
&if_existingcallbacks); &if_existingcallbacks);
a->Bind(&if_noexistingcallbacks); Bind(&if_noexistingcallbacks);
{ {
// Store callbacks directly in the slots. // Store callbacks directly in the slots.
a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferred); StoreObjectField(promise, JSPromise::kDeferredOffset, deferred);
a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, StoreObjectField(promise, JSPromise::kFulfillReactionsOffset,
var_on_resolve.value()); var_on_resolve.value());
a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, StoreObjectField(promise, JSPromise::kRejectReactionsOffset,
var_on_reject.value()); var_on_reject.value());
a->Goto(&out); Goto(&out);
} }
a->Bind(&if_existingcallbacks); Bind(&if_existingcallbacks);
{ {
Label if_singlecallback(a), if_multiplecallbacks(a); Label if_singlecallback(this), if_multiplecallbacks(this);
a->BranchIfJSObject(existing_deferred, &if_singlecallback, BranchIfJSObject(existing_deferred, &if_singlecallback,
&if_multiplecallbacks); &if_multiplecallbacks);
a->Bind(&if_singlecallback); Bind(&if_singlecallback);
{ {
// Create new FixedArrays to store callbacks, and migrate // Create new FixedArrays to store callbacks, and migrate
// existing callbacks. // existing callbacks.
Node* const deferreds = Node* const deferreds =
a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2));
a->StoreFixedArrayElement(deferreds, 0, existing_deferred); StoreFixedArrayElement(deferreds, 0, existing_deferred);
a->StoreFixedArrayElement(deferreds, 1, deferred); StoreFixedArrayElement(deferreds, 1, deferred);
Node* const fulfill_reactions = Node* const fulfill_reactions =
a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2));
a->StoreFixedArrayElement( StoreFixedArrayElement(
fulfill_reactions, 0, fulfill_reactions, 0,
a->LoadObjectField(promise, JSPromise::kFulfillReactionsOffset)); LoadObjectField(promise, JSPromise::kFulfillReactionsOffset));
a->StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value()); StoreFixedArrayElement(fulfill_reactions, 1, var_on_resolve.value());
Node* const reject_reactions = Node* const reject_reactions =
a->AllocateFixedArray(FAST_ELEMENTS, a->IntPtrConstant(2)); AllocateFixedArray(FAST_ELEMENTS, IntPtrConstant(2));
a->StoreFixedArrayElement( StoreFixedArrayElement(
reject_reactions, 0, reject_reactions, 0,
a->LoadObjectField(promise, JSPromise::kRejectReactionsOffset)); LoadObjectField(promise, JSPromise::kRejectReactionsOffset));
a->StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value()); StoreFixedArrayElement(reject_reactions, 1, var_on_reject.value());
// Store new FixedArrays in promise. // Store new FixedArrays in promise.
a->StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds); StoreObjectField(promise, JSPromise::kDeferredOffset, deferreds);
a->StoreObjectField(promise, JSPromise::kFulfillReactionsOffset, StoreObjectField(promise, JSPromise::kFulfillReactionsOffset,
fulfill_reactions); fulfill_reactions);
a->StoreObjectField(promise, JSPromise::kRejectReactionsOffset, StoreObjectField(promise, JSPromise::kRejectReactionsOffset,
reject_reactions); reject_reactions);
a->Goto(&out); Goto(&out);
} }
a->Bind(&if_multiplecallbacks); Bind(&if_multiplecallbacks);
{ {
AppendPromiseCallback(a, JSPromise::kDeferredOffset, promise, deferred); AppendPromiseCallback(JSPromise::kDeferredOffset, promise, deferred);
AppendPromiseCallback(a, JSPromise::kFulfillReactionsOffset, promise, AppendPromiseCallback(JSPromise::kFulfillReactionsOffset, promise,
var_on_resolve.value()); var_on_resolve.value());
AppendPromiseCallback(a, JSPromise::kRejectReactionsOffset, promise, AppendPromiseCallback(JSPromise::kRejectReactionsOffset, promise,
var_on_reject.value()); var_on_reject.value());
a->Goto(&out); Goto(&out);
} }
} }
a->Bind(&fulfilled_check); Bind(&fulfilled_check);
{ {
Label reject(a); Label reject(this);
Node* const result = Node* const result = LoadObjectField(promise, JSPromise::kResultOffset);
a->LoadObjectField(promise, JSPromise::kResultOffset); GotoUnless(WordEqual(status, SmiConstant(kPromiseFulfilled)), &reject);
a->GotoUnless(a->WordEqual(status, a->SmiConstant(kPromiseFulfilled)),
&reject);
// TODO(gsathya): Move this to TF. // TODO(gsathya): Move this to TF.
a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, result,
result, var_on_resolve.value(), deferred, var_on_resolve.value(), deferred,
a->SmiConstant(kPromiseFulfilled)); SmiConstant(kPromiseFulfilled));
a->Goto(&out); Goto(&out);
a->Bind(&reject); Bind(&reject);
{ {
Node* const has_handler = PromiseHasHandler(a, promise); Node* const has_handler = PromiseHasHandler(promise);
Label enqueue(a); Label enqueue(this);
// TODO(gsathya): Fold these runtime calls and move to TF. // TODO(gsathya): Fold these runtime calls and move to TF.
a->GotoIf(has_handler, &enqueue); GotoIf(has_handler, &enqueue);
a->CallRuntime(Runtime::kPromiseRevokeReject, context, promise); CallRuntime(Runtime::kPromiseRevokeReject, context, promise);
a->Goto(&enqueue); Goto(&enqueue);
a->Bind(&enqueue); Bind(&enqueue);
{ {
a->CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise, CallRuntime(Runtime::kEnqueuePromiseReactionJob, context, promise,
result, var_on_reject.value(), deferred, result, var_on_reject.value(), deferred,
a->SmiConstant(kPromiseRejected)); SmiConstant(kPromiseRejected));
a->Goto(&out); Goto(&out);
} }
} }
} }
} }
a->Bind(&out); Bind(&out);
PromiseSetHasHandler(a, promise); PromiseSetHasHandler(promise);
// TODO(gsathya): This call will be removed once we don't have to // TODO(gsathya): This call will be removed once we don't have to
// deal with deferred objects. // deal with deferred objects.
Isolate* isolate = this->isolate();
Callable getproperty_callable = CodeFactory::GetProperty(isolate); Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const key = Node* const key =
a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise")); HeapConstant(isolate->factory()->NewStringFromAsciiChecked("promise"));
Node* const result = Node* const result = CallStub(getproperty_callable, context, deferred, key);
a->CallStub(getproperty_callable, context, deferred, key);
return result; return result;
} }
} // namespace
void Builtins::Generate_PerformPromiseThen(
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
Node* const promise = a.Parameter(1);
Node* const on_resolve = a.Parameter(2);
Node* const on_reject = a.Parameter(3);
Node* const deferred = a.Parameter(4);
Node* const context = a.Parameter(7);
Node* const result = InternalPerformPromiseThen(
&a, context, promise, on_resolve, on_reject, deferred);
// TODO(gsathya): This is unused, but value is returned according to spec.
a.Return(result);
}
void Builtins::Generate_PromiseThen(compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
// 1. Let promise be the this value.
Node* const promise = a.Parameter(0);
Node* const on_resolve = a.Parameter(1);
Node* const on_reject = a.Parameter(2);
Node* const context = a.Parameter(5);
Isolate* isolate = a.isolate();
// 2. If IsPromise(promise) is false, throw a TypeError exception.
a.ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
"Promise.prototype.then");
Node* const native_context = a.LoadNativeContext(context);
Node* const promise_fun =
a.LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Node* constructor =
SpeciesConstructor(&a, isolate, context, promise, promise_fun);
// 4. Let resultCapability be ? NewPromiseCapability(C).
Callable call_callable = CodeFactory::Call(isolate);
Label fast_promise_capability(&a), promise_capability(&a),
perform_promise_then(&a);
Variable var_deferred(&a, MachineRepresentation::kTagged);
a.Branch(a.WordEqual(promise_fun, constructor), &fast_promise_capability,
&promise_capability);
// TODO(gsathya): Remove deferred object and move
// NewPromiseCapbability functions to TF.
a.Bind(&fast_promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const promise_internal_capability = a.LoadContextElement(
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
Node* const capability =
a.CallJS(call_callable, context, promise_internal_capability,
a.UndefinedConstant());
var_deferred.Bind(capability);
a.Goto(&perform_promise_then);
}
a.Bind(&promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const new_promise_capability = a.LoadContextElement(
native_context, Context::NEW_PROMISE_CAPABILITY_INDEX);
Node* const capability =
a.CallJS(call_callable, context, new_promise_capability,
a.UndefinedConstant(), constructor);
var_deferred.Bind(capability);
a.Goto(&perform_promise_then);
}
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
a.Bind(&perform_promise_then);
Node* const result = InternalPerformPromiseThen(
&a, context, promise, on_resolve, on_reject, var_deferred.value());
a.Return(result);
}
namespace {
// Promise fast path implementations rely on unmodified JSPromise instances. // Promise fast path implementations rely on unmodified JSPromise instances.
// We use a fairly coarse granularity for this and simply check whether both // We use a fairly coarse granularity for this and simply check whether both
// the promise itself is unmodified (i.e. its map has not changed) and its // the promise itself is unmodified (i.e. its map has not changed) and its
// prototype is unmodified. // prototype is unmodified.
// TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp // TODO(gsathya): Refactor this out to prevent code dupe with builtins-regexp
void BranchIfFastPath(CodeStubAssembler* a, compiler::Node* context, void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise,
compiler::Node* promise, Label* if_isunmodified,
CodeStubAssembler::Label* if_isunmodified, Label* if_ismodified) {
CodeStubAssembler::Label* if_ismodified) {
typedef compiler::Node Node;
// TODO(gsathya): Assert if promise is receiver // TODO(gsathya): Assert if promise is receiver
Node* const map = a->LoadMap(promise); Node* const map = LoadMap(promise);
Node* const native_context = a->LoadNativeContext(context); Node* const native_context = LoadNativeContext(context);
Node* const promise_fun = Node* const promise_fun =
a->LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX); LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const initial_map = Node* const initial_map =
a->LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset); LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const has_initialmap = a->WordEqual(map, initial_map); Node* const has_initialmap = WordEqual(map, initial_map);
a->GotoUnless(has_initialmap, if_ismodified); GotoUnless(has_initialmap, if_ismodified);
Node* const initial_proto_initial_map = a->LoadContextElement( Node* const initial_proto_initial_map =
native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX); LoadContextElement(native_context, Context::PROMISE_PROTOTYPE_MAP_INDEX);
Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); Node* const proto_map = LoadMap(LoadMapPrototype(map));
Node* const proto_has_initialmap = Node* const proto_has_initialmap =
a->WordEqual(proto_map, initial_proto_initial_map); WordEqual(proto_map, initial_proto_initial_map);
a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
} }
void InternalResolvePromise(CodeStubAssembler* a, compiler::Node* context, void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
compiler::Node* promise, compiler::Node* result, Node* promise,
CodeStubAssembler::Label* out) { Node* result,
typedef CodeStubAssembler::Variable Variable; Label* out) {
typedef CodeStubAssembler::Label Label; Isolate* isolate = this->isolate();
typedef compiler::Node Node;
Isolate* isolate = a->isolate();
Variable var_reason(a, MachineRepresentation::kTagged), Variable var_reason(this, MachineRepresentation::kTagged),
var_then(a, MachineRepresentation::kTagged); var_then(this, MachineRepresentation::kTagged);
Label do_enqueue(a), fulfill(a), if_cycle(a, Label::kDeferred), Label do_enqueue(this), fulfill(this), if_cycle(this, Label::kDeferred),
if_rejectpromise(a, Label::kDeferred); if_rejectpromise(this, Label::kDeferred);
// 6. If SameValue(resolution, promise) is true, then // 6. If SameValue(resolution, promise) is true, then
a->GotoIf(a->SameValue(promise, result, context), &if_cycle); GotoIf(SameValue(promise, result, context), &if_cycle);
// 7. If Type(resolution) is not Object, then // 7. If Type(resolution) is not Object, then
a->GotoIf(a->TaggedIsSmi(result), &fulfill); GotoIf(TaggedIsSmi(result), &fulfill);
a->GotoUnless(a->IsJSReceiver(result), &fulfill); GotoUnless(IsJSReceiver(result), &fulfill);
Label if_nativepromise(a), if_notnativepromise(a, Label::kDeferred); Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
BranchIfFastPath(a, context, result, &if_nativepromise, &if_notnativepromise); BranchIfFastPath(context, result, &if_nativepromise, &if_notnativepromise);
// Resolution is a native promise and if it's already resolved or // Resolution is a native promise and if it's already resolved or
// rejected, shortcircuit the resolution procedure by directly // rejected, shortcircuit the resolution procedure by directly
// reusing the value from the promise. // reusing the value from the promise.
a->Bind(&if_nativepromise); Bind(&if_nativepromise);
{ {
Node* const thenable_status = Node* const thenable_status =
a->LoadObjectField(result, JSPromise::kStatusOffset); LoadObjectField(result, JSPromise::kStatusOffset);
Node* const thenable_value = Node* const thenable_value =
a->LoadObjectField(result, JSPromise::kResultOffset); LoadObjectField(result, JSPromise::kResultOffset);
Label if_isnotpending(a); Label if_isnotpending(this);
a->GotoUnless(a->SmiEqual(a->SmiConstant(kPromisePending), thenable_status), GotoUnless(SmiEqual(SmiConstant(kPromisePending), thenable_status),
&if_isnotpending); &if_isnotpending);
// TODO(gsathya): Use a marker here instead of the actual then // TODO(gsathya): Use a marker here instead of the actual then
// callback, and check for the marker in PromiseResolveThenableJob // callback, and check for the marker in PromiseResolveThenableJob
// and perform PromiseThen. // and perform PromiseThen.
Node* const native_context = a->LoadNativeContext(context); Node* const native_context = LoadNativeContext(context);
Node* const then = Node* const then =
a->LoadContextElement(native_context, Context::PROMISE_THEN_INDEX); LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
var_then.Bind(then); var_then.Bind(then);
a->Goto(&do_enqueue); Goto(&do_enqueue);
a->Bind(&if_isnotpending); Bind(&if_isnotpending);
{ {
Label if_fulfilled(a), if_rejected(a); Label if_fulfilled(this), if_rejected(this);
a->Branch(a->SmiEqual(a->SmiConstant(kPromiseFulfilled), thenable_status), Branch(SmiEqual(SmiConstant(kPromiseFulfilled), thenable_status),
&if_fulfilled, &if_rejected); &if_fulfilled, &if_rejected);
a->Bind(&if_fulfilled); Bind(&if_fulfilled);
{ {
a->CallRuntime(Runtime::kPromiseFulfill, context, promise, CallRuntime(Runtime::kPromiseFulfill, context, promise,
a->SmiConstant(kPromiseFulfilled), thenable_value); SmiConstant(kPromiseFulfilled), thenable_value);
PromiseSetHasHandler(a, promise); PromiseSetHasHandler(promise);
a->Goto(out); Goto(out);
} }
a->Bind(&if_rejected); Bind(&if_rejected);
{ {
Label reject(a); Label reject(this);
Node* const has_handler = PromiseHasHandler(a, result); Node* const has_handler = PromiseHasHandler(result);
// Promise has already been rejected, but had no handler. // Promise has already been rejected, but had no handler.
// Revoke previously triggered reject event. // Revoke previously triggered reject event.
a->GotoIf(has_handler, &reject); GotoIf(has_handler, &reject);
a->CallRuntime(Runtime::kPromiseRevokeReject, context, result); CallRuntime(Runtime::kPromiseRevokeReject, context, result);
a->Goto(&reject); Goto(&reject);
a->Bind(&reject); Bind(&reject);
// Don't cause a debug event as this case is forwarding a rejection // Don't cause a debug event as this case is forwarding a rejection
a->CallRuntime(Runtime::kPromiseReject, context, promise, CallRuntime(Runtime::kPromiseReject, context, promise, thenable_value,
thenable_value, a->FalseConstant()); FalseConstant());
PromiseSetHasHandler(a, result); PromiseSetHasHandler(result);
a->Goto(out); Goto(out);
} }
} }
} }
a->Bind(&if_notnativepromise); Bind(&if_notnativepromise);
{ {
// 8. Let then be Get(resolution, "then"). // 8. Let then be Get(resolution, "then").
Node* const then_str = a->HeapConstant(isolate->factory()->then_string()); Node* const then_str = HeapConstant(isolate->factory()->then_string());
Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const then = Node* const then =
a->CallStub(getproperty_callable, context, result, then_str); CallStub(getproperty_callable, context, result, then_str);
// 9. If then is an abrupt completion, then // 9. If then is an abrupt completion, then
a->GotoIfException(then, &if_rejectpromise, &var_reason); GotoIfException(then, &if_rejectpromise, &var_reason);
// 11. If IsCallable(thenAction) is false, then // 11. If IsCallable(thenAction) is false, then
a->GotoIf(a->TaggedIsSmi(then), &fulfill); GotoIf(TaggedIsSmi(then), &fulfill);
Node* const then_map = a->LoadMap(then); Node* const then_map = LoadMap(then);
a->GotoUnless(a->IsCallableMap(then_map), &fulfill); GotoUnless(IsCallableMap(then_map), &fulfill);
var_then.Bind(then); var_then.Bind(then);
a->Goto(&do_enqueue); Goto(&do_enqueue);
} }
a->Bind(&do_enqueue); Bind(&do_enqueue);
{ {
Label enqueue(a); Label enqueue(this);
a->GotoUnless(a->IsDebugActive(), &enqueue); GotoUnless(IsDebugActive(), &enqueue);
a->GotoIf(a->TaggedIsSmi(result), &enqueue); GotoIf(TaggedIsSmi(result), &enqueue);
a->GotoUnless(a->HasInstanceType(result, JS_PROMISE_TYPE), &enqueue); GotoUnless(HasInstanceType(result, JS_PROMISE_TYPE), &enqueue);
// Mark the dependency of the new promise on the resolution // Mark the dependency of the new promise on the resolution
Node* const key = Node* const key =
a->HeapConstant(isolate->factory()->promise_handled_by_symbol()); HeapConstant(isolate->factory()->promise_handled_by_symbol());
a->CallRuntime(Runtime::kSetProperty, context, result, key, promise, CallRuntime(Runtime::kSetProperty, context, result, key, promise,
a->SmiConstant(STRICT)); SmiConstant(STRICT));
a->Goto(&enqueue); Goto(&enqueue);
// 12. Perform EnqueueJob("PromiseJobs", // 12. Perform EnqueueJob("PromiseJobs",
// PromiseResolveThenableJob, « promise, resolution, thenAction // PromiseResolveThenableJob, « promise, resolution, thenAction
// »). // »).
a->Bind(&enqueue); Bind(&enqueue);
a->CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise, CallRuntime(Runtime::kEnqueuePromiseResolveThenableJob, context, promise,
result, var_then.value()); result, var_then.value());
a->Goto(out); Goto(out);
} }
// 7.b Return FulfillPromise(promise, resolution). // 7.b Return FulfillPromise(promise, resolution).
a->Bind(&fulfill); Bind(&fulfill);
{ {
a->CallRuntime(Runtime::kPromiseFulfill, context, promise, CallRuntime(Runtime::kPromiseFulfill, context, promise,
a->SmiConstant(kPromiseFulfilled), result); SmiConstant(kPromiseFulfilled), result);
a->Goto(out); Goto(out);
} }
a->Bind(&if_cycle); Bind(&if_cycle);
{ {
// 6.a Let selfResolutionError be a newly created TypeError object. // 6.a Let selfResolutionError be a newly created TypeError object.
Node* const message_id = a->SmiConstant(MessageTemplate::kPromiseCyclic); Node* const message_id = SmiConstant(MessageTemplate::kPromiseCyclic);
Node* const error = Node* const error =
a->CallRuntime(Runtime::kNewTypeError, context, message_id, result); CallRuntime(Runtime::kNewTypeError, context, message_id, result);
var_reason.Bind(error); var_reason.Bind(error);
// 6.b Return RejectPromise(promise, selfResolutionError). // 6.b Return RejectPromise(promise, selfResolutionError).
a->Goto(&if_rejectpromise); Goto(&if_rejectpromise);
} }
// 9.a Return RejectPromise(promise, then.[[Value]]). // 9.a Return RejectPromise(promise, then.[[Value]]).
a->Bind(&if_rejectpromise); Bind(&if_rejectpromise);
{
CallRuntime(Runtime::kPromiseReject, context, promise, var_reason.value(),
TrueConstant());
Goto(out);
}
}
// ES#sec-promise-reject-functions
// Promise Reject Functions
BUILTIN(PromiseRejectClosure) {
HandleScope scope(isolate);
Handle<Context> context(isolate->context(), isolate);
if (PromiseUtils::HasAlreadyVisited(context)) {
return isolate->heap()->undefined_value();
}
PromiseUtils::SetAlreadyVisited(context);
Handle<Object> value = args.atOrUndefined(isolate, 1);
Handle<JSObject> promise = handle(PromiseUtils::GetPromise(context), isolate);
Handle<Object> debug_event =
handle(PromiseUtils::GetDebugEvent(context), isolate);
MaybeHandle<Object> maybe_result;
Handle<Object> argv[] = {promise, value, debug_event};
RETURN_FAILURE_ON_EXCEPTION(
isolate, Execution::Call(isolate, isolate->promise_internal_reject(),
isolate->factory()->undefined_value(),
arraysize(argv), argv));
return isolate->heap()->undefined_value();
}
// ES#sec-createresolvingfunctions
// CreateResolvingFunctions ( promise )
BUILTIN(CreateResolvingFunctions) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
Handle<JSObject> promise = args.at<JSObject>(1);
Handle<Object> debug_event = args.at<Object>(2);
Handle<JSFunction> resolve, reject;
PromiseUtils::CreateResolvingFunctions(isolate, promise, debug_event,
&resolve, &reject);
Handle<FixedArray> result = isolate->factory()->NewFixedArray(2);
result->set(0, *resolve);
result->set(1, *reject);
return *isolate->factory()->NewJSArrayWithElements(result, FAST_ELEMENTS, 2,
NOT_TENURED);
}
TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
Node* const executor = Parameter(1);
Node* const new_target = Parameter(2);
Node* const context = Parameter(4);
Isolate* isolate = this->isolate();
Label if_targetisundefined(this, Label::kDeferred);
GotoIf(IsUndefined(new_target), &if_targetisundefined);
Label if_notcallable(this, Label::kDeferred);
GotoIf(TaggedIsSmi(executor), &if_notcallable);
Node* const executor_map = LoadMap(executor);
GotoUnless(IsCallableMap(executor_map), &if_notcallable);
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
Node* const is_debug_active = IsDebugActive();
Label if_targetisnotmodified(this),
if_targetismodified(this, Label::kDeferred), run_executor(this),
debug_push(this, Label::kDeferred), init(this);
Branch(WordEqual(promise_fun, new_target), &if_targetisnotmodified,
&if_targetismodified);
Variable var_result(this, MachineRepresentation::kTagged),
var_reject_call(this, MachineRepresentation::kTagged),
var_reason(this, MachineRepresentation::kTagged);
Bind(&if_targetisnotmodified);
{
Node* const instance = AllocateJSPromise(context);
var_result.Bind(instance);
Goto(&init);
}
Bind(&if_targetismodified);
{
Callable fast_new_object_stub = CodeFactory::FastNewObject(isolate);
Node* const instance =
CallStub(fast_new_object_stub, context, promise_fun, new_target);
var_result.Bind(instance);
Goto(&init);
}
Bind(&init);
{
PromiseInit(var_result.value());
Branch(is_debug_active, &debug_push, &run_executor);
}
Bind(&debug_push);
{
CallRuntime(Runtime::kDebugPushPromise, context, var_result.value());
Goto(&run_executor);
}
Bind(&run_executor);
{
Label out(this), if_rejectpromise(this), debug_pop(this, Label::kDeferred);
// TODO(gsathya): Move this to TF.
Node* const resolving_functions = CallRuntime(
Runtime::kCreateResolvingFunctions, context, var_result.value());
Node* const resolve =
LoadFixedArrayElement(resolving_functions, IntPtrConstant(0));
Node* const reject =
LoadFixedArrayElement(resolving_functions, IntPtrConstant(1));
Callable call_callable = CodeFactory::Call(isolate);
Node* const maybe_exception = CallJS(call_callable, context, executor,
UndefinedConstant(), resolve, reject);
GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
Branch(is_debug_active, &debug_pop, &out);
Bind(&if_rejectpromise);
{
Callable call_callable = CodeFactory::Call(isolate);
CallJS(call_callable, context, reject, UndefinedConstant(),
var_reason.value());
Branch(is_debug_active, &debug_pop, &out);
}
Bind(&debug_pop);
{
CallRuntime(Runtime::kDebugPopPromise, context);
Goto(&out);
}
Bind(&out);
Return(var_result.value());
}
// 1. If NewTarget is undefined, throw a TypeError exception.
Bind(&if_targetisundefined);
{
Node* const message_id = SmiConstant(MessageTemplate::kNotAPromise);
CallRuntime(Runtime::kThrowTypeError, context, message_id, new_target);
Return(UndefinedConstant()); // Never reached.
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
Bind(&if_notcallable);
{ {
a->CallRuntime(Runtime::kPromiseReject, context, promise, Node* const message_id =
var_reason.value(), a->TrueConstant()); SmiConstant(MessageTemplate::kResolverNotAFunction);
a->Goto(out); CallRuntime(Runtime::kThrowTypeError, context, message_id, executor);
Return(UndefinedConstant()); // Never reached.
} }
} }
} // namespace TF_BUILTIN(PromiseInternalConstructor, PromiseBuiltinsAssembler) {
Node* const context = Parameter(3);
Node* const instance = AllocateJSPromise(context);
PromiseInit(instance);
Return(instance);
}
TF_BUILTIN(PromiseCreateAndSet, PromiseBuiltinsAssembler) {
Node* const status = Parameter(1);
Node* const result = Parameter(2);
Node* const context = Parameter(5);
Node* const instance = AllocateJSPromise(context);
PromiseSet(instance, status, result);
Return(instance);
}
TF_BUILTIN(IsPromise, PromiseBuiltinsAssembler) {
Node* const maybe_promise = Parameter(1);
Label if_notpromise(this, Label::kDeferred);
GotoIf(TaggedIsSmi(maybe_promise), &if_notpromise);
Node* const result =
SelectBooleanConstant(HasInstanceType(maybe_promise, JS_PROMISE_TYPE));
Return(result);
Bind(&if_notpromise);
Return(FalseConstant());
}
TF_BUILTIN(PerformPromiseThen, PromiseBuiltinsAssembler) {
Node* const promise = Parameter(1);
Node* const on_resolve = Parameter(2);
Node* const on_reject = Parameter(3);
Node* const deferred = Parameter(4);
Node* const context = Parameter(7);
Node* const result = InternalPerformPromiseThen(context, promise, on_resolve,
on_reject, deferred);
// TODO(gsathya): This is unused, but value is returned according to spec.
Return(result);
}
TF_BUILTIN(PromiseThen, PromiseBuiltinsAssembler) {
// 1. Let promise be the this value.
Node* const promise = Parameter(0);
Node* const on_resolve = Parameter(1);
Node* const on_reject = Parameter(2);
Node* const context = Parameter(5);
Isolate* isolate = this->isolate();
// 2. If IsPromise(promise) is false, throw a TypeError exception.
ThrowIfNotInstanceType(context, promise, JS_PROMISE_TYPE,
"Promise.prototype.then");
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
// 3. Let C be ? SpeciesConstructor(promise, %Promise%).
Node* constructor = SpeciesConstructor(context, promise, promise_fun);
// 4. Let resultCapability be ? NewPromiseCapability(C).
Callable call_callable = CodeFactory::Call(isolate);
Label fast_promise_capability(this), promise_capability(this),
perform_promise_then(this);
Variable var_deferred(this, MachineRepresentation::kTagged);
Branch(WordEqual(promise_fun, constructor), &fast_promise_capability,
&promise_capability);
// TODO(gsathya): Remove deferred object and move
// NewPromiseCapabability functions to TF.
Bind(&fast_promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const promise_internal_capability = LoadContextElement(
native_context, Context::INTERNAL_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, promise_internal_capability,
UndefinedConstant());
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
Bind(&promise_capability);
{
// TODO(gsathya): Move this to TF.
Node* const new_promise_capability = LoadContextElement(
native_context, Context::NEW_PROMISE_CAPABILITY_INDEX);
Node* const capability =
CallJS(call_callable, context, new_promise_capability,
UndefinedConstant(), constructor);
var_deferred.Bind(capability);
Goto(&perform_promise_then);
}
// 5. Return PerformPromiseThen(promise, onFulfilled, onRejected,
// resultCapability).
Bind(&perform_promise_then);
Node* const result = InternalPerformPromiseThen(
context, promise, on_resolve, on_reject, var_deferred.value());
Return(result);
}
// ES#sec-promise-resolve-functions // ES#sec-promise-resolve-functions
// Promise Resolve Functions // Promise Resolve Functions
void Builtins::Generate_PromiseResolveClosure( TF_BUILTIN(PromiseResolveClosure, PromiseBuiltinsAssembler) {
compiler::CodeAssemblerState* state) { Node* const value = Parameter(1);
CodeStubAssembler a(state); Node* const context = Parameter(4);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
Node* const value = a.Parameter(1);
Node* const context = a.Parameter(4);
Label out(&a); Label out(this);
// 3. Let alreadyResolved be F.[[AlreadyResolved]]. // 3. Let alreadyResolved be F.[[AlreadyResolved]].
Node* const has_already_visited_slot = Node* const has_already_visited_slot =
a.IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot); IntPtrConstant(PromiseUtils::kAlreadyVisitedSlot);
Node* const has_already_visited = Node* const has_already_visited =
a.LoadFixedArrayElement(context, has_already_visited_slot); LoadFixedArrayElement(context, has_already_visited_slot);
// 4. If alreadyResolved.[[Value]] is true, return undefined. // 4. If alreadyResolved.[[Value]] is true, return undefined.
a.GotoIf(a.SmiEqual(has_already_visited, a.SmiConstant(1)), &out); GotoIf(SmiEqual(has_already_visited, SmiConstant(1)), &out);
// 5.Set alreadyResolved.[[Value]] to true. // 5.Set alreadyResolved.[[Value]] to true.
a.StoreFixedArrayElement(context, has_already_visited_slot, a.SmiConstant(1)); StoreFixedArrayElement(context, has_already_visited_slot, SmiConstant(1));
// 2. Let promise be F.[[Promise]]. // 2. Let promise be F.[[Promise]].
Node* const promise = a.LoadFixedArrayElement( Node* const promise = LoadFixedArrayElement(
context, a.IntPtrConstant(PromiseUtils::kPromiseSlot)); context, IntPtrConstant(PromiseUtils::kPromiseSlot));
InternalResolvePromise(&a, context, promise, value, &out); InternalResolvePromise(context, promise, value, &out);
a.Bind(&out); Bind(&out);
a.Return(a.UndefinedConstant()); Return(UndefinedConstant());
} }
void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) { TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
CodeStubAssembler a(state); Node* const promise = Parameter(1);
typedef compiler::Node Node; Node* const result = Parameter(2);
typedef CodeStubAssembler::Label Label; Node* const context = Parameter(5);
Node* const promise = a.Parameter(1);
Node* const result = a.Parameter(2);
Node* const context = a.Parameter(5);
Label out(&a); Label out(this);
InternalResolvePromise(&a, context, promise, result, &out); InternalResolvePromise(context, promise, result, &out);
a.Bind(&out); Bind(&out);
a.Return(a.UndefinedConstant()); Return(UndefinedConstant());
} }
void Builtins::Generate_PromiseHandleReject( TF_BUILTIN(PromiseHandleReject, PromiseBuiltinsAssembler) {
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
typedef PromiseHandleRejectDescriptor Descriptor; typedef PromiseHandleRejectDescriptor Descriptor;
Node* const promise = a.Parameter(Descriptor::kPromise); Node* const promise = Parameter(Descriptor::kPromise);
Node* const on_reject = a.Parameter(Descriptor::kOnReject); Node* const on_reject = Parameter(Descriptor::kOnReject);
Node* const exception = a.Parameter(Descriptor::kException); Node* const exception = Parameter(Descriptor::kException);
Node* const context = a.Parameter(Descriptor::kContext); Node* const context = Parameter(Descriptor::kContext);
Isolate* isolate = a.isolate();
Callable call_callable = CodeFactory::Call(isolate); Callable call_callable = CodeFactory::Call(isolate());
Variable var_unused(&a, MachineRepresentation::kTagged); Variable var_unused(this, MachineRepresentation::kTagged);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred); Label if_internalhandler(this), if_customhandler(this, Label::kDeferred);
a.Branch(a.IsUndefined(on_reject), &if_internalhandler, &if_customhandler); Branch(IsUndefined(on_reject), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler); Bind(&if_internalhandler);
{ {
a.CallRuntime(Runtime::kPromiseReject, context, promise, exception, CallRuntime(Runtime::kPromiseReject, context, promise, exception,
a.FalseConstant()); FalseConstant());
a.Return(a.UndefinedConstant()); Return(UndefinedConstant());
} }
a.Bind(&if_customhandler); Bind(&if_customhandler);
{ {
a.CallJS(call_callable, context, on_reject, a.UndefinedConstant(), CallJS(call_callable, context, on_reject, UndefinedConstant(), exception);
exception); Return(UndefinedConstant());
a.Return(a.UndefinedConstant());
} }
} }
void Builtins::Generate_PromiseHandle(compiler::CodeAssemblerState* state) { TF_BUILTIN(PromiseHandle, PromiseBuiltinsAssembler) {
CodeStubAssembler a(state); Node* const value = Parameter(2);
typedef compiler::Node Node; Node* const handler = Parameter(3);
typedef CodeStubAssembler::Label Label; Node* const deferred = Parameter(4);
typedef CodeStubAssembler::Variable Variable; Node* const context = Parameter(7);
Isolate* isolate = this->isolate();
Node* const value = a.Parameter(2);
Node* const handler = a.Parameter(3);
Node* const deferred = a.Parameter(4);
Node* const context = a.Parameter(7);
Isolate* isolate = a.isolate();
// Get promise from deferred // Get promise from deferred
// TODO(gsathya): Remove this lookup by getting rid of the deferred object. // TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Callable getproperty_callable = CodeFactory::GetProperty(isolate); Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const key = a.HeapConstant(isolate->factory()->promise_string()); Node* const key = HeapConstant(isolate->factory()->promise_string());
Node* const deferred_promise = Node* const deferred_promise =
a.CallStub(getproperty_callable, context, deferred, key); CallStub(getproperty_callable, context, deferred, key);
Variable var_reason(&a, MachineRepresentation::kTagged); Variable var_reason(this, MachineRepresentation::kTagged);
Node* const is_debug_active = a.IsDebugActive(); Node* const is_debug_active = IsDebugActive();
Label run_handler(&a), if_rejectpromise(&a), debug_push(&a, Label::kDeferred), Label run_handler(this), if_rejectpromise(this),
debug_pop(&a, Label::kDeferred); debug_push(this, Label::kDeferred), debug_pop(this, Label::kDeferred);
a.Branch(is_debug_active, &debug_push, &run_handler); Branch(is_debug_active, &debug_push, &run_handler);
a.Bind(&debug_push); Bind(&debug_push);
{ {
a.CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise); CallRuntime(Runtime::kDebugPushPromise, context, deferred_promise);
a.Goto(&run_handler); Goto(&run_handler);
} }
a.Bind(&run_handler); Bind(&run_handler);
{ {
Callable call_callable = CodeFactory::Call(isolate); Callable call_callable = CodeFactory::Call(isolate);
Node* const result = Node* const result =
a.CallJS(call_callable, context, handler, a.UndefinedConstant(), value); CallJS(call_callable, context, handler, UndefinedConstant(), value);
a.GotoIfException(result, &if_rejectpromise, &var_reason); GotoIfException(result, &if_rejectpromise, &var_reason);
// TODO(gsathya): Remove this lookup by getting rid of the deferred object. // TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->resolve_string()); Node* const key = HeapConstant(isolate->factory()->resolve_string());
Node* const on_resolve = Node* const on_resolve =
a.CallStub(getproperty_callable, context, deferred, key); CallStub(getproperty_callable, context, deferred, key);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred); Label if_internalhandler(this), if_customhandler(this, Label::kDeferred);
a.Branch(a.IsUndefined(on_resolve), &if_internalhandler, &if_customhandler); Branch(IsUndefined(on_resolve), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler); Bind(&if_internalhandler);
InternalResolvePromise(&a, context, deferred_promise, result, &debug_pop); InternalResolvePromise(context, deferred_promise, result, &debug_pop);
a.Bind(&if_customhandler); Bind(&if_customhandler);
{ {
Node* const maybe_exception = a.CallJS(call_callable, context, on_resolve, Node* const maybe_exception = CallJS(call_callable, context, on_resolve,
a.UndefinedConstant(), result); UndefinedConstant(), result);
a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason); GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
a.Goto(&debug_pop); Goto(&debug_pop);
} }
} }
a.Bind(&if_rejectpromise); Bind(&if_rejectpromise);
{ {
// TODO(gsathya): Remove this lookup by getting rid of the deferred object. // TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->reject_string()); Node* const key = HeapConstant(isolate->factory()->reject_string());
Node* const on_reject = Node* const on_reject =
a.CallStub(getproperty_callable, context, deferred, key); CallStub(getproperty_callable, context, deferred, key);
Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate); Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate);
a.CallStub(promise_handle_reject, context, deferred_promise, on_reject, CallStub(promise_handle_reject, context, deferred_promise, on_reject,
var_reason.value()); var_reason.value());
a.Goto(&debug_pop); Goto(&debug_pop);
} }
a.Bind(&debug_pop); Bind(&debug_pop);
{ {
Label out(&a); Label out(this);
a.GotoUnless(is_debug_active, &out); GotoUnless(is_debug_active, &out);
a.CallRuntime(Runtime::kDebugPopPromise, context); CallRuntime(Runtime::kDebugPopPromise, context);
a.Goto(&out); Goto(&out);
a.Bind(&out); Bind(&out);
a.Return(a.UndefinedConstant()); Return(UndefinedConstant());
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment