Commit cc2f01d3 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Restore message when rethrowing in TryCatch.

Based on a patch contributed by Andrew Paprocki <andrew@ishiboo.com>.

R=jkummerow@chromium.org
BUG=
TEST=cctest/test-api/TryCatchNestedSyntax

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15408 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ca90f4b0
......@@ -4823,7 +4823,10 @@ class V8EXPORT TryCatch {
v8::internal::Isolate* isolate_;
void* next_;
void* exception_;
void* message_;
void* message_obj_;
void* message_script_;
int message_start_pos_;
int message_end_pos_;
bool is_verbose_ : 1;
bool can_continue_ : 1;
bool capture_message_ : 1;
......
......@@ -2092,13 +2092,12 @@ void Script::SetData(v8::Handle<String> data) {
v8::TryCatch::TryCatch()
: isolate_(i::Isolate::Current()),
next_(isolate_->try_catch_handler_address()),
exception_(isolate_->heap()->the_hole_value()),
message_(i::Smi::FromInt(0)),
is_verbose_(false),
can_continue_(true),
capture_message_(true),
rethrow_(false),
has_terminated_(false) {
Reset();
isolate_->RegisterTryCatchHandler(this);
}
......@@ -2108,8 +2107,17 @@ v8::TryCatch::~TryCatch() {
if (rethrow_) {
v8::HandleScope scope(reinterpret_cast<Isolate*>(isolate_));
v8::Local<v8::Value> exc = v8::Local<v8::Value>::New(Exception());
if (HasCaught() && capture_message_) {
// If an exception was caught and rethrow_ is indicated, the saved
// message, script, and location need to be restored to Isolate TLS
// for reuse. capture_message_ needs to be disabled so that DoThrow()
// does not create a new message.
isolate_->thread_local_top()->rethrowing_message_ = true;
isolate_->RestorePendingMessageFromTryCatch(this);
}
isolate_->UnregisterTryCatchHandler(this);
v8::ThrowException(exc);
ASSERT(!isolate_->thread_local_top()->rethrowing_message_);
} else {
isolate_->UnregisterTryCatchHandler(this);
}
......@@ -2170,8 +2178,9 @@ v8::Local<Value> v8::TryCatch::StackTrace() const {
v8::Local<v8::Message> v8::TryCatch::Message() const {
ASSERT(isolate_ == i::Isolate::Current());
if (HasCaught() && message_ != i::Smi::FromInt(0)) {
i::Object* message = reinterpret_cast<i::Object*>(message_);
i::Object* message = reinterpret_cast<i::Object*>(message_obj_);
ASSERT(message->IsJSMessageObject() || message->IsTheHole());
if (HasCaught() && !message->IsTheHole()) {
return v8::Utils::MessageToLocal(i::Handle<i::Object>(message, isolate_));
} else {
return v8::Local<v8::Message>();
......@@ -2181,8 +2190,12 @@ v8::Local<v8::Message> v8::TryCatch::Message() const {
void v8::TryCatch::Reset() {
ASSERT(isolate_ == i::Isolate::Current());
exception_ = isolate_->heap()->the_hole_value();
message_ = i::Smi::FromInt(0);
i::Object* the_hole = isolate_->heap()->the_hole_value();
exception_ = the_hole;
message_obj_ = the_hole;
message_script_ = the_hole;
message_start_pos_ = 0;
message_end_pos_ = 0;
}
......
......@@ -107,6 +107,7 @@ void ThreadLocalTop::InitializeInternal() {
// is complete.
pending_exception_ = NULL;
has_pending_message_ = false;
rethrowing_message_ = false;
pending_message_obj_ = NULL;
pending_message_script_ = NULL;
scheduled_exception_ = NULL;
......@@ -486,7 +487,8 @@ void Isolate::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
block != NULL;
block = TRY_CATCH_FROM_ADDRESS(block->next_)) {
v->VisitPointer(BitCast<Object**>(&(block->exception_)));
v->VisitPointer(BitCast<Object**>(&(block->message_)));
v->VisitPointer(BitCast<Object**>(&(block->message_obj_)));
v->VisitPointer(BitCast<Object**>(&(block->message_script_)));
}
// Iterate over pointers on native execution stack.
......@@ -1162,6 +1164,22 @@ void Isolate::ScheduleThrow(Object* exception) {
}
void Isolate::RestorePendingMessageFromTryCatch(v8::TryCatch* handler) {
ASSERT(handler == try_catch_handler());
ASSERT(handler->HasCaught());
ASSERT(handler->rethrow_);
ASSERT(handler->capture_message_);
Object* message = reinterpret_cast<Object*>(handler->message_obj_);
Object* script = reinterpret_cast<Object*>(handler->message_script_);
ASSERT(message->IsJSMessageObject() || message->IsTheHole());
ASSERT(script->IsScript() || script->IsTheHole());
thread_local_top()->pending_message_obj_ = message;
thread_local_top()->pending_message_script_ = script;
thread_local_top()->pending_message_start_pos_ = handler->message_start_pos_;
thread_local_top()->pending_message_end_pos_ = handler->message_end_pos_;
}
Failure* Isolate::PromoteScheduledException() {
MaybeObject* thrown = scheduled_exception();
clear_scheduled_exception();
......@@ -1280,9 +1298,12 @@ void Isolate::DoThrow(Object* exception, MessageLocation* location) {
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_;
can_be_caught_externally && try_catch_handler()->capture_message_ &&
!thread_local_top()->rethrowing_message_;
bool bootstrapping = bootstrapper()->IsActive();
thread_local_top()->rethrowing_message_ = false;
#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger of exception.
if (catchable_by_javascript) {
......@@ -1464,8 +1485,9 @@ void Isolate::ReportPendingMessages() {
HandleScope scope(this);
Handle<Object> message_obj(thread_local_top_.pending_message_obj_,
this);
if (thread_local_top_.pending_message_script_ != NULL) {
Handle<Script> script(thread_local_top_.pending_message_script_);
if (!thread_local_top_.pending_message_script_->IsTheHole()) {
Handle<Script> script(
Script::cast(thread_local_top_.pending_message_script_));
int start_pos = thread_local_top_.pending_message_start_pos_;
int end_pos = thread_local_top_.pending_message_end_pos_;
MessageLocation location(script, start_pos, end_pos);
......@@ -1487,8 +1509,9 @@ MessageLocation Isolate::GetMessageLocation() {
thread_local_top_.pending_exception_ != heap()->termination_exception() &&
thread_local_top_.has_pending_message_ &&
!thread_local_top_.pending_message_obj_->IsTheHole() &&
thread_local_top_.pending_message_script_ != NULL) {
Handle<Script> script(thread_local_top_.pending_message_script_);
!thread_local_top_.pending_message_obj_->IsTheHole()) {
Handle<Script> script(
Script::cast(thread_local_top_.pending_message_script_));
int start_pos = thread_local_top_.pending_message_start_pos_;
int end_pos = thread_local_top_.pending_message_end_pos_;
return MessageLocation(script, start_pos, end_pos);
......@@ -2039,15 +2062,24 @@ void Isolate::PropagatePendingExceptionToExternalTryCatch() {
try_catch_handler()->has_terminated_ = true;
try_catch_handler()->exception_ = heap()->null_value();
} else {
v8::TryCatch* handler = try_catch_handler();
// At this point all non-object (failure) exceptions have
// been dealt with so this shouldn't fail.
ASSERT(!pending_exception()->IsFailure());
try_catch_handler()->can_continue_ = true;
try_catch_handler()->has_terminated_ = false;
try_catch_handler()->exception_ = pending_exception();
if (!thread_local_top_.pending_message_obj_->IsTheHole()) {
try_catch_handler()->message_ = thread_local_top_.pending_message_obj_;
}
ASSERT(thread_local_top_.pending_message_obj_->IsJSMessageObject() ||
thread_local_top_.pending_message_obj_->IsTheHole());
ASSERT(thread_local_top_.pending_message_script_->IsScript() ||
thread_local_top_.pending_message_script_->IsTheHole());
handler->can_continue_ = true;
handler->has_terminated_ = false;
handler->exception_ = pending_exception();
// Propagate to the external try-catch only if we got an actual message.
if (thread_local_top_.pending_message_obj_->IsTheHole()) return;
handler->message_obj_ = thread_local_top_.pending_message_obj_;
handler->message_script_ = thread_local_top_.pending_message_script_;
handler->message_start_pos_ = thread_local_top_.pending_message_start_pos_;
handler->message_end_pos_ = thread_local_top_.pending_message_end_pos_;
}
}
......
......@@ -246,8 +246,9 @@ class ThreadLocalTop BASE_EMBEDDED {
ThreadId thread_id_;
MaybeObject* pending_exception_;
bool has_pending_message_;
bool rethrowing_message_;
Object* pending_message_obj_;
Script* pending_message_script_;
Object* pending_message_script_;
int pending_message_start_pos_;
int pending_message_end_pos_;
// Use a separate value for scheduled exceptions to preserve the
......@@ -582,7 +583,7 @@ class Isolate {
void clear_pending_message() {
thread_local_top_.has_pending_message_ = false;
thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
thread_local_top_.pending_message_script_ = NULL;
thread_local_top_.pending_message_script_ = heap_.the_hole_value();
}
v8::TryCatch* try_catch_handler() {
return thread_local_top_.TryCatchHandler();
......@@ -760,6 +761,9 @@ class Isolate {
// originally.
Failure* ReThrow(MaybeObject* 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.
void RestorePendingMessageFromTryCatch(v8::TryCatch* handler);
void ReportPendingMessages();
// Return pending location if any or unfilled structure.
MessageLocation GetMessageLocation();
......
......@@ -4502,6 +4502,47 @@ TEST(TryCatchNested) {
}
void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) {
CHECK(try_catch->HasCaught());
Handle<Message> message = try_catch->Message();
Handle<Value> resource = message->GetScriptResourceName();
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(resource), "inner"));
CHECK_EQ(0, strcmp(*v8::String::Utf8Value(message->Get()),
"Uncaught Error: a"));
CHECK_EQ(1, message->GetLineNumber());
CHECK_EQ(6, message->GetStartColumn());
}
void TryCatchMixedNestingHelper(
const v8::FunctionCallbackInfo<v8::Value>& args) {
ApiTestFuzzer::Fuzz();
v8::TryCatch try_catch;
CompileRunWithOrigin("throw new Error('a');\n", "inner", 0, 0);
CHECK(try_catch.HasCaught());
TryCatchMixedNestingCheck(&try_catch);
try_catch.ReThrow();
}
// This test ensures that an outer TryCatch in the following situation:
// C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError
// does not clobber the Message object generated for the inner TryCatch.
// This exercises the ability of TryCatch.ReThrow() to restore the
// inner pending Message before throwing the exception again.
TEST(TryCatchMixedNesting) {
v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::V8::Initialize();
v8::TryCatch try_catch;
Local<ObjectTemplate> templ = ObjectTemplate::New();
templ->Set(v8_str("TryCatchMixedNestingHelper"),
v8::FunctionTemplate::New(TryCatchMixedNestingHelper));
LocalContext context(0, templ);
CompileRunWithOrigin("TryCatchMixedNestingHelper();\n", "outer", 1, 1);
TryCatchMixedNestingCheck(&try_catch);
}
THREADED_TEST(Equality) {
LocalContext context;
v8::Isolate* isolate = context->GetIsolate();
......
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