Commit 1cee0196 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[promises/deoptimizer] Support "catching" builtin continuations

This CL allows builtin continuations to handle pending exceptions.
This implements exception handling for the promise constructor in
case of deoptimization.

Bug: v8:7584


Change-Id: Ib5df5eb6606abb3f9690f294397981858dbdbf25
Reviewed-on: https://chromium-review.googlesource.com/983912
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52340}
parent 16378186
......@@ -827,7 +827,8 @@ namespace internal {
TFJ(PromiseGetCapabilitiesExecutor, 2, kResolve, kReject) \
/* ES6 #sec-newpromisecapability */ \
TFS(NewPromiseCapability, kConstructor, kDebugEvent) \
TFJ(PromiseConstructorLazyDeoptContinuation, 2, kPromise, kResult) \
TFJ(PromiseConstructorLazyDeoptContinuation, 4, kPromise, kReject, \
kException, kResult) \
/* ES6 #sec-promise-executor */ \
TFJ(PromiseConstructor, 1, kExecutor) \
CPP(IsPromise) \
......@@ -1278,6 +1279,7 @@ namespace internal {
V(AsyncGeneratorResolve) \
V(PromiseAll) \
V(PromiseConstructor) \
V(PromiseConstructorLazyDeoptContinuation) \
V(PromiseFulfillReactionJob) \
V(PromiseRace) \
V(ResolvePromise)
......
......@@ -767,6 +767,18 @@ TF_BUILTIN(PromiseCapabilityDefaultResolve, PromiseBuiltinsAssembler) {
TF_BUILTIN(PromiseConstructorLazyDeoptContinuation, PromiseBuiltinsAssembler) {
Node* promise = Parameter(Descriptor::kPromise);
Node* reject = Parameter(Descriptor::kReject);
Node* exception = Parameter(Descriptor::kException);
Node* const context = Parameter(Descriptor::kContext);
Label finally(this);
GotoIf(IsTheHole(exception), &finally);
CallJS(CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
context, reject, UndefinedConstant(), exception);
Goto(&finally);
BIND(&finally);
Return(promise);
}
......
......@@ -1004,6 +1004,14 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
bailout_id, shared_info_id, parameter_count);
break;
}
case FrameStateType::kJavaScriptBuiltinContinuationWithCatch: {
BailoutId bailout_id = descriptor->bailout_id();
int parameter_count =
static_cast<unsigned int>(descriptor->parameters_count());
translation->BeginJavaScriptBuiltinContinuationWithCatchFrame(
bailout_id, shared_info_id, parameter_count);
break;
}
}
TranslateFrameStateDescriptorOperands(descriptor, iter, state_combine,
......
......@@ -63,6 +63,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kJavaScriptBuiltinContinuation:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuationWithCatch:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME";
break;
}
return os;
}
......@@ -80,6 +83,24 @@ std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
namespace {
// Lazy deopt points where the frame state is assocated with a call get an
// additional parameter for the return result from the call. The return result
// is added by the deoptimizer and not explicitly specified in the frame state.
// Lazy deopt points which can catch exceptions further get an additional
// parameter, namely the exception thrown. The exception is also added by the
// deoptimizer.
uint8_t DeoptimizerParameterCountFor(ContinuationFrameStateMode mode) {
switch (mode) {
case ContinuationFrameStateMode::EAGER:
return 0;
case ContinuationFrameStateMode::LAZY:
return 1;
case ContinuationFrameStateMode::LAZY_WITH_CATCH:
return 2;
}
UNREACHABLE();
}
Node* CreateBuiltinContinuationFrameStateCommon(
JSGraph* jsgraph, FrameStateType frame_type, Builtins::Name name,
Node* closure, Node* context, Node** parameters, int parameter_count,
......@@ -120,11 +141,10 @@ Node* CreateStubBuiltinContinuationFrameState(
CallInterfaceDescriptor descriptor = callable.descriptor();
std::vector<Node*> actual_parameters;
// Stack parameters first. If the deoptimization is LAZY, the final parameter
// is added by the deoptimizer and isn't explicitly passed in the frame state.
int stack_parameter_count =
descriptor.GetRegisterParameterCount() -
(mode == ContinuationFrameStateMode::LAZY ? 1 : 0);
// Stack parameters first. Depending on {mode}, final parameters are added
// by the deoptimizer and aren't explicitly passed in the frame state.
int stack_parameter_count = descriptor.GetRegisterParameterCount() -
DeoptimizerParameterCountFor(mode);
for (int i = 0; i < stack_parameter_count; ++i) {
actual_parameters.push_back(
parameters[descriptor.GetRegisterParameterCount() + i]);
......@@ -149,18 +169,12 @@ Node* CreateJavaScriptBuiltinContinuationFrameState(
Isolate* const isolate = jsgraph->isolate();
Callable const callable = Builtins::CallableFor(isolate, name);
// Lazy deopt points where the frame state is assocated with a call get an
// additional parameter for the return result from the call that's added by
// the deoptimizer and not explicitly specified in the frame state. Check that
// there is not a mismatch between the number of frame state parameters and
// the stack parameters required by the builtin taking this into account.
// Depending on {mode}, final parameters are added by the deoptimizer
// and aren't explicitly passed in the frame state.
DCHECK_EQ(Builtins::GetStackParameterCount(name) + 1, // add receiver
stack_parameter_count +
(mode == ContinuationFrameStateMode::EAGER ? 0 : 1));
stack_parameter_count + DeoptimizerParameterCountFor(mode));
Node* argc =
jsgraph->Constant(stack_parameter_count -
(mode == ContinuationFrameStateMode::EAGER ? 1 : 0));
Node* argc = jsgraph->Constant(Builtins::GetStackParameterCount(name));
// Stack parameters first. They must be first because the receiver is expected
// to be the second value in the translation when creating stack crawls
......@@ -177,8 +191,11 @@ Node* CreateJavaScriptBuiltinContinuationFrameState(
actual_parameters.push_back(argc);
return CreateBuiltinContinuationFrameStateCommon(
jsgraph, FrameStateType::kJavaScriptBuiltinContinuation, name, target,
context, &actual_parameters[0],
jsgraph,
mode == ContinuationFrameStateMode::LAZY_WITH_CATCH
? FrameStateType::kJavaScriptBuiltinContinuationWithCatch
: FrameStateType::kJavaScriptBuiltinContinuation,
name, target, context, &actual_parameters[0],
static_cast<int>(actual_parameters.size()), outer_frame_state, shared);
}
......
......@@ -60,12 +60,15 @@ class OutputFrameStateCombine {
// The type of stack frame that a FrameState node represents.
enum class FrameStateType {
kInterpretedFunction, // Represents an InterpretedFrame.
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kConstructStub, // Represents a ConstructStubFrame.
kBuiltinContinuation, // Represents a continuation to a stub.
kJavaScriptBuiltinContinuation // Represents a continuation to a JavaScipt
// builtin.
kInterpretedFunction, // Represents an InterpretedFrame.
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kConstructStub, // Represents a ConstructStubFrame.
kBuiltinContinuation, // Represents a continuation to a stub.
kJavaScriptBuiltinContinuation, // Represents a continuation to a JavaScipt
// builtin.
kJavaScriptBuiltinContinuationWithCatch // Represents a continuation to a
// JavaScipt builtin with a catch
// handler.
};
class FrameStateFunctionInfo {
......@@ -85,7 +88,8 @@ class FrameStateFunctionInfo {
static bool IsJSFunctionType(FrameStateType type) {
return type == FrameStateType::kInterpretedFunction ||
type == FrameStateType::kJavaScriptBuiltinContinuation;
type == FrameStateType::kJavaScriptBuiltinContinuation ||
type == FrameStateType::kJavaScriptBuiltinContinuationWithCatch;
}
private:
......@@ -143,7 +147,7 @@ static const int kFrameStateFunctionInput = 4;
static const int kFrameStateOuterStateInput = 5;
static const int kFrameStateInputCount = kFrameStateOuterStateInput + 1;
enum class ContinuationFrameStateMode { EAGER, LAZY };
enum class ContinuationFrameStateMode { EAGER, LAZY, LAZY_WITH_CATCH };
Node* CreateStubBuiltinContinuationFrameState(
JSGraph* graph, Builtins::Name name, Node* context, Node* const* parameters,
......
......@@ -5344,6 +5344,7 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
Node* control = NodeProperties::GetControlInput(node);
if (!FLAG_experimental_inline_promise_constructor) return NoChange();
if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
// Only handle builtins Promises, not subclasses.
if (target != new_target) return NoChange();
......@@ -5363,23 +5364,25 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
node, outer_frame_state, 1, BailoutId::ConstructStubInvoke(),
FrameStateType::kConstructStub, promise_shared);
// This frame state doesn't ever call the deopt continuation, it's only
// necessary to specifiy a continuation in order to handle the exceptional
// case.
Node* checkpoint_params[] = {jsgraph()->UndefinedConstant(),
jsgraph()->UndefinedConstant()};
const int stack_parameters = arraysize(checkpoint_params);
// The deopt continuation of this frame state is never called; the frame state
// is only necessary to obtain the right stack trace.
const std::vector<Node*> checkpoint_parameters({
jsgraph()->UndefinedConstant(), /* receiver */
jsgraph()->UndefinedConstant(), /* promise */
jsgraph()->UndefinedConstant(), /* reject function */
jsgraph()->TheHoleConstant() /* exception */
});
int checkpoint_parameters_size =
static_cast<int>(checkpoint_parameters.size());
Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), promise_shared,
Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
&checkpoint_params[0], stack_parameters, constructor_frame_state,
ContinuationFrameStateMode::LAZY);
checkpoint_parameters.data(), checkpoint_parameters_size,
constructor_frame_state, ContinuationFrameStateMode::LAZY);
// Check if executor is callable
Node* check_fail = nullptr;
Node* check_throw = nullptr;
// TODO(petermarshall): The frame state is wrong here.
WireInCallbackIsCallableCheck(executor, context, frame_state, effect,
&control, &check_fail, &check_throw);
......@@ -5422,18 +5425,18 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
handle(reject_shared->GetCode(), isolate())),
promise_context, effect, control);
// Re-use the params from above, but actually set the promise parameter now.
checkpoint_params[1] = promise;
// This simple continuation just returns the created promise.
// TODO(petermarshall): If the executor function causes lazy deopt, and it
// also throws an exception, we should catch the exception and call the reject
// function.
const std::vector<Node*> checkpoint_parameters_continuation(
{jsgraph()->UndefinedConstant() /* receiver */, promise, reject});
int checkpoint_parameters_continuation_size =
static_cast<int>(checkpoint_parameters_continuation.size());
// This continuation just returns the created promise and takes care of
// exceptions thrown by the executor.
frame_state = CreateJavaScriptBuiltinContinuationFrameState(
jsgraph(), promise_shared,
Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
&checkpoint_params[0], stack_parameters, constructor_frame_state,
ContinuationFrameStateMode::LAZY);
checkpoint_parameters_continuation.data(),
checkpoint_parameters_continuation_size, constructor_frame_state,
ContinuationFrameStateMode::LAZY_WITH_CATCH);
// 9. Call executor with both resolving functions
effect = control = graph()->NewNode(
......
......@@ -100,7 +100,9 @@ DeoptimizedFrameInfo* Deoptimizer::DebuggerInspectableFrame(
for (auto it = translated_values.begin(); it != translated_values.end();
it++) {
if (it->kind() == TranslatedFrame::kInterpretedFunction ||
it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation) {
it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
it->kind() ==
TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
if (counter == 0) {
frame_it = it;
break;
......@@ -549,6 +551,9 @@ int LookupCatchHandler(TranslatedFrame* translated_frame, int* data_out) {
HandlerTable table(translated_frame->raw_shared_info()->bytecode_array());
return table.LookupRange(bytecode_offset, data_out, nullptr);
}
case TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch: {
return 0;
}
default:
break;
}
......@@ -653,10 +658,11 @@ void Deoptimizer::DoComputeOutputFrames() {
for (size_t i = 0; i < count; ++i, ++frame_index) {
// Read the ast node id, function, and frame height for this output frame.
TranslatedFrame* translated_frame = &(translated_state_.frames()[i]);
bool handle_exception = deoptimizing_throw_ && i == count - 1;
switch (translated_frame->kind()) {
case TranslatedFrame::kInterpretedFunction:
DoComputeInterpretedFrame(translated_frame, frame_index,
deoptimizing_throw_ && i == count - 1);
handle_exception);
jsframe_count_++;
break;
case TranslatedFrame::kArgumentsAdaptor:
......@@ -666,10 +672,19 @@ void Deoptimizer::DoComputeOutputFrames() {
DoComputeConstructStubFrame(translated_frame, frame_index);
break;
case TranslatedFrame::kBuiltinContinuation:
DoComputeBuiltinContinuation(translated_frame, frame_index, false);
DoComputeBuiltinContinuation(translated_frame, frame_index,
BuiltinContinuationMode::STUB);
break;
case TranslatedFrame::kJavaScriptBuiltinContinuation:
DoComputeBuiltinContinuation(translated_frame, frame_index, true);
DoComputeBuiltinContinuation(translated_frame, frame_index,
BuiltinContinuationMode::JAVASCRIPT);
break;
case TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch:
DoComputeBuiltinContinuation(
translated_frame, frame_index,
handle_exception
? BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION
: BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH);
break;
case TranslatedFrame::kInvalid:
FATAL("invalid frame");
......@@ -1342,6 +1357,63 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
}
}
bool Deoptimizer::BuiltinContinuationModeIsJavaScript(
BuiltinContinuationMode mode) {
switch (mode) {
case BuiltinContinuationMode::STUB:
return false;
case BuiltinContinuationMode::JAVASCRIPT:
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return true;
}
UNREACHABLE();
}
bool Deoptimizer::BuiltinContinuationModeIsWithCatch(
BuiltinContinuationMode mode) {
switch (mode) {
case BuiltinContinuationMode::STUB:
case BuiltinContinuationMode::JAVASCRIPT:
return false;
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return true;
}
UNREACHABLE();
}
StackFrame::Type Deoptimizer::BuiltinContinuationModeToFrameType(
BuiltinContinuationMode mode) {
switch (mode) {
case BuiltinContinuationMode::STUB:
return StackFrame::BUILTIN_CONTINUATION;
case BuiltinContinuationMode::JAVASCRIPT:
return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION;
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
}
UNREACHABLE();
}
Builtins::Name Deoptimizer::TrampolineForBuiltinContinuation(
BuiltinContinuationMode mode, bool must_handle_result) {
switch (mode) {
case BuiltinContinuationMode::STUB:
return must_handle_result ? Builtins::kContinueToCodeStubBuiltinWithResult
: Builtins::kContinueToCodeStubBuiltin;
case BuiltinContinuationMode::JAVASCRIPT:
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return must_handle_result
? Builtins::kContinueToJavaScriptBuiltinWithResult
: Builtins::kContinueToJavaScriptBuiltin;
}
UNREACHABLE();
}
// BuiltinContinuationFrames capture the machine state that is expected as input
// to a builtin, including both input register values and stack parameters. When
// the frame is reactivated (i.e. the frame below it returns), a
......@@ -1400,7 +1472,7 @@ void Deoptimizer::DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
//
void Deoptimizer::DoComputeBuiltinContinuation(
TranslatedFrame* translated_frame, int frame_index,
bool java_script_builtin) {
BuiltinContinuationMode mode) {
TranslatedFrame::iterator value_iterator = translated_frame->begin();
int input_index = 0;
......@@ -1435,7 +1507,8 @@ void Deoptimizer::DoComputeBuiltinContinuation(
const int translated_stack_parameters =
height_in_words - register_parameter_count - 1;
const int stack_param_count =
translated_stack_parameters + (must_handle_result ? 1 : 0);
translated_stack_parameters + (must_handle_result ? 1 : 0) +
(BuiltinContinuationModeIsWithCatch(mode) ? 1 : 0);
const int stack_param_pad_count =
ShouldPadArguments(stack_param_count) ? 1 : 0;
......@@ -1475,7 +1548,7 @@ void Deoptimizer::DoComputeBuiltinContinuation(
CHECK(IsAnyTagged(type.representation()));
}
}
CHECK_EQ(java_script_builtin, has_argc);
CHECK_EQ(BuiltinContinuationModeIsJavaScript(mode), has_argc);
if (trace_scope_ != nullptr) {
PrintF(trace_scope_->file(),
......@@ -1531,6 +1604,27 @@ void Deoptimizer::DoComputeBuiltinContinuation(
output_frame_offset);
}
switch (mode) {
case BuiltinContinuationMode::STUB:
break;
case BuiltinContinuationMode::JAVASCRIPT:
break;
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH: {
output_frame_offset -= kPointerSize;
WriteValueToOutput(isolate()->heap()->the_hole_value(), input_index,
frame_index, output_frame_offset,
"placeholder for exception on lazy deopt ");
} break;
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION: {
output_frame_offset -= kPointerSize;
intptr_t accumulator_value =
input_->GetRegister(kInterpreterAccumulatorRegister.code());
WriteValueToOutput(reinterpret_cast<Object*>(accumulator_value), 0,
frame_index, output_frame_offset,
"exception (from accumulator)");
} break;
}
if (must_handle_result) {
output_frame_offset -= kPointerSize;
WriteValueToOutput(isolate()->heap()->the_hole_value(), input_index,
......@@ -1603,19 +1697,18 @@ void Deoptimizer::DoComputeBuiltinContinuation(
// A marker value is used in place of the context.
output_frame_offset -= kPointerSize;
intptr_t marker =
java_script_builtin
? StackFrame::TypeToMarker(
StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION)
: StackFrame::TypeToMarker(StackFrame::BUILTIN_CONTINUATION);
StackFrame::TypeToMarker(BuiltinContinuationModeToFrameType(mode));
output_frame->SetFrameSlot(output_frame_offset, marker);
DebugPrintOutputSlot(marker, frame_index, output_frame_offset,
"context (builtin continuation sentinel)\n");
output_frame_offset -= kPointerSize;
value = java_script_builtin ? maybe_function : 0;
value = BuiltinContinuationModeIsJavaScript(mode) ? maybe_function : 0;
output_frame->SetFrameSlot(output_frame_offset, value);
DebugPrintOutputSlot(value, frame_index, output_frame_offset,
java_script_builtin ? "JSFunction\n" : "unused\n");
DebugPrintOutputSlot(
value, frame_index, output_frame_offset,
BuiltinContinuationModeIsJavaScript(mode) ? "JSFunction\n" : "unused\n");
// The delta from the SP to the FP; used to reconstruct SP in
// Isolate::UnwindAndFindHandler.
......@@ -1640,7 +1733,7 @@ void Deoptimizer::DoComputeBuiltinContinuation(
output_frame->SetFrameSlot(output_frame_offset, value);
if (trace_scope_ != nullptr) {
ScopedVector<char> str(128);
if (java_script_builtin &&
if (BuiltinContinuationModeIsJavaScript(mode) &&
code == kJavaScriptCallArgCountRegister.code()) {
SNPrintF(
str,
......@@ -1705,18 +1798,8 @@ void Deoptimizer::DoComputeBuiltinContinuation(
Register fp_reg = JavaScriptFrame::fp_register();
output_frame->SetRegister(fp_reg.code(), output_[frame_index - 1]->GetFp());
Code* continue_to_builtin =
java_script_builtin
? (must_handle_result
? isolate()->builtins()->builtin(
Builtins::kContinueToJavaScriptBuiltinWithResult)
: isolate()->builtins()->builtin(
Builtins::kContinueToJavaScriptBuiltin))
: (must_handle_result
? isolate()->builtins()->builtin(
Builtins::kContinueToCodeStubBuiltinWithResult)
: isolate()->builtins()->builtin(
Builtins::kContinueToCodeStubBuiltin));
Code* continue_to_builtin = isolate()->builtins()->builtin(
TrampolineForBuiltinContinuation(mode, must_handle_result));
output_frame->SetPc(
reinterpret_cast<intptr_t>(continue_to_builtin->InstructionStart()));
......@@ -1966,6 +2049,14 @@ void Translation::BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,
buffer_->Add(height);
}
void Translation::BeginJavaScriptBuiltinContinuationWithCatchFrame(
BailoutId bailout_id, int literal_id, unsigned height) {
buffer_->Add(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME);
buffer_->Add(bailout_id.ToInt());
buffer_->Add(literal_id);
buffer_->Add(height);
}
void Translation::BeginConstructStubFrame(BailoutId bailout_id, int literal_id,
unsigned height) {
buffer_->Add(CONSTRUCT_STUB_FRAME);
......@@ -2124,6 +2215,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) {
case CONSTRUCT_STUB_FRAME:
case BUILTIN_CONTINUATION_FRAME:
case JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME:
case JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME:
return 3;
}
FATAL("Unexpected translation type");
......@@ -2701,6 +2793,14 @@ TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationFrame(
return frame;
}
TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationWithCatchFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) {
TranslatedFrame frame(kJavaScriptBuiltinContinuationWithCatch, shared_info,
height);
frame.node_id_ = bailout_id;
return frame;
}
int TranslatedFrame::GetValueCount() {
switch (kind()) {
case kInterpretedFunction: {
......@@ -2714,6 +2814,7 @@ int TranslatedFrame::GetValueCount() {
case kConstructStub:
case kBuiltinContinuation:
case kJavaScriptBuiltinContinuation:
case kJavaScriptBuiltinContinuationWithCatch:
return 1 + height_;
case kInvalid:
......@@ -2822,6 +2923,25 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame(
return TranslatedFrame::JavaScriptBuiltinContinuationFrame(
bailout_id, shared_info, height_with_context);
}
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME: {
BailoutId bailout_id = BailoutId(iterator->Next());
SharedFunctionInfo* shared_info =
SharedFunctionInfo::cast(literal_array->get(iterator->Next()));
int height = iterator->Next();
if (trace_file != nullptr) {
std::unique_ptr<char[]> name = shared_info->DebugName()->ToCString();
PrintF(trace_file,
" reading JavaScript builtin continuation frame with catch %s",
name.get());
PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n",
bailout_id.ToInt(), height);
}
// Add one to the height to account for the context which was implicitly
// added to the translation during code generation.
int height_with_context = height + 1;
return TranslatedFrame::JavaScriptBuiltinContinuationWithCatchFrame(
bailout_id, shared_info, height_with_context);
}
case Translation::UPDATE_FEEDBACK:
case Translation::BEGIN:
case Translation::DUPLICATED_OBJECT:
......@@ -2965,6 +3085,7 @@ int TranslatedState::CreateNextTranslatedValue(
case Translation::ARGUMENTS_ADAPTOR_FRAME:
case Translation::CONSTRUCT_STUB_FRAME:
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME:
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME:
case Translation::BUILTIN_CONTINUATION_FRAME:
case Translation::UPDATE_FEEDBACK:
// Peeled off before getting here.
......@@ -3813,7 +3934,9 @@ TranslatedValue* TranslatedState::ResolveCapturedObject(TranslatedValue* slot) {
TranslatedFrame* TranslatedState::GetFrameFromJSFrameIndex(int jsframe_index) {
for (size_t i = 0; i < frames_.size(); i++) {
if (frames_[i].kind() == TranslatedFrame::kInterpretedFunction ||
frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation) {
frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
frames_[i].kind() ==
TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
if (jsframe_index > 0) {
jsframe_index--;
} else {
......@@ -3828,7 +3951,9 @@ TranslatedFrame* TranslatedState::GetArgumentsInfoFromJSFrameIndex(
int jsframe_index, int* args_count) {
for (size_t i = 0; i < frames_.size(); i++) {
if (frames_[i].kind() == TranslatedFrame::kInterpretedFunction ||
frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation) {
frames_[i].kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
frames_[i].kind() ==
TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
if (jsframe_index > 0) {
jsframe_index--;
} else {
......
......@@ -152,6 +152,7 @@ class TranslatedFrame {
kConstructStub,
kBuiltinContinuation,
kJavaScriptBuiltinContinuation,
kJavaScriptBuiltinContinuationWithCatch,
kInvalid
};
......@@ -224,6 +225,8 @@ class TranslatedFrame {
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height);
static TranslatedFrame JavaScriptBuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height);
static TranslatedFrame JavaScriptBuiltinContinuationWithCatchFrame(
BailoutId bailout_id, SharedFunctionInfo* shared_info, int height);
static TranslatedFrame InvalidFrame() {
return TranslatedFrame(kInvalid, nullptr);
}
......@@ -533,8 +536,23 @@ class Deoptimizer : public Malloced {
int frame_index);
void DoComputeConstructStubFrame(TranslatedFrame* translated_frame,
int frame_index);
enum class BuiltinContinuationMode {
STUB,
JAVASCRIPT,
JAVASCRIPT_WITH_CATCH,
JAVASCRIPT_HANDLE_EXCEPTION
};
static bool BuiltinContinuationModeIsWithCatch(BuiltinContinuationMode mode);
static bool BuiltinContinuationModeIsJavaScript(BuiltinContinuationMode mode);
static StackFrame::Type BuiltinContinuationModeToFrameType(
BuiltinContinuationMode mode);
static Builtins::Name TrampolineForBuiltinContinuation(
BuiltinContinuationMode mode, bool must_handle_result);
void DoComputeBuiltinContinuation(TranslatedFrame* translated_frame,
int frame_index, bool java_script_frame);
int frame_index,
BuiltinContinuationMode mode);
void WriteTranslatedValueToOutput(
TranslatedFrame::iterator* iterator, int* input_index, int frame_index,
......@@ -868,30 +886,31 @@ class TranslationIterator BASE_EMBEDDED {
int index_;
};
#define TRANSLATION_OPCODE_LIST(V) \
V(BEGIN) \
V(INTERPRETED_FRAME) \
V(BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) \
V(CONSTRUCT_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_ELEMENTS) \
V(ARGUMENTS_LENGTH) \
V(CAPTURED_OBJECT) \
V(REGISTER) \
V(INT32_REGISTER) \
V(UINT32_REGISTER) \
V(BOOL_REGISTER) \
V(FLOAT_REGISTER) \
V(DOUBLE_REGISTER) \
V(STACK_SLOT) \
V(INT32_STACK_SLOT) \
V(UINT32_STACK_SLOT) \
V(BOOL_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \
V(LITERAL) \
#define TRANSLATION_OPCODE_LIST(V) \
V(BEGIN) \
V(INTERPRETED_FRAME) \
V(BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME) \
V(CONSTRUCT_STUB_FRAME) \
V(ARGUMENTS_ADAPTOR_FRAME) \
V(DUPLICATED_OBJECT) \
V(ARGUMENTS_ELEMENTS) \
V(ARGUMENTS_LENGTH) \
V(CAPTURED_OBJECT) \
V(REGISTER) \
V(INT32_REGISTER) \
V(UINT32_REGISTER) \
V(BOOL_REGISTER) \
V(FLOAT_REGISTER) \
V(DOUBLE_REGISTER) \
V(STACK_SLOT) \
V(INT32_STACK_SLOT) \
V(UINT32_STACK_SLOT) \
V(BOOL_STACK_SLOT) \
V(FLOAT_STACK_SLOT) \
V(DOUBLE_STACK_SLOT) \
V(LITERAL) \
V(UPDATE_FEEDBACK)
class Translation BASE_EMBEDDED {
......@@ -924,6 +943,9 @@ class Translation BASE_EMBEDDED {
unsigned height);
void BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,
int literal_id, unsigned height);
void BeginJavaScriptBuiltinContinuationWithCatchFrame(BailoutId bailout_id,
int literal_id,
unsigned height);
void ArgumentsElements(CreateArgumentsType type);
void ArgumentsLength(CreateArgumentsType type);
void BeginCapturedObject(int length);
......
......@@ -228,6 +228,11 @@ inline JavaScriptBuiltinContinuationFrame::JavaScriptBuiltinContinuationFrame(
StackFrameIteratorBase* iterator)
: JavaScriptFrame(iterator) {}
inline JavaScriptBuiltinContinuationWithCatchFrame::
JavaScriptBuiltinContinuationWithCatchFrame(
StackFrameIteratorBase* iterator)
: JavaScriptBuiltinContinuationFrame(iterator) {}
inline JavaScriptFrameIterator::JavaScriptFrameIterator(
Isolate* isolate)
: iterator_(isolate) {
......
......@@ -512,6 +512,7 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case BUILTIN_EXIT:
case STUB:
case INTERNAL:
......@@ -855,6 +856,7 @@ void StandardFrame::IterateCompiledFrame(RootVisitor* v) const {
case EXIT:
case BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION:
case JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case BUILTIN_EXIT:
case ARGUMENTS_ADAPTOR:
case STUB:
......@@ -1220,6 +1222,25 @@ int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const {
return Smi::ToInt(argc_object);
}
intptr_t JavaScriptBuiltinContinuationFrame::GetSPToFPDelta() const {
Address height_slot =
fp() + BuiltinContinuationFrameConstants::kFrameSPtoFPDeltaAtDeoptimize;
intptr_t height = *reinterpret_cast<intptr_t*>(height_slot);
return height;
}
void JavaScriptBuiltinContinuationWithCatchFrame::SetException(
Object* exception) {
Address exception_argument_slot =
fp() + JavaScriptFrameConstants::kLastParameterOffset +
kPointerSize; // Skip over return value slot.
// Only allow setting exception if previous value was the hole.
CHECK_EQ(isolate()->heap()->the_hole_value(),
Memory::Object_at(exception_argument_slot));
Memory::Object_at(exception_argument_slot) = exception;
}
FrameSummary::JavaScriptFrameSummary::JavaScriptFrameSummary(
Isolate* isolate, Object* receiver, JSFunction* function,
AbstractCode* abstract_code, int code_offset, bool is_constructor)
......@@ -1436,7 +1457,9 @@ void OptimizedFrame::Summarize(std::vector<FrameSummary>* frames) const {
bool is_constructor = IsConstructor();
for (auto it = translated.begin(); it != translated.end(); it++) {
if (it->kind() == TranslatedFrame::kInterpretedFunction ||
it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation) {
it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
it->kind() ==
TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
Handle<SharedFunctionInfo> shared_info = it->shared_info();
// The translation commands are ordered and the function is always
......@@ -1456,7 +1479,9 @@ void OptimizedFrame::Summarize(std::vector<FrameSummary>* frames) const {
// the translation corresponding to the frame type in question.
Handle<AbstractCode> abstract_code;
unsigned code_offset;
if (it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation) {
if (it->kind() == TranslatedFrame::kJavaScriptBuiltinContinuation ||
it->kind() ==
TranslatedFrame::kJavaScriptBuiltinContinuationWithCatch) {
code_offset = 0;
abstract_code =
handle(AbstractCode::cast(isolate()->builtins()->builtin(
......@@ -1575,7 +1600,9 @@ void OptimizedFrame::GetFunctions(
while (jsframe_count != 0) {
opcode = static_cast<Translation::Opcode>(it.Next());
if (opcode == Translation::INTERPRETED_FRAME ||
opcode == Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) {
opcode == Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME ||
opcode ==
Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME) {
it.Next(); // Skip bailout id.
jsframe_count--;
......
......@@ -101,6 +101,8 @@ class StackHandler BASE_EMBEDDED {
V(STUB, StubFrame) \
V(BUILTIN_CONTINUATION, BuiltinContinuationFrame) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION, JavaScriptBuiltinContinuationFrame) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH, \
JavaScriptBuiltinContinuationWithCatchFrame) \
V(INTERNAL, InternalFrame) \
V(CONSTRUCT, ConstructFrame) \
V(ARGUMENTS_ADAPTOR, ArgumentsAdaptorFrame) \
......@@ -214,6 +216,9 @@ class StackFrame BASE_EMBEDDED {
bool is_java_script_builtin_continuation() const {
return type() == JAVA_SCRIPT_BUILTIN_CONTINUATION;
}
bool is_java_script_builtin_with_catch_continuation() const {
return type() == JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
}
bool is_construct() const { return type() == CONSTRUCT; }
bool is_builtin_exit() const { return type() == BUILTIN_EXIT; }
virtual bool is_standard() const { return false; }
......@@ -221,7 +226,8 @@ class StackFrame BASE_EMBEDDED {
bool is_java_script() const {
Type type = this->type();
return (type == OPTIMIZED) || (type == INTERPRETED) || (type == BUILTIN) ||
(type == JAVA_SCRIPT_BUILTIN_CONTINUATION);
(type == JAVA_SCRIPT_BUILTIN_CONTINUATION) ||
(type == JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH);
}
bool is_wasm() const {
Type type = this->type();
......@@ -1152,6 +1158,7 @@ class JavaScriptBuiltinContinuationFrame : public JavaScriptFrame {
}
int ComputeParametersCount() const override;
intptr_t GetSPToFPDelta() const;
protected:
inline explicit JavaScriptBuiltinContinuationFrame(
......@@ -1161,6 +1168,30 @@ class JavaScriptBuiltinContinuationFrame : public JavaScriptFrame {
friend class StackFrameIteratorBase;
};
class JavaScriptBuiltinContinuationWithCatchFrame
: public JavaScriptBuiltinContinuationFrame {
public:
Type type() const override {
return JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH;
}
static JavaScriptBuiltinContinuationWithCatchFrame* cast(StackFrame* frame) {
DCHECK(frame->is_java_script_builtin_with_catch_continuation());
return static_cast<JavaScriptBuiltinContinuationWithCatchFrame*>(frame);
}
// Patch in the exception object at the appropriate location into the stack
// frame.
void SetException(Object* exception);
protected:
inline explicit JavaScriptBuiltinContinuationWithCatchFrame(
StackFrameIteratorBase* iterator);
private:
friend class StackFrameIteratorBase;
};
class StackFrameIteratorBase BASE_EMBEDDED {
public:
Isolate* isolate() const { return isolate_; }
......
......@@ -631,6 +631,7 @@ Handle<Object> Isolate::CaptureSimpleStackTrace(Handle<JSReceiver> error_object,
switch (frame->type()) {
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::OPTIMIZED:
case StackFrame::INTERPRETED:
case StackFrame::BUILTIN:
......@@ -1451,6 +1452,20 @@ Object* Isolate::UnwindAndFindHandler() {
interpreter_frame->debug_info()->Unwind(frame->fp());
} break;
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: {
// Builtin continuation frames with catch can handle exceptions.
if (!catchable_by_js) break;
JavaScriptBuiltinContinuationWithCatchFrame* js_frame =
JavaScriptBuiltinContinuationWithCatchFrame::cast(frame);
js_frame->SetException(exception);
// Reconstructor stack pointer from the frame pointer.
Address return_sp = js_frame->fp() - js_frame->GetSPToFPDelta();
Code* code = js_frame->LookupCode();
return FoundHandler(nullptr, code->InstructionStart(), 0,
code->constant_pool(), return_sp, frame->fp());
} break;
default:
// All other types can not handle exception.
break;
......@@ -1564,6 +1579,12 @@ Isolate::CatchType Isolate::PredictExceptionCatcher() {
if (prediction != NOT_CAUGHT) return prediction;
} break;
case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: {
Handle<Code> code(frame->LookupCode());
CatchType prediction = ToCatchType(code->GetBuiltinCatchPrediction());
if (prediction != NOT_CAUGHT) return prediction;
} break;
default:
// All other types can not handle exception.
break;
......
......@@ -14397,7 +14397,8 @@ void DeoptimizationData::DeoptimizationDataPrint(std::ostream& os) { // NOLINT
}
case Translation::BUILTIN_CONTINUATION_FRAME:
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: {
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME:
case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME: {
int bailout_id = iterator.Next();
int shared_info_id = iterator.Next();
Object* shared_info = LiteralArray()->get(shared_info_id);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Test exercises code paths for catching exceptions in the promise constructor
// in conjunction with deoptimization.
Debug = debug.Debug;
var expected_events = 4;
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Exception) {
expected_events--;
assertTrue(expected_events >= 0);
assertEquals("uncaught", event_data.exception().message);
assertTrue(event_data.uncaught());
// The frame comes from the Promise.reject call
assertNotNull(/\/\/ EXCEPTION/.exec(event_data.sourceLineText()));
assertTrue(event_data.uncaught());
}
} catch (e) {
%AbortJS(e + "\n" + e.stack);
}
}
Debug.setBreakOnException();
Debug.setListener(listener);
function foo(a,b) {
let P = new Promise((resolve, reject) => { bar(a,b); resolve()})
return P;
}
function bar(a,b) {
%DeoptimizeFunction(foo);
throw new Error("uncaught"); // EXCEPTION
}
foo();
%RunMicrotasks();
foo();
%RunMicrotasks();
%OptimizeFunctionOnNextCall(foo);
// bar likely gets inlined into foo.
foo();
%RunMicrotasks();
%NeverOptimizeFunction(bar);
%OptimizeFunctionOnNextCall(foo);
// bar does not get inlined into foo.
foo();
%RunMicrotasks();
......@@ -149,11 +149,9 @@ failWithMessage = (msg) => %AbortJS(msg);
} catch (e) {
// The promise constructor should catch the exception and reject the
// promise instead.
// TODO(petermarshall): This fails but should not. We need to fix deopts.
// assertUnreachable();
assertUnreachable();
}
// TODO(petermarshall): This fails but should not.
// assertInstanceof(p, Promise);
assertInstanceof(p, Promise);
}
foo();
......@@ -162,6 +160,35 @@ failWithMessage = (msg) => %AbortJS(msg);
foo();
})();
// Check that when the promise constructor is marked for lazy deoptimization
// from below, but not immediatelly deoptimized, and then throws, the deopt continuation
// catches and calls the reject function instead of propagating the exception.
(function() {
function foo() {
let p;
try {
p = new Promise((resolve, reject) => { bar(); resolve()});
} catch (e) {
// The promise constructor should catch the exception and reject the
// promise instead.
assertUnreachable();
}
assertInstanceof(p, Promise);
}
function bar() {
%DeoptimizeFunction(foo);
throw new Error();
}
%NeverOptimizeFunction(bar);
foo();
foo();
%OptimizeFunctionOnNextCall(foo);
foo();
})();
// Test when the executor is not inlined.
(function() {
let resolve, reject, promise;
......
......@@ -353,6 +353,7 @@ FRAME_MARKERS = (
"STUB",
"BUILTIN_CONTINUATION",
"JAVA_SCRIPT_BUILTIN_CONTINUATION",
"JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH",
"INTERNAL",
"CONSTRUCT",
"ARGUMENTS_ADAPTOR",
......
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