Commit 11359e33 authored by gsathya's avatar gsathya Committed by Commit bot

[promises] Port ResolvePromise to TF

-- Moves promiseHasHandlerSymbol to inobject property
-- Ports PromiseResolveClosure to TF
-- Fix a non spec async-await test which fails now because we do a map
check for native promise check (instead of IsPromise). Changing the
constructor (in the test) invalidates the map check.

This patch results in a 7.1% performance improvement in the bluebird
benchmark (over 5 runs).

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2541283002
Cr-Commit-Position: refs/heads/master@{#41569}
parent 21c9d278
......@@ -7238,8 +7238,11 @@ bool Promise::HasHandler() {
i::Isolate* isolate = promise->GetIsolate();
LOG_API(isolate, Promise, HasRejectHandler);
ENTER_V8(isolate);
i::Handle<i::Symbol> key = isolate->factory()->promise_has_handler_symbol();
return i::JSReceiver::GetDataProperty(promise, key)->IsTrue(isolate);
if (promise->IsJSPromise()) {
i::Handle<i::JSPromise> js_promise = i::Handle<i::JSPromise>::cast(promise);
return js_promise->has_handler();
}
return false;
}
......
......@@ -1855,9 +1855,25 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
prototype, factory->to_string_tag_symbol(), factory->Promise_string(),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
SimpleInstallFunction(prototype, "then", Builtins::kPromiseThen, 2, true,
Handle<JSFunction> promise_then =
SimpleCreateFunction(isolate, isolate->factory()->then_string(),
Builtins::kPromiseThen, 2, true);
JSObject::AddProperty(prototype, isolate->factory()->then_string(),
promise_then, DONT_ENUM);
InstallWithIntrinsicDefaultProto(isolate, promise_then,
Context::PROMISE_THEN_INDEX);
// TODO(gsathya): Move to TF
SimpleInstallFunction(prototype, "catch", Builtins::kIllegal, 1, true,
DONT_ENUM);
Handle<Map> prototype_map(prototype->map());
Map::SetShouldBeFastPrototypeMap(prototype_map, true, isolate);
// Store the initial Promise.prototype map. This is used in fast-path
// checks. Do not alter the prototype after this point.
native_context()->set_promise_prototype_map(*prototype_map);
{ // Internal: PromiseInternalConstructor
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
......@@ -1889,6 +1905,14 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Context::PERFORM_PROMISE_THEN_INDEX);
}
{ // Internal: ResolvePromise
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kResolvePromise, 2, false);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::PROMISE_RESOLVE_INDEX);
}
{
Handle<Code> code =
handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),
......@@ -3523,6 +3547,9 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
InstallInternalArray(extras_utils, "InternalPackedArray", FAST_ELEMENTS);
InstallFunction(extras_utils, isolate()->promise_resolve(),
factory()->NewStringFromAsciiChecked("resolvePromise"));
int builtin_index = Natives::GetDebuggerCount();
// Only run prologue.js and runtime.js at this point.
DCHECK_EQ(builtin_index, Natives::GetIndex("prologue"));
......
This diff is collapsed.
......@@ -565,11 +565,12 @@ namespace internal {
TFJ(PromiseInternalConstructor, 0) \
TFJ(IsPromise, 1) \
CPP(CreateResolvingFunctions) \
CPP(PromiseResolveClosure) \
TFJ(PromiseResolveClosure, 1) \
CPP(PromiseRejectClosure) \
TFJ(PromiseThen, 2) \
TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \
TFJ(ResolvePromise, 2) \
\
/* Proxy */ \
CPP(ProxyConstructor) \
......
......@@ -65,7 +65,9 @@ enum ContextLookupFlags {
promise_internal_constructor) \
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set)
V(PROMISE_CREATE_AND_SET_INDEX, JSFunction, promise_create_and_set) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
......@@ -105,10 +107,8 @@ enum ContextLookupFlags {
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
V(PROMISE_REJECT_INDEX, JSFunction, promise_reject) \
V(PROMISE_INTERNAL_REJECT_INDEX, JSFunction, promise_internal_reject) \
V(PROMISE_RESOLVE_INDEX, JSFunction, promise_resolve) \
V(PROMISE_ID_RESOLVE_HANDLER_INDEX, JSFunction, promise_id_resolve_handler) \
V(PROMISE_ID_REJECT_HANDLER_INDEX, JSFunction, promise_id_reject_handler) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(NEW_PROMISE_CAPABILITY_INDEX, JSFunction, new_promise_capability) \
V(INTERNAL_PROMISE_CAPABILITY_INDEX, JSFunction, \
internal_promise_capability) \
......@@ -291,6 +291,7 @@ enum ContextLookupFlags {
V(PROMISE_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
promise_resolve_shared_fun) \
V(PROMISE_REJECT_SHARED_FUN, SharedFunctionInfo, promise_reject_shared_fun) \
V(PROMISE_PROTOTYPE_MAP_INDEX, Map, promise_prototype_map) \
V(REGEXP_EXEC_FUNCTION_INDEX, JSFunction, regexp_exec_function) \
V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \
V(REGEXP_LAST_MATCH_INFO_INDEX, RegExpMatchInfo, regexp_last_match_info) \
......
......@@ -150,6 +150,7 @@
V(symbol_string, "symbol") \
V(Symbol_string, "Symbol") \
V(SyntaxError_string, "SyntaxError") \
V(then_string, "then") \
V(this_string, "this") \
V(throw_string, "throw") \
V(timed_out, "timed-out") \
......@@ -213,7 +214,6 @@
V(promise_forwarding_handler_symbol) \
V(promise_handled_by_symbol) \
V(promise_handled_hint_symbol) \
V(promise_has_handler_symbol) \
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
......
......@@ -17,7 +17,6 @@ var CreateInternalPromiseCapability;
var PromiseCreate;
var PromiseNextMicrotaskID;
var RejectPromise;
var ResolvePromise;
utils.Import(function(from) {
AsyncFunctionNext = from.AsyncFunctionNext;
......@@ -25,7 +24,6 @@ utils.Import(function(from) {
CreateInternalPromiseCapability = from.CreateInternalPromiseCapability;
PromiseCreate = from.PromiseCreate;
RejectPromise = from.RejectPromise;
ResolvePromise = from.ResolvePromise;
});
var promiseAsyncStackIDSymbol =
......@@ -36,8 +34,6 @@ var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
var promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol");
var promiseHasHandlerSymbol =
utils.ImportNow("promise_has_handler_symbol");
// -------------------------------------------------------------------
......@@ -47,7 +43,7 @@ function PromiseCastResolved(value) {
return value;
} else {
var promise = PromiseCreate();
ResolvePromise(promise, value);
%promise_resolve(promise, value);
return promise;
}
}
......@@ -90,7 +86,7 @@ function AsyncFunctionAwait(generator, awaited, outerPromise) {
// The Promise will be thrown away and not handled, but it shouldn't trigger
// unhandled reject events as its work is done
SET_PRIVATE(throwawayCapability.promise, promiseHasHandlerSymbol, true);
%PromiseMarkAsHandled(throwawayCapability.promise);
if (DEBUG_IS_ACTIVE) {
if (%is_promise(awaited)) {
......
......@@ -18,8 +18,6 @@ var promiseHandledBySymbol =
utils.ImportNow("promise_handled_by_symbol");
var promiseForwardingHandlerSymbol =
utils.ImportNow("promise_forwarding_handler_symbol");
var promiseHasHandlerSymbol =
utils.ImportNow("promise_has_handler_symbol");
var promiseHandledHintSymbol =
utils.ImportNow("promise_handled_hint_symbol");
var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
......@@ -47,7 +45,7 @@ function PromiseHandle(value, handler, deferred) {
if (debug_is_active) %DebugPushPromise(deferred.promise);
var result = handler(value);
if (IS_UNDEFINED(deferred.resolve)) {
ResolvePromise(deferred.promise, result);
%promise_resolve(deferred.promise, result);
} else {
%_Call(deferred.resolve, UNDEFINED, result);
}
......@@ -106,61 +104,6 @@ function PromiseCreate() {
return %promise_internal_constructor();
}
// ES#sec-promise-resolve-functions
// Promise Resolve Functions, steps 6-13
function ResolvePromise(promise, resolution) {
if (resolution === promise) {
var exception = %make_type_error(kPromiseCyclic, resolution);
%PromiseReject(promise, exception, true);
return;
}
if (IS_RECEIVER(resolution)) {
// 25.4.1.3.2 steps 8-12
try {
var then = resolution.then;
} catch (e) {
%PromiseReject(promise, e, true);
return;
}
// Resolution is a native promise and if it's already resolved or
// rejected, shortcircuit the resolution procedure by directly
// reusing the value from the promise.
if (%is_promise(resolution) && then === PromiseThen) {
var thenableState = %PromiseStatus(resolution);
if (thenableState === kFulfilled) {
// This goes inside the if-else to save one symbol lookup in
// the slow path.
var thenableValue = %PromiseResult(resolution);
%PromiseFulfill(promise, kFulfilled, thenableValue);
SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
return;
} else if (thenableState === kRejected) {
var thenableValue = %PromiseResult(resolution);
if (!HAS_DEFINED_PRIVATE(resolution, promiseHasHandlerSymbol)) {
// Promise has already been rejected, but had no handler.
// Revoke previously triggered reject event.
%PromiseRevokeReject(resolution);
}
// Don't cause a debug event as this case is forwarding a rejection
%PromiseReject(promise, thenableValue, false);
SET_PRIVATE(resolution, promiseHasHandlerSymbol, true);
return;
}
}
if (IS_CALLABLE(then)) {
if (DEBUG_IS_ACTIVE && %is_promise(resolution)) {
// Mark the dependency of the new promise on the resolution
SET_PRIVATE(resolution, promiseHandledBySymbol, promise);
}
%EnqueuePromiseResolveThenableJob(promise, resolution, then);
return;
}
}
%PromiseFulfill(promise, kFulfilled, resolution);
}
// Only used by async-await.js
function RejectPromise(promise, reason, debugEvent) {
%PromiseReject(promise, reason, debugEvent);
......@@ -251,7 +194,7 @@ function PromiseResolve(x) {
// Avoid creating resolving functions.
if (this === GlobalPromise) {
var promise = %promise_internal_constructor();
ResolvePromise(promise, x);
%promise_resolve(promise, x);
return promise;
}
......@@ -422,7 +365,7 @@ function PromiseHasUserDefinedRejectHandler() {
};
function MarkPromiseAsHandled(promise) {
SET_PRIVATE(promise, promiseHasHandlerSymbol, true);
%PromiseMarkAsHandled(promise);
}
......@@ -442,9 +385,7 @@ utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
"catch", PromiseCatch
]);
%SetCode(GlobalPromise.prototype.catch, PromiseCatch);
%InstallToContext([
"promise_catch", PromiseCatch,
......@@ -453,8 +394,6 @@ utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
"promise_reject", DoRejectPromise,
// TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise,
"promise_resolve", ResolvePromise,
"promise_then", PromiseThen,
"promise_handle", PromiseHandle,
"promise_debug_get_info", PromiseDebugGetInfo,
"new_promise_capability", NewPromiseCapability,
......@@ -468,7 +407,6 @@ utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
// promise without having to hold on to those closures forever.
utils.InstallFunctions(extrasUtils, 0, [
"createPromise", PromiseCreate,
"resolvePromise", ResolvePromise,
"rejectPromise", DoRejectPromise,
"markPromiseAsHandled", MarkPromiseAsHandled
]);
......@@ -478,7 +416,6 @@ utils.Export(function(to) {
to.PromiseThen = PromiseThen;
to.CreateInternalPromiseCapability = CreateInternalPromiseCapability;
to.ResolvePromise = ResolvePromise;
to.RejectPromise = RejectPromise;
});
......
......@@ -7100,6 +7100,8 @@ ACCESSORS(JSPromise, result, Object, kResultOffset)
ACCESSORS(JSPromise, deferred, Object, kDeferredOffset)
ACCESSORS(JSPromise, fulfill_reactions, Object, kFulfillReactionsOffset)
ACCESSORS(JSPromise, reject_reactions, Object, kRejectReactionsOffset)
SMI_ACCESSORS(JSPromise, flags, kFlagsOffset)
BOOL_ACCESSORS(JSPromise, flags, has_handler, kHasHandlerBit)
ACCESSORS(JSRegExp, data, Object, kDataOffset)
ACCESSORS(JSRegExp, flags, Object, kFlagsOffset)
......
......@@ -551,6 +551,7 @@ void JSPromise::JSPromisePrint(std::ostream& os) { // NOLINT
os << "\n - deferreds = " << Brief(deferred());
os << "\n - fulfill_reactions = " << Brief(fulfill_reactions());
os << "\n - reject_reactions = " << Brief(reject_reactions());
os << "\n - has_handler = " << has_handler();
}
void JSRegExp::JSRegExpPrint(std::ostream& os) { // NOLINT
......
......@@ -8910,6 +8910,11 @@ class JSPromise : public JSObject {
DECL_ACCESSORS(fulfill_reactions, Object)
DECL_ACCESSORS(reject_reactions, Object)
DECL_INT_ACCESSORS(flags)
// [has_handler]: Whether this promise has a reject handler or not.
DECL_BOOLEAN_ACCESSORS(has_handler)
static const char* Status(int status);
DECLARE_CAST(JSPromise)
......@@ -8925,7 +8930,11 @@ class JSPromise : public JSObject {
static const int kFulfillReactionsOffset = kDeferredOffset + kPointerSize;
static const int kRejectReactionsOffset =
kFulfillReactionsOffset + kPointerSize;
static const int kSize = kRejectReactionsOffset + kPointerSize;
static const int kFlagsOffset = kRejectReactionsOffset + kPointerSize;
static const int kSize = kFlagsOffset + kPointerSize;
// Flags layout.
static const int kHasHandlerBit = 0;
};
// Regular expressions
......
......@@ -11,13 +11,6 @@
namespace v8 {
namespace internal {
enum PromiseResolvingFunctionContextSlot {
kAlreadyVisitedSlot = Context::MIN_CONTEXT_SLOTS,
kPromiseSlot,
kDebugEventSlot,
kPromiseContextLength,
};
JSObject* PromiseUtils::GetPromise(Handle<Context> context) {
return JSObject::cast(context->get(kPromiseSlot));
}
......
......@@ -5,6 +5,7 @@
#ifndef V8_PROMISE_UTILS_H_
#define V8_PROMISE_UTILS_H_
#include "src/contexts.h"
#include "src/objects.h"
namespace v8 {
......@@ -13,6 +14,19 @@ namespace internal {
// Helper methods for Promise builtins.
class PromiseUtils : public AllStatic {
public:
enum PromiseResolvingFunctionContextSlot {
// Whether the resolve/reject callback was already called.
kAlreadyVisitedSlot = Context::MIN_CONTEXT_SLOTS,
// The promise which resolve/reject callbacks fulfill.
kPromiseSlot,
// Whether to trigger a debug event or not. Used in catch
// prediction.
kDebugEventSlot,
kPromiseContextLength,
};
// These get and set the slots on the PromiseResolvingContext, which
// is used by the resolve/reject promise callbacks.
static JSObject* GetPromise(Handle<Context> context);
......
......@@ -12,15 +12,15 @@ namespace internal {
namespace {
void PromiseRejectEvent(Isolate* isolate, Handle<JSReceiver> promise,
void PromiseRejectEvent(Isolate* isolate, Handle<JSPromise> promise,
Handle<Object> rejected_promise, Handle<Object> value,
bool debug_event) {
if (isolate->debug()->is_active() && debug_event) {
isolate->debug()->OnPromiseReject(rejected_promise, value);
}
Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol();
// Do not report if we actually have a handler.
if (JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate)) {
// Report only if we don't actually have a handler.
if (!promise->has_handler()) {
isolate->ReportPromiseReject(Handle<JSObject>::cast(promise), value,
v8::kPromiseRejectWithNoHandler);
}
......@@ -31,7 +31,7 @@ void PromiseRejectEvent(Isolate* isolate, Handle<JSReceiver> promise,
RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
DCHECK(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
Handle<Object> rejected_promise = promise;
......@@ -48,10 +48,9 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectEventFromStack) {
RUNTIME_FUNCTION(Runtime_PromiseRevokeReject) {
DCHECK(args.length() == 1);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
Handle<Symbol> key = isolate->factory()->promise_has_handler_symbol();
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
// At this point, no revocation has been issued before
CHECK(JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate));
CHECK(!promise->has_handler());
isolate->ReportPromiseReject(promise, Handle<Object>(),
v8::kPromiseHandlerAddedAfterReject);
return isolate->heap()->undefined_value();
......@@ -275,5 +274,14 @@ RUNTIME_FUNCTION(Runtime_PromiseRejectReactions) {
Handle<FixedArray>::cast(reject_reactions));
}
RUNTIME_FUNCTION(Runtime_PromiseMarkAsHandled) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
promise->set_has_handler(true);
return isolate->heap()->undefined_value();
}
} // namespace internal
} // namespace v8
......@@ -309,6 +309,7 @@ namespace internal {
F(PromiseDeferred, 1, 1) \
F(PromiseReject, 3, 1) \
F(PromiseFulfill, 3, 1) \
F(PromiseMarkAsHandled, 1, 1) \
F(PromiseRejectEventFromStack, 2, 1) \
F(PromiseRejectReactions, 1, 1) \
F(PromiseRevokeReject, 1, 1) \
......
......@@ -79,7 +79,7 @@ bytecodes: [
B(Star), R(0),
B(CreateArrayLiteral), U8(0), U8(0), U8(9),
B(Star), R(1),
B(CallJSRuntime), U8(156), R(0), U8(2),
B(CallJSRuntime), U8(157), R(0), U8(2),
/* 44 S> */ B(Return),
]
constant pool: [
......
......@@ -126,14 +126,14 @@ bytecodes: [
B(LdaUndefined),
B(Star), R(11),
B(Mov), R(2), R(12),
/* 152 E> */ B(CallJSRuntime), U8(156), R(11), U8(2),
/* 152 E> */ B(CallJSRuntime), U8(157), R(11), U8(2),
B(Star), R(9),
B(CreateArrayLiteral), U8(1), U8(1), U8(9),
B(Star), R(10),
B(CallJSRuntime), U8(155), R(7), U8(4),
B(CallJSRuntime), U8(156), R(7), U8(4),
B(Star), R(5),
B(Mov), R(0), R(6),
/* 140 E> */ B(CallJSRuntime), U8(152), R(3), U8(4),
/* 140 E> */ B(CallJSRuntime), U8(153), R(3), U8(4),
B(Star), R(3),
B(Ldar), R(this),
B(JumpIfNotHole), U8(4),
......
......@@ -24,4 +24,4 @@ async function bar() {
foo();
bar();
%RunMicrotasks();
assertEquals(0, count);
assertEquals(1, count);
......@@ -49,7 +49,7 @@ function getStack(error) {
map(line => line.replace(/^\s*at (@?[a-zA-Z0-9_\.\[\]]+)(.*)/, "$1"));
// remove `Promise.then()` invocation by assertEqualsAsync()
if (stack[2] === "assertEqualsAsync") return [];
if (stack[1] === "assertEqualsAsync") return [];
return stack.reverse();
}
......@@ -96,6 +96,6 @@ assertEqualsAsync(
}),
"should call Promise[@@Species] after non-internal Then");
assertEquals([
"@@Species: [@testThenOnReturnedPromise > Promise.then > FakePromise]",
"@@Species: [@testThenOnReturnedPromise > FakePromise]",
"Then: foo"
], log);
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