Index: src/isolate.cc |
diff --git a/src/isolate.cc b/src/isolate.cc |
index 084be15806f59dc95afae1d4e1a39bad9149d118..0e2d1cf6832f45072454a022824e7b6620f58e20 100644 |
--- a/src/isolate.cc |
+++ b/src/isolate.cc |
@@ -934,39 +934,34 @@ void ReportBootstrappingException(Handle<Object> exception, |
} |
-namespace { |
- |
-// Only use by Isolate::Throw for --abort-on-uncaught-exception. |
-int fatal_exception_depth = 0; |
- |
-} // namespace |
- |
- |
Object* Isolate::Throw(Object* exception, MessageLocation* location) { |
DCHECK(!has_pending_exception()); |
HandleScope scope(this); |
Handle<Object> exception_handle(exception, this); |
- // Determine reporting and whether the exception is caught externally. |
- bool catchable_by_javascript = is_catchable_by_javascript(exception); |
- bool can_be_caught_externally = false; |
- bool should_report_exception = |
- ShouldReportException(&can_be_caught_externally, catchable_by_javascript); |
- bool report_exception = catchable_by_javascript && should_report_exception; |
- bool try_catch_needs_message = |
- can_be_caught_externally && try_catch_handler()->capture_message_; |
+ // Determine whether a message needs to be created for the given exception |
+ // depending on the following criteria: |
+ // 1) External v8::TryCatch missing: Always create a message because any |
+ // JavaScript handler for a finally-block might re-throw to top-level. |
+ // 2) External v8::TryCatch exists: Only create a message if the handler |
+ // captures messages or is verbose (which reports despite the catch). |
+ // 3) ReThrow from v8::TryCatch: The message from a previous throw still |
+ // exists and we preserve it instead of creating a new message. |
+ bool requires_message = try_catch_handler() == nullptr || |
+ try_catch_handler()->is_verbose_ || |
+ try_catch_handler()->capture_message_; |
bool rethrowing_message = thread_local_top()->rethrowing_message_; |
thread_local_top()->rethrowing_message_ = false; |
// Notify debugger of exception. |
- if (catchable_by_javascript) { |
- debug()->OnThrow(exception_handle, report_exception); |
+ if (is_catchable_by_javascript(exception)) { |
+ debug()->OnThrow(exception_handle); |
} |
// Generate the message if required. |
- if (!rethrowing_message && (report_exception || try_catch_needs_message)) { |
+ if (requires_message && !rethrowing_message) { |
MessageLocation potential_computed_location; |
if (location == NULL) { |
// If no location was specified we use a computed one instead. |
@@ -981,16 +976,15 @@ Object* Isolate::Throw(Object* exception, MessageLocation* location) { |
ReportBootstrappingException(exception_handle, location); |
} else { |
Handle<Object> message_obj = CreateMessage(exception_handle, location); |
- |
thread_local_top()->pending_message_obj_ = *message_obj; |
// If the abort-on-uncaught-exception flag is specified, abort on any |
// exception not caught by JavaScript, even when an external handler is |
// present. This flag is intended for use by JavaScript developers, so |
// print a user-friendly stack trace (not an internal one). |
- if (fatal_exception_depth == 0 && FLAG_abort_on_uncaught_exception && |
- (report_exception || can_be_caught_externally)) { |
- fatal_exception_depth++; |
+ if (FLAG_abort_on_uncaught_exception && |
+ !PredictWhetherExceptionIsCaught(*exception_handle)) { |
+ FLAG_abort_on_uncaught_exception = false; // Prevent endless recursion. |
PrintF(stderr, "%s\n\nFROM\n", |
MessageHandler::GetLocalizedMessage(this, message_obj).get()); |
PrintCurrentStackTrace(stderr); |
@@ -1047,7 +1041,6 @@ Object* Isolate::FindHandler() { |
// For JSEntryStub frames we always have a handler. |
if (frame->is_entry() || frame->is_entry_construct()) { |
StackHandler* handler = frame->top_handler(); |
- DCHECK_EQ(StackHandler::JS_ENTRY, handler->kind()); |
DCHECK_EQ(0, handler->index()); |
// Restore the next handler. |
@@ -1063,7 +1056,6 @@ Object* Isolate::FindHandler() { |
// For JavaScript frames which have a handler, we use the handler. |
if (frame->is_java_script() && catchable_by_js && frame->HasHandler()) { |
StackHandler* handler = frame->top_handler(); |
- DCHECK_NE(StackHandler::JS_ENTRY, handler->kind()); |
// Restore the next handler. |
thread_local_top()->handler_ = handler->next()->address(); |
@@ -1116,9 +1108,39 @@ Object* Isolate::FindHandler() { |
} |
+// TODO(mstarzinger): We shouldn't need the exception object here. |
+bool Isolate::PredictWhetherExceptionIsCaught(Object* exception) { |
+ if (IsExternalHandlerOnTop(exception)) return true; |
+ |
+ // Search for a JavaScript handler by performing a full walk over the stack |
+ // and dispatching according to the frame type. |
+ for (StackFrameIterator iter(this); !iter.done(); iter.Advance()) { |
+ StackFrame* frame = iter.frame(); |
+ |
+ // For JavaScript frames which have a handler, we use the handler. |
+ if (frame->is_java_script() && frame->HasHandler()) { |
+ return true; |
+ } |
+ |
+ // For optimized frames we perform a lookup in the handler table. |
+ if (frame->is_optimized()) { |
+ Code* frame_code = frame->LookupCode(); |
+ DCHECK(frame_code->is_optimized_code()); |
+ int pc_offset = static_cast<int>(frame->pc() - frame_code->entry()); |
+ int handler_offset = LookupInHandlerTable(frame_code, pc_offset); |
+ if (handler_offset < 0) continue; |
+ return true; |
+ } |
+ } |
+ |
+ // Handler not found. |
+ return false; |
+} |
+ |
+ |
Object* Isolate::ThrowIllegalOperation() { |
if (FLAG_stack_trace_on_illegal) PrintStack(stdout); |
- return Throw(heap_.illegal_access_string()); |
+ return Throw(heap()->illegal_access_string()); |
} |
@@ -1269,37 +1291,6 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target, |
} |
-bool Isolate::ShouldReportException(bool* can_be_caught_externally, |
- bool catchable_by_javascript) { |
- // Find the top-most try-catch handler. |
- StackHandler* handler = |
- StackHandler::FromAddress(Isolate::handler(thread_local_top())); |
- while (handler != NULL && !handler->is_catch()) { |
- handler = handler->next(); |
- } |
- |
- // Get the address of the external handler so we can compare the address to |
- // determine which one is closer to the top of the stack. |
- Address external_handler_address = |
- thread_local_top()->try_catch_handler_address(); |
- |
- // The exception has been externally caught if and only if there is |
- // an external handler which is on top of the top-most try-catch |
- // handler. |
- *can_be_caught_externally = external_handler_address != NULL && |
- (handler == NULL || handler->address() > external_handler_address || |
- !catchable_by_javascript); |
- |
- if (*can_be_caught_externally) { |
- // Only report the exception if the external handler is verbose. |
- return try_catch_handler()->is_verbose_; |
- } else { |
- // Report the exception if it isn't caught by JavaScript code. |
- return handler == NULL; |
- } |
-} |
- |
- |
// Traverse prototype chain to find out whether the object is derived from |
// the Error object. |
bool Isolate::IsErrorObject(Handle<Object> obj) { |
@@ -1371,32 +1362,53 @@ Handle<JSMessageObject> Isolate::CreateMessage(Handle<Object> exception, |
} |
-bool Isolate::IsFinallyOnTop() { |
+bool Isolate::IsJavaScriptHandlerOnTop(Object* exception) { |
+ DCHECK_NE(heap()->the_hole_value(), exception); |
+ |
+ // For uncatchable exceptions, the JavaScript handler cannot be on top. |
+ if (!is_catchable_by_javascript(exception)) return false; |
+ |
+ // Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist. |
+ Address entry_handler = Isolate::handler(thread_local_top()); |
+ if (entry_handler == nullptr) return false; |
+ |
// Get the address of the external handler so we can compare the address to |
// determine which one is closer to the top of the stack. |
- Address external_handler_address = |
- thread_local_top()->try_catch_handler_address(); |
- DCHECK(external_handler_address != NULL); |
- |
- // The exception has been externally caught if and only if there is |
- // an external handler which is on top of the top-most try-finally |
- // handler. |
- // There should be no try-catch blocks as they would prohibit us from |
- // finding external catcher in the first place (see catcher_ check above). |
+ Address external_handler = thread_local_top()->try_catch_handler_address(); |
+ if (external_handler == nullptr) return true; |
+ |
+ // The exception has been externally caught if and only if there is an |
+ // external handler which is on top of the top-most JS_ENTRY handler. |
// |
- // Note, that finally clause would rethrow an exception unless it's |
- // aborted by jumps in control flow like return, break, etc. and we'll |
- // have another chances to set proper v8::TryCatch. |
- StackHandler* handler = |
- StackHandler::FromAddress(Isolate::handler(thread_local_top())); |
- while (handler != NULL && handler->address() < external_handler_address) { |
- DCHECK(!handler->is_catch()); |
- if (handler->is_finally()) return true; |
+ // Note, that finally clauses would re-throw an exception unless it's aborted |
+ // by jumps in control flow (like return, break, etc.) and we'll have another |
+ // chance to set proper v8::TryCatch later. |
+ return (entry_handler < external_handler); |
+} |
- handler = handler->next(); |
- } |
- return false; |
+bool Isolate::IsExternalHandlerOnTop(Object* exception) { |
+ DCHECK_NE(heap()->the_hole_value(), exception); |
+ |
+ // Get the address of the external handler so we can compare the address to |
+ // determine which one is closer to the top of the stack. |
+ Address external_handler = thread_local_top()->try_catch_handler_address(); |
+ if (external_handler == nullptr) return false; |
+ |
+ // For uncatchable exceptions, the external handler is always on top. |
+ if (!is_catchable_by_javascript(exception)) return true; |
+ |
+ // Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist. |
+ Address entry_handler = Isolate::handler(thread_local_top()); |
+ if (entry_handler == nullptr) return true; |
+ |
+ // The exception has been externally caught if and only if there is an |
+ // external handler which is on top of the top-most JS_ENTRY handler. |
+ // |
+ // Note, that finally clauses would re-throw an exception unless it's aborted |
+ // by jumps in control flow (like return, break, etc.) and we'll have another |
+ // chance to set proper v8::TryCatch later. |
+ return (entry_handler > external_handler); |
} |
@@ -1413,25 +1425,32 @@ void Isolate::ReportPendingMessages() { |
Object* message_obj = thread_local_top_.pending_message_obj_; |
clear_pending_message(); |
- bool can_be_caught_externally = false; |
- bool catchable_by_javascript = is_catchable_by_javascript(exception); |
- bool should_report_exception = |
- ShouldReportException(&can_be_caught_externally, catchable_by_javascript); |
+ // For uncatchable exceptions we do nothing. If needed, the exception and the |
+ // message have already been propagated to v8::TryCatch. |
+ if (!is_catchable_by_javascript(exception)) return; |
- if (!catchable_by_javascript) { |
- // Do nothing: if needed, the exception has been already propagated to |
- // v8::TryCatch. |
+ // Determine whether the message needs to be reported to all message handlers |
+ // depending on whether and external v8::TryCatch or an internal JavaScript |
+ // handler is on top. |
+ bool should_report_exception; |
+ if (IsExternalHandlerOnTop(exception)) { |
+ // Only report the exception if the external handler is verbose. |
+ should_report_exception = try_catch_handler()->is_verbose_; |
} else { |
- if (!message_obj->IsTheHole() && should_report_exception) { |
- HandleScope scope(this); |
- Handle<JSMessageObject> message(JSMessageObject::cast(message_obj)); |
- Handle<JSValue> script_wrapper(JSValue::cast(message->script())); |
- Handle<Script> script(Script::cast(script_wrapper->value())); |
- int start_pos = message->start_position(); |
- int end_pos = message->end_position(); |
- MessageLocation location(script, start_pos, end_pos); |
- MessageHandler::ReportMessage(this, &location, message); |
- } |
+ // Report the exception if it isn't caught by JavaScript code. |
+ should_report_exception = !IsJavaScriptHandlerOnTop(exception); |
+ } |
+ |
+ // Actually report the pending message to all message handlers. |
+ if (!message_obj->IsTheHole() && should_report_exception) { |
+ HandleScope scope(this); |
+ Handle<JSMessageObject> message(JSMessageObject::cast(message_obj)); |
+ Handle<JSValue> script_wrapper(JSValue::cast(message->script())); |
+ Handle<Script> script(Script::cast(script_wrapper->value())); |
+ int start_pos = message->start_position(); |
+ int end_pos = message->end_position(); |
+ MessageLocation location(script, start_pos, end_pos); |
+ MessageHandler::ReportMessage(this, &location, message); |
} |
} |
@@ -1939,21 +1958,18 @@ void Isolate::InitializeThreadLocal() { |
bool Isolate::PropagatePendingExceptionToExternalTryCatch() { |
Object* exception = pending_exception(); |
- bool can_be_caught_externally = false; |
- bool catchable_by_javascript = is_catchable_by_javascript(exception); |
- ShouldReportException(&can_be_caught_externally, catchable_by_javascript); |
- if (!can_be_caught_externally) { |
+ if (IsJavaScriptHandlerOnTop(exception)) { |
thread_local_top_.external_caught_exception_ = false; |
- return true; |
+ return false; |
} |
- if (catchable_by_javascript && IsFinallyOnTop()) { |
+ if (!IsExternalHandlerOnTop(exception)) { |
thread_local_top_.external_caught_exception_ = false; |
- return false; |
+ return true; |
} |
thread_local_top_.external_caught_exception_ = true; |
- if (!catchable_by_javascript) { |
+ if (!is_catchable_by_javascript(exception)) { |
try_catch_handler()->can_continue_ = false; |
try_catch_handler()->has_terminated_ = true; |
try_catch_handler()->exception_ = heap()->null_value(); |