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;
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);
}
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);
}
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>();
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;
}
// is complete.
pending_exception_ = NULL;
has_pending_message_ = false;
+ rethrowing_message_ = false;
pending_message_obj_ = NULL;
pending_message_script_ = NULL;
scheduled_exception_ = NULL;
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.
}
+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();
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) {
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);
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);
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_;
}
}
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
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();
// 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();
}
+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();