Remove kind field from StackHandler.

This relands commit 96f79568a9.

This makes the Isolate::Throw logic not depend on a prediction of
whether an exception is caught or uncaught. Such a prediction is
inherently undecidable because a finally block can decide between
consuming or re-throwing an exception depending on arbitray control
flow.

There still is a conservative prediction mechanism in place that
components like the debugger or tracing can use for reporting.

With this change we can get rid of the StackHandler::kind field, a
pre-requisite to do table-based lookups of exception handlers.

R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/997213003

Cr-Commit-Position: refs/heads/master@{#27263}
This commit is contained in:
mstarzinger 2015-03-18 03:19:04 -07:00 committed by Commit bot
parent 0e024449b8
commit 15f8213809
11 changed files with 152 additions and 183 deletions

View File

@ -1406,13 +1406,10 @@ void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
// For the JSEntry handler, we must preserve r0-r4, r5-r6 are available.
// We will build up the handler from the bottom by pushing on the stack.
// Set up the the state (r6) for pushing.
unsigned state =
StackHandler::IndexField::encode(handler_index) |
StackHandler::KindField::encode(kind);
mov(r6, Operand(state));
// Set up the the index (r6) for pushing.
mov(r6, Operand(handler_index));
// Push the context and state.
// Push the context and index.
if (kind == StackHandler::JS_ENTRY) {
mov(cp, Operand(Smi::FromInt(0))); // Indicates no context.
stm(db_w, sp, r6.bit() | cp.bit());

View File

@ -3050,12 +3050,8 @@ void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
// For the JSEntry handler, we must preserve the live registers x0-x4.
// (See JSEntryStub::GenerateBody().)
unsigned state =
StackHandler::IndexField::encode(handler_index) |
StackHandler::KindField::encode(kind);
// Set up the state for pushing.
Mov(x11, state);
// Set up the index for pushing.
Mov(x11, handler_index);
// Push the context and state.
if (kind == StackHandler::JS_ENTRY) {

View File

@ -2456,7 +2456,7 @@ MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) {
}
void Debug::OnThrow(Handle<Object> exception, bool uncaught) {
void Debug::OnThrow(Handle<Object> exception) {
if (in_debug_scope() || ignore_events()) return;
// Temporarily clear any scheduled_exception to allow evaluating
// JavaScript from the debug event handler.
@ -2466,7 +2466,7 @@ void Debug::OnThrow(Handle<Object> exception, bool uncaught) {
scheduled_exception = handle(isolate_->scheduled_exception(), isolate_);
isolate_->clear_scheduled_exception();
}
OnException(exception, uncaught, isolate_->GetPromiseOnStackOnThrow());
OnException(exception, isolate_->GetPromiseOnStackOnThrow());
if (!scheduled_exception.is_null()) {
isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception;
}
@ -2479,7 +2479,7 @@ void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) {
// Check whether the promise has been marked as having triggered a message.
Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();
if (JSObject::GetDataProperty(promise, key)->IsUndefined()) {
OnException(value, false, promise);
OnException(value, promise);
}
}
@ -2494,9 +2494,9 @@ MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler(
}
void Debug::OnException(Handle<Object> exception, bool uncaught,
Handle<Object> promise) {
if (!uncaught && promise->IsJSObject()) {
void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
bool uncaught = !isolate_->PredictWhetherExceptionIsCaught(*exception);
if (promise->IsJSObject()) {
Handle<JSObject> jspromise = Handle<JSObject>::cast(promise);
// Mark the promise as already having triggered a message.
Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol();

View File

@ -425,7 +425,7 @@ class Debug {
// Debug event triggers.
void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
void OnThrow(Handle<Object> exception, bool uncaught);
void OnThrow(Handle<Object> exception);
void OnPromiseReject(Handle<JSObject> promise, Handle<Object> value);
void OnCompileError(Handle<Script> script);
void OnBeforeCompile(Handle<Script> script);
@ -595,8 +595,7 @@ class Debug {
return break_disabled_ || in_debug_event_listener_;
}
void OnException(Handle<Object> exception, bool uncaught,
Handle<Object> promise);
void OnException(Handle<Object> exception, Handle<Object> promise);
// Constructors for debug event objects.
MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(

View File

@ -61,36 +61,15 @@ inline StackHandler* StackHandler::FromAddress(Address address) {
}
inline bool StackHandler::is_js_entry() const {
return kind() == JS_ENTRY;
}
inline bool StackHandler::is_catch() const {
return kind() == CATCH;
}
inline bool StackHandler::is_finally() const {
return kind() == FINALLY;
}
inline Context* StackHandler::context() const {
const int offset = StackHandlerConstants::kContextOffset;
return Context::cast(Memory::Object_at(address() + offset));
}
inline StackHandler::Kind StackHandler::kind() const {
inline int StackHandler::index() const {
const int offset = StackHandlerConstants::kStateIntOffset;
return KindField::decode(Memory::unsigned_at(address() + offset));
}
inline unsigned StackHandler::index() const {
const int offset = StackHandlerConstants::kStateIntOffset;
return IndexField::decode(Memory::unsigned_at(address() + offset));
return Memory::int_at(address() + offset);
}

View File

@ -1338,7 +1338,6 @@ void EntryFrame::Iterate(ObjectVisitor* v) const {
StackHandlerIterator it(this, top_handler());
DCHECK(!it.done());
StackHandler* handler = it.handler();
DCHECK(handler->is_js_entry());
handler->Iterate(v, LookupCode());
#ifdef DEBUG
// Make sure that the entry frame does not contain more than one

View File

@ -88,15 +88,8 @@ class StackHandler BASE_EMBEDDED {
JS_ENTRY,
CATCH,
FINALLY,
LAST_KIND = FINALLY
};
static const int kKindWidth = 2;
STATIC_ASSERT(LAST_KIND < (1 << kKindWidth));
static const int kIndexWidth = 32 - kKindWidth;
class KindField: public BitField<StackHandler::Kind, 0, kKindWidth> {};
class IndexField: public BitField<unsigned, kKindWidth, kIndexWidth> {};
// Get the address of this stack handler.
inline Address address() const;
@ -114,13 +107,7 @@ class StackHandler BASE_EMBEDDED {
// Accessors.
inline Context* context() const;
inline Kind kind() const;
inline unsigned index() const;
// Testers.
inline bool is_js_entry() const;
inline bool is_catch() const;
inline bool is_finally() const;
inline int index() const;
// Generator support to preserve stack handlers.
void Unwind(Isolate* isolate, FixedArray* array, int offset,

View File

@ -1038,11 +1038,9 @@ void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
} else {
push(esi);
}
// Push the state.
unsigned state =
StackHandler::IndexField::encode(handler_index) |
StackHandler::KindField::encode(kind);
push(Immediate(state));
// Push the index.
push(Immediate(handler_index));
// Link the current handler as the next handler.
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());

View File

@ -929,39 +929,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.
@ -976,16 +971,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);
@ -1042,7 +1036,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.
@ -1058,7 +1051,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();
@ -1111,9 +1103,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());
}
@ -1264,37 +1286,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) {
@ -1366,32 +1357,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);
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 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).
// 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);
}
@ -1408,25 +1420,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);
}
}
@ -1934,21 +1953,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) {
thread_local_top_.external_caught_exception_ = false;
return true;
}
if (catchable_by_javascript && IsFinallyOnTop()) {
if (IsJavaScriptHandlerOnTop(exception)) {
thread_local_top_.external_caught_exception_ = false;
return false;
}
if (!IsExternalHandlerOnTop(exception)) {
thread_local_top_.external_caught_exception_ = 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();

View File

@ -629,7 +629,8 @@ class Isolate {
thread_local_top_.scheduled_exception_ = heap_.the_hole_value();
}
bool IsFinallyOnTop();
bool IsJavaScriptHandlerOnTop(Object* exception);
bool IsExternalHandlerOnTop(Object* exception);
bool is_catchable_by_javascript(Object* exception) {
return exception != heap()->termination_exception();
@ -743,6 +744,7 @@ class Isolate {
// Exception throwing support. The caller should use the result
// of Throw() as its return value.
Object* Throw(Object* exception, MessageLocation* location = NULL);
Object* ThrowIllegalOperation();
template <typename T>
MUST_USE_RESULT MaybeHandle<T> Throw(Handle<Object> exception,
@ -751,15 +753,20 @@ class Isolate {
return MaybeHandle<T>();
}
// Re-throw an exception. This involves no error reporting since
// error reporting was handled when the exception was thrown
// originally.
// Re-throw an exception. This involves no error reporting since error
// reporting was handled when the exception was thrown originally.
Object* ReThrow(Object* exception);
// Find the correct handler for the current pending exception. This also
// clears and returns the current pending exception.
Object* FindHandler();
// Tries to predict whether the exception will be caught. Note that this can
// only produce an estimate, because it is undecidable whether a finally
// clause will consume or re-throw an exception. We conservatively assume any
// finally clause will behave as if the exception were consumed.
bool PredictWhetherExceptionIsCaught(Object* exception);
void ScheduleThrow(Object* exception);
// Re-set pending message, script and positions reported to the TryCatch
// back to the TLS for re-use when rethrowing.
@ -769,14 +776,9 @@ class Isolate {
void ReportPendingMessages();
// Return pending location if any or unfilled structure.
MessageLocation GetMessageLocation();
Object* ThrowIllegalOperation();
// Promote a scheduled exception to pending. Asserts has_scheduled_exception.
Object* PromoteScheduledException();
// Checks if exception should be reported and finds out if it's
// caught externally.
bool ShouldReportException(bool* can_be_caught_externally,
bool catchable_by_javascript);
// Attempts to compute the current source location, storing the
// result in the target out parameter.
@ -803,7 +805,6 @@ class Isolate {
char* Iterate(ObjectVisitor* v, char* t);
void IterateThread(ThreadVisitor* v, char* t);
// Returns the current native context.
Handle<Context> native_context();

View File

@ -2995,11 +2995,8 @@ void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
Push(rsi);
}
// Push the state.
unsigned state =
StackHandler::IndexField::encode(handler_index) |
StackHandler::KindField::encode(kind);
Push(Immediate(state));
// Push the index.
Push(Immediate(handler_index));
// Link the current handler as the next handler.
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());