| Index: src/builtins/builtins-promise.cc
|
| diff --git a/src/builtins/builtins-promise.cc b/src/builtins/builtins-promise.cc
|
| index 564fbaf54ccbcb1017a812c14ad80dad3875a6d6..06809d6ae7629a83d9e3e0c67b5942b87490603a 100644
|
| --- a/src/builtins/builtins-promise.cc
|
| +++ b/src/builtins/builtins-promise.cc
|
| @@ -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);
|
|
|