Added source info to TryCatches. Reorganized exception messaging
authorchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 9 Sep 2008 18:55:41 +0000 (18:55 +0000)
committerchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 9 Sep 2008 18:55:41 +0000 (18:55 +0000)
somewhat and folded stack traces into message.  Use of this in the
shell will follow in a separate changelist.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@235 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/debug.cc
src/messages.cc
src/messages.h
src/messages.js
src/top.cc
src/top.h
test/cctest/test-api.cc

index ebbc6ef..a8f7211 100644 (file)
@@ -553,7 +553,7 @@ class EXPORT Script {
 class EXPORT Message {
  public:
   Local<String> Get();
-  Local<Value> GetSourceLine();
+  Local<String> GetSourceLine();
 
   // TODO(1241256): Rewrite (or remove) this method.  We don't want to
   // deal with ownership of the returned string and we want to use
@@ -566,8 +566,41 @@ class EXPORT Message {
   // bindings.
   Handle<Value> GetSourceData();
 
+  /**
+   * Returns the number, 1-based, of the line where the error occurred.
+   */
   int GetLineNumber();
 
+  /**
+   * Returns the index within the script of the first character where
+   * the error occurred.
+   */
+  int GetStartPosition();
+
+  /**
+   * Returns the index within the script of the last character where
+   * the error occurred.
+   */
+  int GetEndPosition();
+
+  /**
+   * Returns the index within the line of the first character where
+   * the error occurred.
+   */
+  int GetStartColumn();
+
+  /**
+   * Returns the index within the line of the last character where
+   * the error occurred.
+   */
+  int GetEndColumn();
+
+  /**
+   * Returns a string stack trace if trace_exceptions is enabled and
+   * one is available.
+   */
+  Local<String> GetStackTrace();
+
   // TODO(1245381): Print to a string instead of on a FILE.
   static void PrintCurrentStackTrace(FILE* out);
 };
@@ -1912,6 +1945,15 @@ class EXPORT TryCatch {
   Local<Value> Exception();
 
   /**
+   * Returns the message associated with this exception.  If there is
+   * no message associated an empty handle is returned.
+   *
+   * The returned handle is valid until this TryCatch block has been
+   * destroyed.
+   */
+  Local<v8::Message> Message();
+
+  /**
    * Clears any exceptions that may have been caught by this try/catch block.
    * After this method has been called, HasCaught() will return false.
    *
@@ -1935,6 +1977,7 @@ class EXPORT TryCatch {
  public:
   TryCatch* next_;
   void* exception_;
+  void* message_;
   bool is_verbose_;
 };
 
index f8bd63f..0c5405e 100644 (file)
@@ -1081,6 +1081,7 @@ Local<Value> Script::Run() {
 v8::TryCatch::TryCatch()
     : next_(i::Top::try_catch_handler()),
       exception_(i::Heap::the_hole_value()),
+      message_(i::Smi::FromInt(0)),
       is_verbose_(false) {
   i::Top::RegisterTryCatchHandler(this);
 }
@@ -1107,8 +1108,19 @@ v8::Local<Value> v8::TryCatch::Exception() {
 }
 
 
+v8::Local<v8::Message> v8::TryCatch::Message() {
+  if (HasCaught() && message_ != i::Smi::FromInt(0)) {
+    i::Object* message = reinterpret_cast<i::Object*>(message_);
+    return v8::Utils::MessageToLocal(i::Handle<i::Object>(message));
+  } else {
+    return v8::Local<v8::Message>();
+  }
+}
+
+
 void v8::TryCatch::Reset() {
   exception_ = i::Heap::the_hole_value();
+  message_ = i::Smi::FromInt(0);
 }
 
 
@@ -1196,15 +1208,80 @@ int Message::GetLineNumber() {
 }
 
 
-Local<Value> Message::GetSourceLine() {
-  ON_BAILOUT("v8::Message::GetSourceLine()", return Local<Value>());
+int Message::GetStartPosition() {
+  if (IsDeadCheck("v8::Message::GetStartPosition()")) return 0;
+  HandleScope scope;
+
+  i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+  return static_cast<int>(GetProperty(data_obj, "startPos")->Number());
+}
+
+
+int Message::GetEndPosition() {
+  if (IsDeadCheck("v8::Message::GetEndPosition()")) return 0;
+  HandleScope scope;
+  i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+  return static_cast<int>(GetProperty(data_obj, "endPos")->Number());
+}
+
+
+int Message::GetStartColumn() {
+  if (IsDeadCheck("v8::Message::GetStartColumn()")) return 0;
+  HandleScope scope;
+  i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+  EXCEPTION_PREAMBLE();
+  i::Handle<i::Object> start_col_obj = CallV8HeapFunction(
+      "GetPositionInLine",
+      data_obj,
+      &has_pending_exception);
+  EXCEPTION_BAILOUT_CHECK(0);
+  return static_cast<int>(start_col_obj->Number());
+}
+
+
+int Message::GetEndColumn() {
+  if (IsDeadCheck("v8::Message::GetEndColumn()")) return 0;
+  HandleScope scope;
+  i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+  EXCEPTION_PREAMBLE();
+  i::Handle<i::Object> start_col_obj = CallV8HeapFunction(
+      "GetPositionInLine",
+      data_obj,
+      &has_pending_exception);
+  EXCEPTION_BAILOUT_CHECK(0);
+  int start = static_cast<int>(GetProperty(data_obj, "startPos")->Number());
+  int end = static_cast<int>(GetProperty(data_obj, "endPos")->Number());
+  return static_cast<int>(start_col_obj->Number()) + (end - start);
+}
+
+
+v8::Local<v8::String> Message::GetStackTrace() {
+  if (IsDeadCheck("v8::Message::GetStackTrace()"))
+    return v8::Local<v8::String>();
+  HandleScope scope;
+  i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
+  i::Handle<i::Object> trace = GetProperty(data_obj, "stackTrace");
+  if (trace->IsString()) {
+    return scope.Close(Utils::ToLocal(i::Handle<i::String>::cast(trace)));
+  } else {
+    return Local<String>();
+  }
+}
+
+
+Local<String> Message::GetSourceLine() {
+  ON_BAILOUT("v8::Message::GetSourceLine()", return Local<String>());
   HandleScope scope;
   EXCEPTION_PREAMBLE();
   i::Handle<i::Object> result = CallV8HeapFunction("GetSourceLine",
                                                    Utils::OpenHandle(this),
                                                    &has_pending_exception);
-  EXCEPTION_BAILOUT_CHECK(Local<v8::Value>());
-  return scope.Close(Utils::ToLocal(result));
+  EXCEPTION_BAILOUT_CHECK(Local<v8::String>());
+  if (result->IsString()) {
+    return scope.Close(Utils::ToLocal(i::Handle<i::String>::cast(result)));
+  } else {
+    return Local<String>();
+  }
 }
 
 
index 7487cdc..8de46c1 100644 (file)
@@ -537,8 +537,10 @@ bool Debug::CompileDebuggerScript(int index) {
 
   // Check for caught exceptions.
   if (caught_exception) {
-    MessageHandler::ReportMessage("error_loading_debugger", NULL,
-                                  HandleVector<Object>(&result, 1));
+    Handle<Object> message = MessageHandler::MakeMessageObject(
+        "error_loading_debugger", NULL, HandleVector<Object>(&result, 1),
+        Handle<String>());
+    MessageHandler::ReportMessage(NULL, message);
     return false;
   }
 
index a50bf61..76027b3 100644 (file)
@@ -60,8 +60,11 @@ void MessageHandler::ReportMessage(const char* msg) {
 }
 
 
-void MessageHandler::ReportMessage(const char* type, MessageLocation* loc,
-                                   Vector< Handle<Object> > args) {
+Handle<Object> MessageHandler::MakeMessageObject(
+    const char* type,
+    MessageLocation* loc,
+    Vector< Handle<Object> > args,
+    Handle<String> stack_trace) {
   // Build error message object
   HandleScope scope;
   Handle<Object> type_str = Factory::LookupAsciiSymbol(type);
@@ -82,12 +85,16 @@ void MessageHandler::ReportMessage(const char* type, MessageLocation* loc,
   }
   Handle<Object> start_handle(Smi::FromInt(start));
   Handle<Object> end_handle(Smi::FromInt(end));
-  const int argc = 5;
+  Handle<Object> stack_trace_val = stack_trace.is_null()
+    ? Factory::undefined_value()
+    : Handle<Object>::cast(stack_trace);
+  const int argc = 6;
   Object** argv[argc] = { type_str.location(),
                           array.location(),
                           start_handle.location(),
                           end_handle.location(),
-                          script.location() };
+                          script.location(),
+                          stack_trace_val.location() };
 
   bool caught_exception = false;
   Handle<Object> message =
@@ -97,8 +104,13 @@ void MessageHandler::ReportMessage(const char* type, MessageLocation* loc,
   // skip doing the callback. This usually only happens in case of
   // stack overflow exceptions being thrown by the parser when the
   // stack is almost full.
-  if (caught_exception) return;
+  if (caught_exception) return Handle<Object>();
+  return message.EscapeFrom(&scope);
+}
+
 
+void MessageHandler::ReportMessage(MessageLocation* loc,
+                                   Handle<Object> message) {
   v8::Local<v8::Message> api_message_obj = v8::Utils::MessageToLocal(message);
 
   v8::NeanderArray global_listeners(Factory::message_listeners());
index 2e1dda4..f409a71 100644 (file)
@@ -70,6 +70,7 @@ class MessageLocation {
       : script_(script),
         start_pos_(start_pos),
         end_pos_(end_pos) { }
+  MessageLocation() : start_pos_(-1), end_pos_(-1) { }
 
   Handle<Script> script() const { return script_; }
   int start_pos() const { return start_pos_; }
@@ -89,10 +90,14 @@ class MessageHandler {
   // Report a message (w/o JS heap allocation).
   static void ReportMessage(const char* msg);
 
+  // Returns a message object for the API to use.
+  static Handle<Object> MakeMessageObject(const char* type,
+                                          MessageLocation* loc,
+                                          Vector< Handle<Object> > args,
+                                          Handle<String> stack_trace);
+
   // Report a formatted message (needs JS allocation).
-  static void ReportMessage(const char* type,
-                            MessageLocation* loc,
-                            Vector< Handle<Object> > args);
+  static void ReportMessage(MessageLocation* loc, Handle<Object> message);
 
   static void DefaultMessageReport(const MessageLocation* loc,
                                    Handle<Object> message_obj);
index 49b0241..1270ffb 100644 (file)
@@ -555,17 +555,18 @@ function GetPositionInLine(message) {
 };
 
 
-function ErrorMessage(type, args, startPos, endPos, script) {
+function ErrorMessage(type, args, startPos, endPos, script, stackTrace) {
   this.startPos = startPos;
   this.endPos = endPos;
   this.type = type;
   this.args = args;
   this.script = script;
+  this.stackTrace = stackTrace;
 };
 
 
-function MakeMessage(type, args, startPos, endPos, script) {
-  return new ErrorMessage(type, args, startPos, endPos, script);
+function MakeMessage(type, args, startPos, endPos, script, stackTrace) {
+  return new ErrorMessage(type, args, startPos, endPos, script, stackTrace);
 };
 
 
index 94e4cc8..b7b5099 100644 (file)
@@ -79,6 +79,7 @@ void Top::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
        block != NULL;
        block = block->next_) {
     VISIT(reinterpret_cast<Object*&>(block->exception_));
+    VISIT(reinterpret_cast<Object*&>(block->message_));
   }
 
   // Iterate over pointers on native execution stack.
@@ -671,39 +672,35 @@ void Top::PrintCurrentStackTrace(FILE* out) {
 }
 
 
+void Top::ComputeLocation(MessageLocation* target) {
+  *target = MessageLocation(empty_script(), -1, -1);
+  StackTraceFrameIterator it;
+  if (!it.done()) {
+    JavaScriptFrame* frame = it.frame();
+    JSFunction* fun = JSFunction::cast(frame->function());
+    Object* script = fun->shared()->script();
+    if (script->IsScript() &&
+        !(Script::cast(script)->source()->IsUndefined())) {
+      int pos = frame->FindCode()->SourcePosition(frame->pc());
+      // Compute the location from the function and the reloc info.
+      Handle<Script> casted_script(Script::cast(script));
+      *target = MessageLocation(casted_script, pos, pos + 1);
+    }
+  }
+}
+
+
 void Top::ReportUncaughtException(Handle<Object> exception,
                                   MessageLocation* location,
                                   Handle<String> stack_trace) {
-  MessageLocation computed_location(empty_script(), -1, -1);
-  if (location == NULL) {
-    location = &computed_location;
-
-    StackTraceFrameIterator it;
-    if (!it.done()) {
-      JavaScriptFrame* frame = it.frame();
-      JSFunction* fun = JSFunction::cast(frame->function());
-      Object* script = fun->shared()->script();
-      if (script->IsScript() &&
-          !(Script::cast(script)->source()->IsUndefined())) {
-        int pos = frame->FindCode()->SourcePosition(frame->pc());
-        // Compute the location from the function and the reloc info.
-        Handle<Script> casted_script(Script::cast(script));
-        computed_location = MessageLocation(casted_script, pos, pos + 1);
-      }
-    }
-  }
+  Handle<Object> message =
+    MessageHandler::MakeMessageObject("uncaught_exception",
+                                      location,
+                                      HandleVector<Object>(&exception, 1),
+                                      stack_trace);
 
   // Report the uncaught exception.
-  MessageHandler::ReportMessage("uncaught_exception",
-                                location,
-                                HandleVector<Object>(&exception, 1));
-
-  // Optionally, report the stack trace separately.
-  if (!stack_trace.is_null()) {
-    MessageHandler::ReportMessage("stack_trace",
-                                  location,
-                                  HandleVector<String>(&stack_trace, 1));
-  }
+  MessageHandler::ReportMessage(location, message);
 }
 
 
@@ -771,12 +768,30 @@ void Top::DoThrow(Object* exception,
     ShouldReportException(&is_caught_externally);
   if (is_rethrow) report_exception = false;
 
+  Handle<Object> message_obj;
+  MessageLocation potential_computed_location;
+  if (is_caught_externally || report_exception) {
+    if (location == NULL) {
+      // If no location was specified we use a computed one instead
+      ComputeLocation(&potential_computed_location);
+      location = &potential_computed_location;
+    }
+    Handle<String> stack_trace;
+    if (FLAG_trace_exception) stack_trace = StackTrace();
+    message_obj = MessageHandler::MakeMessageObject("uncaught_exception",
+        location, HandleVector<Object>(&exception_handle, 1), stack_trace);
+  }
+
   // If the exception is caught externally, we store it in the
   // try/catch handler. The C code can find it later and process it if
   // necessary.
   if (is_caught_externally) {
     thread_local_.try_catch_handler_->exception_ =
       reinterpret_cast<void*>(*exception_handle);
+    if (!message_obj.is_null()) {
+      thread_local_.try_catch_handler_->message_ =
+        reinterpret_cast<void*>(*message_obj);
+    }
   }
 
   // Notify debugger of exception.
@@ -786,9 +801,7 @@ void Top::DoThrow(Object* exception,
     if (message != NULL) {
       MessageHandler::ReportMessage(message);
     } else {
-      Handle<String> stack_trace;
-      if (FLAG_trace_exception) stack_trace = StackTrace();
-      ReportUncaughtException(exception_handle, location, stack_trace);
+      MessageHandler::ReportMessage(location, message_obj);
     }
   }
   thread_local_.external_caught_exception_ = is_caught_externally;
index fffdb8a..55e7386 100644 (file)
--- a/src/top.h
+++ b/src/top.h
@@ -226,6 +226,10 @@ class Top {
                                       MessageLocation* location,
                                       Handle<String> stack_trace);
 
+  // Attempts to compute the current source location, storing the
+  // result in the target out parameter.
+  static void ComputeLocation(MessageLocation* target);
+
   // Override command line flag.
   static void TraceException(bool flag);
 
index a550e5e..53035b2 100644 (file)
@@ -4843,3 +4843,40 @@ THREADED_TEST(Regress54) {
   v8::Handle<v8::Object> result = templ->NewInstance();
   CHECK_EQ(1, result->InternalFieldCount());
 }
+
+
+THREADED_TEST(TryCatchSourceInfo) {
+  v8::HandleScope scope;
+  LocalContext context;
+  v8::Handle<v8::String> source = v8::String::New(
+      "function Foo() {\n"
+      "  return Bar();\n"
+      "}\n"
+      "\n"
+      "function Bar() {\n"
+      "  return Baz();\n"
+      "}\n"
+      "\n"
+      "function Baz() {\n"
+      "  throw 'nirk';\n"
+      "}\n"
+      "\n"
+      "Foo();\n");
+  v8::Handle<v8::Script> script =
+    v8::Script::Compile(source, v8::String::New("test.js"));
+  v8::TryCatch try_catch;
+  v8::Handle<v8::Value> result = script->Run();
+  CHECK(result.IsEmpty());
+  CHECK(try_catch.HasCaught());
+  v8::Handle<v8::Message> message = try_catch.Message();
+  CHECK(!message.IsEmpty());
+  CHECK_EQ(10, message->GetLineNumber());
+  CHECK_EQ(91, message->GetStartPosition());
+  CHECK_EQ(92, message->GetEndPosition());
+  CHECK_EQ(2, message->GetStartColumn());
+  CHECK_EQ(3, message->GetEndColumn());
+  v8::String::AsciiValue line(message->GetSourceLine());
+  CHECK_EQ("  throw 'nirk';", *line);
+  v8::String::AsciiValue name(message->GetScriptResourceName());
+  CHECK_EQ("test.js", *name);
+}