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}
This commit is contained in:
parent
3b1a09f56d
commit
9fc3c017e1
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user