Commit 9fc3c017 authored by gsathya's avatar gsathya Committed by Commit bot

PromiseHandle port to TF

Splits PromiseHandle into two TF builtins to account for catch
prediction. An exception in PromiseHandleReject builtin results in a
"caught" prediction whereas an expception in PromiseHandle results in a
"promise rejection" prediction.

An extra is_exception_caught bit is added to Code to mark this catch
prediction behavior.

BUG=v8:5343

Review-Url: https://codereview.chromium.org/2572623002
Cr-Commit-Position: refs/heads/master@{#41683}
parent 3b1a09f5
......@@ -1926,6 +1926,27 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Context::PROMISE_RESOLVE_INDEX);
}
{ // Internal: PromiseHandle
Handle<JSFunction> function = SimpleCreateFunction(
isolate, factory->empty_string(), Builtins::kPromiseHandle, 3, true);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::PROMISE_HANDLE_INDEX);
// Set up catch prediction
Handle<Code> promise_handle = isolate->builtins()->PromiseHandle();
promise_handle->set_is_promise_rejection(true);
}
{ // Internal: PromiseHandleReject
Handle<JSFunction> function =
SimpleCreateFunction(isolate, factory->empty_string(),
Builtins::kPromiseHandleReject, 3, false);
InstallWithIntrinsicDefaultProto(isolate, function,
Context::PROMISE_HANDLE_REJECT_INDEX);
// Set up catch prediction
Handle<Code> promise_handle = isolate->builtins()->PromiseHandleReject();
promise_handle->set_is_exception_caught(true);
}
{
Handle<Code> code =
handle(isolate->builtins()->builtin(Builtins::kPromiseResolveClosure),
......
......@@ -867,5 +867,127 @@ void Builtins::Generate_ResolvePromise(compiler::CodeAssemblerState* state) {
a.Return(a.UndefinedConstant());
}
void Builtins::Generate_PromiseHandleReject(
compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
typedef PromiseHandleRejectDescriptor Descriptor;
Node* const promise = a.Parameter(Descriptor::kPromise);
Node* const on_reject = a.Parameter(Descriptor::kOnReject);
Node* const exception = a.Parameter(Descriptor::kException);
Node* const context = a.Parameter(Descriptor::kContext);
Isolate* isolate = a.isolate();
Callable call_callable = CodeFactory::Call(isolate);
Variable var_unused(&a, MachineRepresentation::kTagged);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred);
a.Branch(a.IsUndefined(on_reject), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler);
{
a.CallRuntime(Runtime::kPromiseReject, context, promise, exception,
a.FalseConstant());
a.Return(a.UndefinedConstant());
}
a.Bind(&if_customhandler);
{
a.CallJS(call_callable, context, on_reject, a.UndefinedConstant(),
exception);
a.Return(a.UndefinedConstant());
}
}
void Builtins::Generate_PromiseHandle(compiler::CodeAssemblerState* state) {
CodeStubAssembler a(state);
typedef compiler::Node Node;
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Node* const value = a.Parameter(1);
Node* const handler = a.Parameter(2);
Node* const deferred = a.Parameter(3);
Node* const context = a.Parameter(6);
Isolate* isolate = a.isolate();
// Get promise from deferred
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Callable getproperty_callable = CodeFactory::GetProperty(isolate);
Node* const key = a.HeapConstant(isolate->factory()->promise_string());
Node* const promise =
a.CallStub(getproperty_callable, context, deferred, key);
Variable var_reason(&a, MachineRepresentation::kTagged);
Node* const is_debug_active = a.IsDebugActive();
Label run_handler(&a), if_rejectpromise(&a), debug_push(&a, Label::kDeferred),
debug_pop(&a, Label::kDeferred);
a.Branch(is_debug_active, &debug_push, &run_handler);
a.Bind(&debug_push);
{
a.CallRuntime(Runtime::kDebugPushPromise, context, promise);
a.Goto(&run_handler);
}
a.Bind(&run_handler);
{
Callable call_callable = CodeFactory::Call(isolate);
Node* const result =
a.CallJS(call_callable, context, handler, a.UndefinedConstant(), value);
a.GotoIfException(result, &if_rejectpromise, &var_reason);
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->resolve_string());
Node* const on_resolve =
a.CallStub(getproperty_callable, context, deferred, key);
Label if_internalhandler(&a), if_customhandler(&a, Label::kDeferred);
a.Branch(a.IsUndefined(on_resolve), &if_internalhandler, &if_customhandler);
a.Bind(&if_internalhandler);
InternalResolvePromise(&a, context, promise, result, &debug_pop);
a.Bind(&if_customhandler);
{
Node* const maybe_exception = a.CallJS(call_callable, context, on_resolve,
a.UndefinedConstant(), result);
a.GotoIfException(maybe_exception, &if_rejectpromise, &var_reason);
a.Goto(&debug_pop);
}
}
a.Bind(&if_rejectpromise);
{
// TODO(gsathya): Remove this lookup by getting rid of the deferred object.
Node* const key = a.HeapConstant(isolate->factory()->reject_string());
Node* const on_reject =
a.CallStub(getproperty_callable, context, deferred, key);
Callable promise_handle_reject = CodeFactory::PromiseHandleReject(isolate);
a.CallStub(promise_handle_reject, context, promise, on_reject,
var_reason.value());
a.Goto(&debug_pop);
}
a.Bind(&debug_pop);
{
Label out(&a);
a.GotoUnless(is_debug_active, &out);
a.CallRuntime(Runtime::kDebugPopPromise, context);
a.Goto(&out);
a.Bind(&out);
a.Return(a.UndefinedConstant());
}
}
} // namespace internal
} // namespace v8
......@@ -575,6 +575,8 @@ namespace internal {
TFJ(PromiseCreateAndSet, 2) \
TFJ(PerformPromiseThen, 4) \
TFJ(ResolvePromise, 2) \
TFS(PromiseHandleReject, BUILTIN, kNoExtraICState, PromiseHandleReject) \
TFJ(PromiseHandle, 3) \
\
/* Proxy */ \
CPP(ProxyConstructor) \
......
......@@ -244,6 +244,7 @@ TFS_BUILTIN(OrdinaryHasInstance)
TFS_BUILTIN(ForInFilter)
TFS_BUILTIN(NewUnmappedArgumentsElements)
TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject)
#undef TFS_BUILTIN
......
......@@ -172,6 +172,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable ArrayPush(Isolate* isolate);
static Callable FunctionPrototypeBind(Isolate* isolate);
static Callable PromiseHandleReject(Isolate* isolate);
};
} // namespace internal
......
......@@ -67,7 +67,9 @@ enum ContextLookupFlags {
V(PERFORM_PROMISE_THEN_INDEX, JSFunction, perform_promise_then) \
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)
V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HANDLE_REJECT_INDEX, JSFunction, promise_handle_reject)
#define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \
V(ARRAY_CONCAT_INDEX, JSFunction, array_concat) \
......@@ -102,7 +104,6 @@ enum ContextLookupFlags {
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
V(PROMISE_CREATE_INDEX, JSFunction, promise_create) \
V(PROMISE_FUNCTION_INDEX, JSFunction, promise_function) \
V(PROMISE_HANDLE_INDEX, JSFunction, promise_handle) \
V(PROMISE_HAS_USER_DEFINED_REJECT_HANDLER_INDEX, JSFunction, \
promise_has_user_defined_reject_handler) \
V(PROMISE_DEBUG_GET_INFO_INDEX, JSFunction, promise_debug_get_info) \
......
......@@ -125,6 +125,7 @@
V(preventExtensions_string, "preventExtensions") \
V(Promise_string, "Promise") \
V(PromiseResolveThenableJob_string, "PromiseResolveThenableJob") \
V(promise_string, "promise") \
V(proto_string, "__proto__") \
V(prototype_string, "prototype") \
V(Proxy_string, "Proxy") \
......@@ -132,6 +133,8 @@
V(RangeError_string, "RangeError") \
V(ReferenceError_string, "ReferenceError") \
V(RegExp_string, "RegExp") \
V(reject_string, "reject") \
V(resolve_string, "resolve") \
V(script_string, "script") \
V(second_string, "second") \
V(setPrototypeOf_string, "setPrototypeOf") \
......
......@@ -93,7 +93,8 @@ class PlatformInterfaceDescriptor;
V(InterpreterPushArgsAndConstruct) \
V(InterpreterPushArgsAndConstructArray) \
V(InterpreterCEntry) \
V(ResumeGenerator)
V(ResumeGenerator) \
V(PromiseHandleReject)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
public:
......@@ -835,6 +836,13 @@ class ResumeGeneratorDescriptor final : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(ResumeGeneratorDescriptor, CallInterfaceDescriptor)
};
class PromiseHandleRejectDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kPromise, kOnReject, kException)
DECLARE_DEFAULT_DESCRIPTOR(PromiseHandleRejectDescriptor,
CallInterfaceDescriptor, kParameterCount)
};
#undef DECLARE_DESCRIPTOR_WITH_BASE
#undef DECLARE_DESCRIPTOR
#undef DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE
......
......@@ -1342,10 +1342,18 @@ HandlerTable::CatchPrediction PredictException(JavaScriptFrame* frame) {
frame->Summarize(&summaries);
for (const FrameSummary& summary : summaries) {
Handle<AbstractCode> code = summary.abstract_code();
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN &&
code->GetCode()->is_promise_rejection()) {
return HandlerTable::PROMISE;
if (code->IsCode() && code->kind() == AbstractCode::BUILTIN) {
if (code->GetCode()->is_promise_rejection()) {
return HandlerTable::PROMISE;
}
// This the exception throw in PromiseHandle which doesn't
// cause a promise rejection.
if (code->GetCode()->is_exception_caught()) {
return HandlerTable::CAUGHT;
}
}
if (code->kind() == AbstractCode::OPTIMIZED_FUNCTION) {
DCHECK(summary.function()->shared()->asm_function());
// asm code cannot contain try-catch.
......
......@@ -39,31 +39,6 @@ utils.Import(function(from) {
// Core functionality.
function PromiseHandle(value, handler, deferred) {
var debug_is_active = DEBUG_IS_ACTIVE;
try {
if (debug_is_active) %DebugPushPromise(deferred.promise);
var result = handler(value);
if (IS_UNDEFINED(deferred.resolve)) {
%promise_resolve(deferred.promise, result);
} else {
%_Call(deferred.resolve, UNDEFINED, result);
}
} %catch (exception) { // Natives syntax to mark this catch block.
try {
if (IS_UNDEFINED(deferred.reject)) {
// Pass false for debugEvent so .then chaining or throwaway promises
// in async functions do not trigger redundant ExceptionEvents.
%PromiseReject(deferred.promise, exception, false);
} else {
%_Call(deferred.reject, UNDEFINED, exception);
}
} catch (e) { }
} finally {
if (debug_is_active) %DebugPopPromise();
}
}
function PromiseDebugGetInfo(deferreds, status) {
var id, name, instrumenting = DEBUG_IS_ACTIVE;
......@@ -394,7 +369,6 @@ utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies);
"promise_reject", DoRejectPromise,
// TODO(gsathya): Remove this once we update the promise builtin.
"promise_internal_reject", RejectPromise,
"promise_handle", PromiseHandle,
"promise_debug_get_info", PromiseDebugGetInfo,
"new_promise_capability", NewPromiseCapability,
"internal_promise_capability", CreateInternalPromiseCapability,
......
......@@ -5145,6 +5145,19 @@ inline void Code::set_is_promise_rejection(bool value) {
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
inline bool Code::is_exception_caught() {
DCHECK(kind() == BUILTIN);
return IsExceptionCaughtField::decode(
READ_UINT32_FIELD(this, kKindSpecificFlags1Offset));
}
inline void Code::set_is_exception_caught(bool value) {
DCHECK(kind() == BUILTIN);
int previous = READ_UINT32_FIELD(this, kKindSpecificFlags1Offset);
int updated = IsExceptionCaughtField::update(previous, value);
WRITE_UINT32_FIELD(this, kKindSpecificFlags1Offset, updated);
}
bool Code::has_deoptimization_support() {
DCHECK_EQ(FUNCTION, kind());
unsigned flags = READ_UINT32_FIELD(this, kFullCodeFlags);
......
......@@ -5579,6 +5579,11 @@ class Code: public HeapObject {
inline bool is_promise_rejection();
inline void set_is_promise_rejection(bool flag);
// [is_exception_caught]: For kind BUILTIN tells whether the exception
// thrown by the code will be caught internally.
inline bool is_exception_caught();
inline void set_is_exception_caught(bool flag);
// [constant_pool]: The constant pool for this function.
inline Address constant_pool();
......@@ -5856,9 +5861,10 @@ class Code: public HeapObject {
// Could be moved to overlap previous bits when we need more space.
static const int kIsConstructStub = kCanHaveWeakObjects + 1;
static const int kIsPromiseRejection = kIsConstructStub + 1;
static const int kIsExceptionCaught = kIsPromiseRejection + 1;
STATIC_ASSERT(kStackSlotsFirstBit + kStackSlotsBitCount <= 32);
STATIC_ASSERT(kIsPromiseRejection + 1 <= 32);
STATIC_ASSERT(kIsExceptionCaught + 1 <= 32);
class StackSlotsField: public BitField<int,
kStackSlotsFirstBit, kStackSlotsBitCount> {}; // NOLINT
......@@ -5872,6 +5878,8 @@ class Code: public HeapObject {
}; // NOLINT
class IsPromiseRejectionField
: public BitField<bool, kIsPromiseRejection, 1> {}; // NOLINT
class IsExceptionCaughtField : public BitField<bool, kIsExceptionCaught, 1> {
}; // NOLINT
// KindSpecificFlags2 layout (ALL)
static const int kIsCrankshaftedBit = 0;
......
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