@@ -21,10 +21,11 @@ using v8::Local;
2121using v8::Maybe;
2222using v8::MaybeLocal;
2323using v8::Message;
24- using v8::NewStringType;
2524using v8::Number;
2625using v8::Object;
2726using v8::ScriptOrigin;
27+ using v8::StackFrame;
28+ using v8::StackTrace;
2829using v8::String;
2930using v8::Undefined;
3031using v8::Value;
@@ -44,30 +45,20 @@ namespace per_process {
4445static Mutex tty_mutex;
4546} // namespace per_process
4647
47- void AppendExceptionLine (Environment* env,
48- Local<Value> er,
49- Local<Message> message,
50- enum ErrorHandlingMode mode) {
51- if (message.IsEmpty ()) return ;
48+ static const int kMaxErrorSourceLength = 1024 ;
5249
53- HandleScope scope (env->isolate ());
54- Local<Object> err_obj;
55- if (!er.IsEmpty () && er->IsObject ()) {
56- err_obj = er.As <Object>();
57- }
50+ static std::string GetErrorSource (Isolate* isolate,
51+ Local<Context> context,
52+ Local<Message> message,
53+ bool * added_exception_line) {
54+ MaybeLocal<String> source_line_maybe = message->GetSourceLine (context);
55+ node::Utf8Value encoded_source (isolate, source_line_maybe.ToLocalChecked ());
56+ std::string sourceline (*encoded_source, encoded_source.length ());
5857
59- // Print (filename):(line number): (message).
60- ScriptOrigin origin = message->GetScriptOrigin ();
61- node::Utf8Value filename (env->isolate (), message->GetScriptResourceName ());
62- const char * filename_string = *filename;
63- int linenum = message->GetLineNumber (env->context ()).FromJust ();
64- // Print line of source code.
65- MaybeLocal<String> source_line_maybe = message->GetSourceLine (env->context ());
66- node::Utf8Value sourceline (env->isolate (),
67- source_line_maybe.ToLocalChecked ());
68- const char * sourceline_string = *sourceline;
69- if (strstr (sourceline_string, " node-do-not-add-exception-line" ) != nullptr )
70- return ;
58+ if (sourceline.find (" node-do-not-add-exception-line" ) != std::string::npos) {
59+ *added_exception_line = false ;
60+ return sourceline;
61+ }
7162
7263 // Because of how node modules work, all scripts are wrapped with a
7364 // "function (module, exports, __filename, ...) {"
@@ -90,53 +81,131 @@ void AppendExceptionLine(Environment* env,
9081 // sourceline to 78 characters, and we end up not providing very much
9182 // useful debugging info to the user if we remove 62 characters.
9283
84+ // Print (filename):(line number): (message).
85+ ScriptOrigin origin = message->GetScriptOrigin ();
86+ node::Utf8Value filename (isolate, message->GetScriptResourceName ());
87+ const char * filename_string = *filename;
88+ int linenum = message->GetLineNumber (context).FromJust ();
89+
9390 int script_start = (linenum - origin.ResourceLineOffset ()->Value ()) == 1
9491 ? origin.ResourceColumnOffset ()->Value ()
9592 : 0 ;
96- int start = message->GetStartColumn (env-> context () ).FromMaybe (0 );
97- int end = message->GetEndColumn (env-> context () ).FromMaybe (0 );
93+ int start = message->GetStartColumn (context).FromMaybe (0 );
94+ int end = message->GetEndColumn (context).FromMaybe (0 );
9895 if (start >= script_start) {
9996 CHECK_GE (end, start);
10097 start -= script_start;
10198 end -= script_start;
10299 }
103100
104- char arrow[1024 ];
105- int max_off = sizeof (arrow) - 2 ;
101+ int max_off = kMaxErrorSourceLength - 2 ;
106102
107- int off = snprintf (arrow,
108- sizeof (arrow),
103+ char buf[kMaxErrorSourceLength ];
104+ int off = snprintf (buf,
105+ kMaxErrorSourceLength ,
109106 " %s:%i\n %s\n " ,
110107 filename_string,
111108 linenum,
112- sourceline_string );
109+ sourceline. c_str () );
113110 CHECK_GE (off, 0 );
114111 if (off > max_off) {
115112 off = max_off;
116113 }
117114
118115 // Print wavy underline (GetUnderline is deprecated).
119116 for (int i = 0 ; i < start; i++) {
120- if (sourceline_string [i] == ' \0 ' || off >= max_off) {
117+ if (sourceline [i] == ' \0 ' || off >= max_off) {
121118 break ;
122119 }
123120 CHECK_LT (off, max_off);
124- arrow [off++] = (sourceline_string [i] == ' \t ' ) ? ' \t ' : ' ' ;
121+ buf [off++] = (sourceline [i] == ' \t ' ) ? ' \t ' : ' ' ;
125122 }
126123 for (int i = start; i < end; i++) {
127- if (sourceline_string [i] == ' \0 ' || off >= max_off) {
124+ if (sourceline [i] == ' \0 ' || off >= max_off) {
128125 break ;
129126 }
130127 CHECK_LT (off, max_off);
131- arrow [off++] = ' ^' ;
128+ buf [off++] = ' ^' ;
132129 }
133130 CHECK_LE (off, max_off);
134- arrow[off] = ' \n ' ;
135- arrow[off + 1 ] = ' \0 ' ;
131+ buf[off] = ' \n ' ;
132+ buf[off + 1 ] = ' \0 ' ;
133+
134+ *added_exception_line = true ;
135+ return std::string (buf);
136+ }
137+
138+ void PrintStackTrace (Isolate* isolate, Local<StackTrace> stack) {
139+ for (int i = 0 ; i < stack->GetFrameCount () - 1 ; i++) {
140+ Local<StackFrame> stack_frame = stack->GetFrame (isolate, i);
141+ node::Utf8Value fn_name_s (isolate, stack_frame->GetFunctionName ());
142+ node::Utf8Value script_name (isolate, stack_frame->GetScriptName ());
143+ const int line_number = stack_frame->GetLineNumber ();
144+ const int column = stack_frame->GetColumn ();
145+
146+ if (stack_frame->IsEval ()) {
147+ if (stack_frame->GetScriptId () == Message::kNoScriptIdInfo ) {
148+ fprintf (stderr, " at [eval]:%i:%i\n " , line_number, column);
149+ } else {
150+ fprintf (stderr,
151+ " at [eval] (%s:%i:%i)\n " ,
152+ *script_name,
153+ line_number,
154+ column);
155+ }
156+ break ;
157+ }
158+
159+ if (fn_name_s.length () == 0 ) {
160+ fprintf (stderr, " at %s:%i:%i\n " , *script_name, line_number, column);
161+ } else {
162+ fprintf (stderr,
163+ " at %s (%s:%i:%i)\n " ,
164+ *fn_name_s,
165+ *script_name,
166+ line_number,
167+ column);
168+ }
169+ }
170+ fflush (stderr);
171+ }
172+
173+ void PrintCaughtException (Isolate* isolate,
174+ Local<Context> context,
175+ const v8::TryCatch& try_catch) {
176+ CHECK (try_catch.HasCaught ());
177+ Local<Value> err = try_catch.Exception ();
178+ Local<Message> message = try_catch.Message ();
179+ Local<v8::StackTrace> stack = message->GetStackTrace ();
180+ node::Utf8Value reason (isolate,
181+ err->ToDetailString (context).ToLocalChecked ());
182+ bool added_exception_line = false ;
183+ std::string source =
184+ GetErrorSource (isolate, context, message, &added_exception_line);
185+ fprintf (stderr, " %s\n " , source.c_str ());
186+ fprintf (stderr, " %s\n " , *reason);
187+ PrintStackTrace (isolate, stack);
188+ }
189+
190+ void AppendExceptionLine (Environment* env,
191+ Local<Value> er,
192+ Local<Message> message,
193+ enum ErrorHandlingMode mode) {
194+ if (message.IsEmpty ()) return ;
195+
196+ HandleScope scope (env->isolate ());
197+ Local<Object> err_obj;
198+ if (!er.IsEmpty () && er->IsObject ()) {
199+ err_obj = er.As <Object>();
200+ }
136201
137- Local<String> arrow_str =
138- String::NewFromUtf8 (env->isolate (), arrow, NewStringType::kNormal )
139- .ToLocalChecked ();
202+ bool added_exception_line = false ;
203+ std::string source = GetErrorSource (
204+ env->isolate (), env->context (), message, &added_exception_line);
205+ if (!added_exception_line) {
206+ return ;
207+ }
208+ MaybeLocal<Value> arrow_str = ToV8Value (env->context (), source);
140209
141210 const bool can_set_arrow = !arrow_str.IsEmpty () && !err_obj.IsEmpty ();
142211 // If allocating arrow_str failed, print it out. There's not much else to do.
@@ -150,13 +219,14 @@ void AppendExceptionLine(Environment* env,
150219 env->set_printed_error (true );
151220
152221 uv_tty_reset_mode ();
153- PrintErrorString (" \n %s" , arrow );
222+ PrintErrorString (" \n %s" , source. c_str () );
154223 return ;
155224 }
156225
157226 CHECK (err_obj
158- ->SetPrivate (
159- env->context (), env->arrow_message_private_symbol (), arrow_str)
227+ ->SetPrivate (env->context (),
228+ env->arrow_message_private_symbol (),
229+ arrow_str.ToLocalChecked ())
160230 .FromMaybe (false ));
161231}
162232
0 commit comments