Commit 79ae8f17 authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Move Promise.resolve to TF

Add a more low level BranchIfFastPath to take the native_context and
promise_fun as args and change the existing one to use this.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2592933004
Cr-Commit-Position: refs/heads/master@{#42075}
parent e878ea9d
......@@ -1888,6 +1888,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
factory->species_symbol(), Builtins::kReturnReceiver,
true);
SimpleInstallFunction(promise_fun, "resolve", Builtins::kPromiseResolve, 1,
true, DONT_ENUM);
Handle<Map> prototype_map(prototype->map());
Map::SetShouldBeFastPrototypeMap(prototype_map, true, isolate);
......
......@@ -232,7 +232,8 @@ Node* PromiseBuiltinsAssembler::CreatePromiseGetCapabilitiesExecutorContext(
}
Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver(
Node* context, Node* value, MessageTemplate::Template msg_template) {
Node* context, Node* value, MessageTemplate::Template msg_template,
const char* method_name) {
Label out(this), throw_exception(this, Label::kDeferred);
Variable var_value_map(this, MachineRepresentation::kTagged);
......@@ -247,8 +248,13 @@ Node* PromiseBuiltinsAssembler::ThrowIfNotJSReceiver(
// The {value} is not a compatible receiver for this method.
Bind(&throw_exception);
{
Node* const method =
method_name == nullptr
? UndefinedConstant()
: HeapConstant(
isolate()->factory()->NewStringFromAsciiChecked(method_name));
Node* const message_id = SmiConstant(msg_template);
CallRuntime(Runtime::kThrowTypeError, context, message_id);
CallRuntime(Runtime::kThrowTypeError, context, message_id, method);
var_value_map.Bind(UndefinedConstant());
Goto(&out); // Never reached.
}
......@@ -607,11 +613,25 @@ Node* PromiseBuiltinsAssembler::InternalPerformPromiseThen(
void PromiseBuiltinsAssembler::BranchIfFastPath(Node* context, Node* promise,
Label* if_isunmodified,
Label* if_ismodified) {
// TODO(gsathya): Assert if promise is receiver
Node* const map = LoadMap(promise);
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
BranchIfFastPath(native_context, promise_fun, promise, if_isunmodified,
if_ismodified);
}
void PromiseBuiltinsAssembler::BranchIfFastPath(Node* native_context,
Node* promise_fun,
Node* promise,
Label* if_isunmodified,
Label* if_ismodified) {
CSA_ASSERT(this, IsNativeContext(native_context));
CSA_ASSERT(this,
WordEqual(promise_fun,
LoadContextElement(native_context,
Context::PROMISE_FUNCTION_INDEX)));
Node* const map = LoadMap(promise);
Node* const initial_map =
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
Node* const has_initialmap = WordEqual(map, initial_map);
......@@ -652,7 +672,11 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
GotoUnless(IsJSReceiver(result), &fulfill);
Label if_nativepromise(this), if_notnativepromise(this, Label::kDeferred);
BranchIfFastPath(context, result, &if_nativepromise, &if_notnativepromise);
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
BranchIfFastPath(native_context, promise_fun, result, &if_nativepromise,
&if_notnativepromise);
// Resolution is a native promise and if it's already resolved or
// rejected, shortcircuit the resolution procedure by directly
......@@ -671,7 +695,6 @@ void PromiseBuiltinsAssembler::InternalResolvePromise(Node* context,
// TODO(gsathya): Use a marker here instead of the actual then
// callback, and check for the marker in PromiseResolveThenableJob
// and perform PromiseThen.
Node* const native_context = LoadNativeContext(context);
Node* const then =
LoadContextElement(native_context, Context::PROMISE_THEN_INDEX);
var_then.Bind(then);
......@@ -1207,6 +1230,95 @@ TF_BUILTIN(PromiseCatch, PromiseBuiltinsAssembler) {
}
}
TF_BUILTIN(PromiseResolve, PromiseBuiltinsAssembler) {
// 1. Let C be the this value.
Node* receiver = Parameter(0);
Node* value = Parameter(1);
Node* context = Parameter(4);
Isolate* isolate = this->isolate();
// 2. If Type(C) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(context, receiver, MessageTemplate::kCalledOnNonObject,
"PromiseResolve");
Label if_valueisnativepromise(this), if_valueisnotnativepromise(this),
if_valueisnotpromise(this);
// 3.If IsPromise(x) is true, then
GotoIf(TaggedIsSmi(value), &if_valueisnotpromise);
// This shortcircuits the constructor lookups.
GotoUnless(HasInstanceType(value, JS_PROMISE_TYPE), &if_valueisnotpromise);
// This adds a fast path as non-subclassed native promises don't have
// an observable constructor lookup.
Node* const native_context = LoadNativeContext(context);
Node* const promise_fun =
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
BranchIfFastPath(native_context, promise_fun, value, &if_valueisnativepromise,
&if_valueisnotnativepromise);
Bind(&if_valueisnativepromise);
{
GotoUnless(WordEqual(promise_fun, receiver), &if_valueisnotnativepromise);
Return(value);
}
// At this point, value or/and receiver are not native promises, but
// they could be of the same subclass.
Bind(&if_valueisnotnativepromise);
{
// 3.a Let xConstructor be ? Get(x, "constructor").
// The constructor lookup is observable.
Node* const constructor_str =
HeapConstant(isolate->factory()->constructor_string());
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const constructor =
CallStub(getproperty_callable, context, value, constructor_str);
// 3.b If SameValue(xConstructor, C) is true, return x.
GotoUnless(SameValue(constructor, receiver, context),
&if_valueisnotpromise);
Return(value);
}
Bind(&if_valueisnotpromise);
{
Label if_nativepromise(this), if_notnativepromise(this);
BranchIfFastPath(context, receiver, &if_nativepromise,
&if_notnativepromise);
// This adds a fast path for native promises that don't need to
// create NewPromiseCapability.
Bind(&if_nativepromise);
{
Label do_resolve(this);
Node* const result = AllocateAndInitJSPromise(context);
InternalResolvePromise(context, result, value);
Return(result);
}
Bind(&if_notnativepromise);
{
// 4. Let promiseCapability be ? NewPromiseCapability(C).
Node* const capability = NewPromiseCapability(context, receiver);
// 5. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
Callable call_callable = CodeFactory::Call(isolate);
Node* const resolve =
LoadObjectField(capability, JSPromiseCapability::kResolveOffset);
CallJS(call_callable, context, resolve, UndefinedConstant(), value);
// 6. Return promiseCapability.[[Promise]].
Node* const result =
LoadObjectField(capability, JSPromiseCapability::kPromiseOffset);
Return(result);
}
}
}
TF_BUILTIN(PromiseGetCapabilitiesExecutor, PromiseBuiltinsAssembler) {
Node* const resolve = Parameter(1);
Node* const reject = Parameter(2);
......
......@@ -31,7 +31,8 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
Node* AllocateAndSetJSPromise(Node* context, Node* status, Node* result);
Node* ThrowIfNotJSReceiver(Node* context, Node* value,
MessageTemplate::Template msg_template);
MessageTemplate::Template msg_template,
const char* method_name = nullptr);
Node* SpeciesConstructor(Node* context, Node* object,
Node* default_constructor);
......@@ -57,6 +58,9 @@ class PromiseBuiltinsAssembler : public CodeStubAssembler {
void BranchIfFastPath(Node* context, Node* promise, Label* if_isunmodified,
Label* if_ismodified);
void BranchIfFastPath(Node* native_context, Node* promise_fun, Node* promise,
Label* if_isunmodified, Label* if_ismodified);
Node* CreatePromiseContext(Node* native_context, int slots);
Node* CreatePromiseResolvingFunctionsContext(Node* promise, Node* debug_event,
Node* native_context);
......
......@@ -634,6 +634,7 @@ namespace internal {
TFJ(ResolvePromise, 2) \
TFS(PromiseHandleReject, BUILTIN, kNoExtraICState, PromiseHandleReject) \
TFJ(PromiseHandle, 6) \
TFJ(PromiseResolve, 1) \
\
/* Proxy */ \
CPP(ProxyConstructor) \
......
......@@ -68,27 +68,6 @@ function PromiseReject(r) {
// Combinators.
// ES#sec-promise.resolve
// Promise.resolve ( x )
function PromiseResolve(x) {
if (!IS_RECEIVER(this)) {
throw %make_type_error(kCalledOnNonObject, PromiseResolve);
}
if (%is_promise(x) && x.constructor === this) return x;
// Avoid creating resolving functions.
if (this === GlobalPromise) {
var promise = %promise_internal_constructor(UNDEFINED);
%promise_resolve(promise, x);
return promise;
}
// debugEvent is not so meaningful here as it will be resolved
var promiseCapability = %new_promise_capability(this, true);
%_Call(promiseCapability.resolve, UNDEFINED, x);
return promiseCapability.promise;
}
// ES#sec-promise.all
// Promise.all ( iterable )
function PromiseAll(iterable) {
......@@ -198,7 +177,6 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
"reject", PromiseReject,
"all", PromiseAll,
"race", PromiseRace,
"resolve", PromiseResolve
]);
%InstallToContext([
......
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