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(
......
This diff is collapsed.
......@@ -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