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
// 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);
};
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.
*
public:
TryCatch* next_;
void* exception_;
+ void* message_;
bool is_verbose_;
};
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);
}
}
+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);
}
}
-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>();
+ }
}
// 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;
}
}
-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);
}
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 =
// 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());
: 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_; }
// 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);
};
-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);
};
block != NULL;
block = block->next_) {
VISIT(reinterpret_cast<Object*&>(block->exception_));
+ VISIT(reinterpret_cast<Object*&>(block->message_));
}
// Iterate over pointers on native execution stack.
}
+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);
}
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.
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;
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);
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);
+}