• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/objects/stack-frame-info.h"
6 
7 #include "src/objects/stack-frame-info-inl.h"
8 #include "src/strings/string-builder-inl.h"
9 
10 namespace v8 {
11 namespace internal {
12 
13 // static
GetLineNumber(Handle<StackTraceFrame> frame)14 int StackTraceFrame::GetLineNumber(Handle<StackTraceFrame> frame) {
15   int line = GetFrameInfo(frame)->line_number();
16   return line != StackFrameBase::kNone ? line : Message::kNoLineNumberInfo;
17 }
18 
19 // static
GetOneBasedLineNumber(Handle<StackTraceFrame> frame)20 int StackTraceFrame::GetOneBasedLineNumber(Handle<StackTraceFrame> frame) {
21   // JavaScript line numbers are already 1-based. Wasm line numbers need
22   // to be adjusted.
23   int line = StackTraceFrame::GetLineNumber(frame);
24   if (StackTraceFrame::IsWasm(frame) && line >= 0) line++;
25   return line;
26 }
27 
28 // static
GetColumnNumber(Handle<StackTraceFrame> frame)29 int StackTraceFrame::GetColumnNumber(Handle<StackTraceFrame> frame) {
30   int column = GetFrameInfo(frame)->column_number();
31   return column != StackFrameBase::kNone ? column : Message::kNoColumnInfo;
32 }
33 
34 // static
GetOneBasedColumnNumber(Handle<StackTraceFrame> frame)35 int StackTraceFrame::GetOneBasedColumnNumber(Handle<StackTraceFrame> frame) {
36   // JavaScript colun numbers are already 1-based. Wasm column numbers need
37   // to be adjusted.
38   int column = StackTraceFrame::GetColumnNumber(frame);
39   if (StackTraceFrame::IsWasm(frame) && column >= 0) column++;
40   return column;
41 }
42 
43 // static
GetScriptId(Handle<StackTraceFrame> frame)44 int StackTraceFrame::GetScriptId(Handle<StackTraceFrame> frame) {
45   Isolate* isolate = frame->GetIsolate();
46 
47   // Use FrameInfo if it's already there, but avoid initializing it for just
48   // the script id, as it is much more expensive than just getting this
49   // directly. See GetScriptNameOrSourceUrl() for more detail.
50   int id;
51   if (!frame->frame_info().IsUndefined()) {
52     id = GetFrameInfo(frame)->script_id();
53   } else {
54     FrameArrayIterator it(
55         isolate, handle(FrameArray::cast(frame->frame_array()), isolate),
56         frame->frame_index());
57     DCHECK(it.HasFrame());
58     id = it.Frame()->GetScriptId();
59   }
60   return id != StackFrameBase::kNone ? id : Message::kNoScriptIdInfo;
61 }
62 
63 // static
GetPromiseCombinatorIndex(Handle<StackTraceFrame> frame)64 int StackTraceFrame::GetPromiseCombinatorIndex(Handle<StackTraceFrame> frame) {
65   return GetFrameInfo(frame)->promise_combinator_index();
66 }
67 
68 // static
GetFunctionOffset(Handle<StackTraceFrame> frame)69 int StackTraceFrame::GetFunctionOffset(Handle<StackTraceFrame> frame) {
70   DCHECK(IsWasm(frame));
71   return GetFrameInfo(frame)->function_offset();
72 }
73 
74 // static
GetWasmFunctionIndex(Handle<StackTraceFrame> frame)75 int StackTraceFrame::GetWasmFunctionIndex(Handle<StackTraceFrame> frame) {
76   return GetFrameInfo(frame)->wasm_function_index();
77 }
78 
79 // static
GetFileName(Handle<StackTraceFrame> frame)80 Handle<Object> StackTraceFrame::GetFileName(Handle<StackTraceFrame> frame) {
81   auto name = GetFrameInfo(frame)->script_name();
82   return handle(name, frame->GetIsolate());
83 }
84 
85 // static
GetScriptNameOrSourceUrl(Handle<StackTraceFrame> frame)86 Handle<Object> StackTraceFrame::GetScriptNameOrSourceUrl(
87     Handle<StackTraceFrame> frame) {
88   Isolate* isolate = frame->GetIsolate();
89   // TODO(caseq, szuend): the logic below is a workaround for crbug.com/1057211.
90   // We should probably have a dedicated API for the scenario described in the
91   // bug above and make getters of this class behave consistently.
92   // See https://bit.ly/2wkbuIy for further discussion.
93   // Use FrameInfo if it's already there, but avoid initializing it for just
94   // the script name, as it is much more expensive than just getting this
95   // directly.
96   if (!frame->frame_info().IsUndefined()) {
97     auto name = GetFrameInfo(frame)->script_name_or_source_url();
98     return handle(name, isolate);
99   }
100   FrameArrayIterator it(isolate,
101                         handle(FrameArray::cast(frame->frame_array()), isolate),
102                         frame->frame_index());
103   DCHECK(it.HasFrame());
104   return it.Frame()->GetScriptNameOrSourceUrl();
105 }
106 
107 // static
GetFunctionName(Handle<StackTraceFrame> frame)108 Handle<Object> StackTraceFrame::GetFunctionName(Handle<StackTraceFrame> frame) {
109   auto name = GetFrameInfo(frame)->function_name();
110   return handle(name, frame->GetIsolate());
111 }
112 
113 // static
GetMethodName(Handle<StackTraceFrame> frame)114 Handle<Object> StackTraceFrame::GetMethodName(Handle<StackTraceFrame> frame) {
115   auto name = GetFrameInfo(frame)->method_name();
116   return handle(name, frame->GetIsolate());
117 }
118 
119 // static
GetTypeName(Handle<StackTraceFrame> frame)120 Handle<Object> StackTraceFrame::GetTypeName(Handle<StackTraceFrame> frame) {
121   auto name = GetFrameInfo(frame)->type_name();
122   return handle(name, frame->GetIsolate());
123 }
124 
125 // static
GetEvalOrigin(Handle<StackTraceFrame> frame)126 Handle<Object> StackTraceFrame::GetEvalOrigin(Handle<StackTraceFrame> frame) {
127   auto origin = GetFrameInfo(frame)->eval_origin();
128   return handle(origin, frame->GetIsolate());
129 }
130 
131 // static
GetWasmModuleName(Handle<StackTraceFrame> frame)132 Handle<Object> StackTraceFrame::GetWasmModuleName(
133     Handle<StackTraceFrame> frame) {
134   auto module = GetFrameInfo(frame)->wasm_module_name();
135   return handle(module, frame->GetIsolate());
136 }
137 
138 // static
GetWasmInstance(Handle<StackTraceFrame> frame)139 Handle<WasmInstanceObject> StackTraceFrame::GetWasmInstance(
140     Handle<StackTraceFrame> frame) {
141   Object instance = GetFrameInfo(frame)->wasm_instance();
142   return handle(WasmInstanceObject::cast(instance), frame->GetIsolate());
143 }
144 
145 // static
IsEval(Handle<StackTraceFrame> frame)146 bool StackTraceFrame::IsEval(Handle<StackTraceFrame> frame) {
147   return GetFrameInfo(frame)->is_eval();
148 }
149 
150 // static
IsConstructor(Handle<StackTraceFrame> frame)151 bool StackTraceFrame::IsConstructor(Handle<StackTraceFrame> frame) {
152   return GetFrameInfo(frame)->is_constructor();
153 }
154 
155 // static
IsWasm(Handle<StackTraceFrame> frame)156 bool StackTraceFrame::IsWasm(Handle<StackTraceFrame> frame) {
157   return GetFrameInfo(frame)->is_wasm();
158 }
159 
160 // static
IsAsmJsWasm(Handle<StackTraceFrame> frame)161 bool StackTraceFrame::IsAsmJsWasm(Handle<StackTraceFrame> frame) {
162   return GetFrameInfo(frame)->is_asmjs_wasm();
163 }
164 
165 // static
IsUserJavaScript(Handle<StackTraceFrame> frame)166 bool StackTraceFrame::IsUserJavaScript(Handle<StackTraceFrame> frame) {
167   return GetFrameInfo(frame)->is_user_java_script();
168 }
169 
170 // static
IsToplevel(Handle<StackTraceFrame> frame)171 bool StackTraceFrame::IsToplevel(Handle<StackTraceFrame> frame) {
172   return GetFrameInfo(frame)->is_toplevel();
173 }
174 
175 // static
IsAsync(Handle<StackTraceFrame> frame)176 bool StackTraceFrame::IsAsync(Handle<StackTraceFrame> frame) {
177   return GetFrameInfo(frame)->is_async();
178 }
179 
180 // static
IsPromiseAll(Handle<StackTraceFrame> frame)181 bool StackTraceFrame::IsPromiseAll(Handle<StackTraceFrame> frame) {
182   return GetFrameInfo(frame)->is_promise_all();
183 }
184 
185 // static
IsPromiseAny(Handle<StackTraceFrame> frame)186 bool StackTraceFrame::IsPromiseAny(Handle<StackTraceFrame> frame) {
187   return GetFrameInfo(frame)->is_promise_any();
188 }
189 
190 // static
GetFrameInfo(Handle<StackTraceFrame> frame)191 Handle<StackFrameInfo> StackTraceFrame::GetFrameInfo(
192     Handle<StackTraceFrame> frame) {
193   if (frame->frame_info().IsUndefined()) InitializeFrameInfo(frame);
194   return handle(StackFrameInfo::cast(frame->frame_info()), frame->GetIsolate());
195 }
196 
197 // static
InitializeFrameInfo(Handle<StackTraceFrame> frame)198 void StackTraceFrame::InitializeFrameInfo(Handle<StackTraceFrame> frame) {
199   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"),
200                "SymbolizeStackFrame", "frameIndex", frame->frame_index());
201 
202   Isolate* isolate = frame->GetIsolate();
203   Handle<StackFrameInfo> frame_info = isolate->factory()->NewStackFrameInfo(
204       handle(FrameArray::cast(frame->frame_array()), isolate),
205       frame->frame_index());
206   frame->set_frame_info(*frame_info);
207 
208   // After initializing, we no longer need to keep a reference
209   // to the frame_array.
210   frame->set_frame_array(ReadOnlyRoots(isolate).undefined_value());
211   frame->set_frame_index(-1);
212 }
213 
GetFrameArrayFromStackTrace(Isolate * isolate,Handle<FixedArray> stack_trace)214 Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
215                                                Handle<FixedArray> stack_trace) {
216   // For the empty case, a empty FrameArray needs to be allocated so the rest
217   // of the code doesn't has to be special cased everywhere.
218   if (stack_trace->length() == 0) {
219     return isolate->factory()->NewFrameArray(0);
220   }
221 
222   // Retrieve the FrameArray from the first StackTraceFrame.
223   DCHECK_GT(stack_trace->length(), 0);
224   Handle<StackTraceFrame> frame(StackTraceFrame::cast(stack_trace->get(0)),
225                                 isolate);
226   return handle(FrameArray::cast(frame->frame_array()), isolate);
227 }
228 
229 namespace {
230 
IsNonEmptyString(Handle<Object> object)231 bool IsNonEmptyString(Handle<Object> object) {
232   return (object->IsString() && String::cast(*object).length() > 0);
233 }
234 
AppendFileLocation(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)235 void AppendFileLocation(Isolate* isolate, Handle<StackTraceFrame> frame,
236                         IncrementalStringBuilder* builder) {
237   Handle<Object> file_name = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
238   if (!file_name->IsString() && StackTraceFrame::IsEval(frame)) {
239     Handle<Object> eval_origin = StackTraceFrame::GetEvalOrigin(frame);
240     DCHECK(eval_origin->IsString());
241     builder->AppendString(Handle<String>::cast(eval_origin));
242     builder->AppendCString(", ");  // Expecting source position to follow.
243   }
244 
245   if (IsNonEmptyString(file_name)) {
246     builder->AppendString(Handle<String>::cast(file_name));
247   } else {
248     // Source code does not originate from a file and is not native, but we
249     // can still get the source position inside the source string, e.g. in
250     // an eval string.
251     builder->AppendCString("<anonymous>");
252   }
253 
254   int line_number = StackTraceFrame::GetLineNumber(frame);
255   if (line_number != Message::kNoLineNumberInfo) {
256     builder->AppendCharacter(':');
257     builder->AppendInt(line_number);
258 
259     int column_number = StackTraceFrame::GetColumnNumber(frame);
260     if (column_number != Message::kNoColumnInfo) {
261       builder->AppendCharacter(':');
262       builder->AppendInt(column_number);
263     }
264   }
265 }
266 
StringIndexOf(Isolate * isolate,Handle<String> subject,Handle<String> pattern)267 int StringIndexOf(Isolate* isolate, Handle<String> subject,
268                   Handle<String> pattern) {
269   if (pattern->length() > subject->length()) return -1;
270   return String::IndexOf(isolate, subject, pattern, 0);
271 }
272 
273 // Returns true iff
274 // 1. the subject ends with '.' + pattern, or
275 // 2. subject == pattern.
StringEndsWithMethodName(Isolate * isolate,Handle<String> subject,Handle<String> pattern)276 bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
277                               Handle<String> pattern) {
278   if (String::Equals(isolate, subject, pattern)) return true;
279 
280   FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
281   FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));
282 
283   int pattern_index = pattern_reader.length() - 1;
284   int subject_index = subject_reader.length() - 1;
285   for (int i = 0; i <= pattern_reader.length(); i++) {  // Iterate over len + 1.
286     if (subject_index < 0) {
287       return false;
288     }
289 
290     const uc32 subject_char = subject_reader.Get(subject_index);
291     if (i == pattern_reader.length()) {
292       if (subject_char != '.') return false;
293     } else if (subject_char != pattern_reader.Get(pattern_index)) {
294       return false;
295     }
296 
297     pattern_index--;
298     subject_index--;
299   }
300 
301   return true;
302 }
303 
AppendMethodCall(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)304 void AppendMethodCall(Isolate* isolate, Handle<StackTraceFrame> frame,
305                       IncrementalStringBuilder* builder) {
306   Handle<Object> type_name = StackTraceFrame::GetTypeName(frame);
307   Handle<Object> method_name = StackTraceFrame::GetMethodName(frame);
308   Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
309 
310   if (IsNonEmptyString(function_name)) {
311     Handle<String> function_string = Handle<String>::cast(function_name);
312     if (IsNonEmptyString(type_name)) {
313       Handle<String> type_string = Handle<String>::cast(type_name);
314       bool starts_with_type_name =
315           (StringIndexOf(isolate, function_string, type_string) == 0);
316       if (!starts_with_type_name) {
317         builder->AppendString(type_string);
318         builder->AppendCharacter('.');
319       }
320     }
321     builder->AppendString(function_string);
322 
323     if (IsNonEmptyString(method_name)) {
324       Handle<String> method_string = Handle<String>::cast(method_name);
325       if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
326         builder->AppendCString(" [as ");
327         builder->AppendString(method_string);
328         builder->AppendCharacter(']');
329       }
330     }
331   } else {
332     if (IsNonEmptyString(type_name)) {
333       builder->AppendString(Handle<String>::cast(type_name));
334       builder->AppendCharacter('.');
335     }
336     if (IsNonEmptyString(method_name)) {
337       builder->AppendString(Handle<String>::cast(method_name));
338     } else {
339       builder->AppendCString("<anonymous>");
340     }
341   }
342 }
343 
SerializeJSStackFrame(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)344 void SerializeJSStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
345                            IncrementalStringBuilder* builder) {
346   Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
347 
348   const bool is_toplevel = StackTraceFrame::IsToplevel(frame);
349   const bool is_async = StackTraceFrame::IsAsync(frame);
350   const bool is_promise_all = StackTraceFrame::IsPromiseAll(frame);
351   const bool is_promise_any = StackTraceFrame::IsPromiseAny(frame);
352   const bool is_constructor = StackTraceFrame::IsConstructor(frame);
353   // Note: Keep the {is_method_call} predicate in sync with the corresponding
354   //       predicate in factory.cc where the StackFrameInfo is created.
355   //       Otherwise necessary fields for serialzing this frame might be
356   //       missing.
357   const bool is_method_call = !(is_toplevel || is_constructor);
358 
359   if (is_async) {
360     builder->AppendCString("async ");
361   }
362   if (is_promise_all) {
363     builder->AppendCString("Promise.all (index ");
364     builder->AppendInt(StackTraceFrame::GetPromiseCombinatorIndex(frame));
365     builder->AppendCString(")");
366     return;
367   }
368   if (is_promise_any) {
369     builder->AppendCString("Promise.any (index ");
370     builder->AppendInt(StackTraceFrame::GetPromiseCombinatorIndex(frame));
371     builder->AppendCString(")");
372     return;
373   }
374   if (is_method_call) {
375     AppendMethodCall(isolate, frame, builder);
376   } else if (is_constructor) {
377     builder->AppendCString("new ");
378     if (IsNonEmptyString(function_name)) {
379       builder->AppendString(Handle<String>::cast(function_name));
380     } else {
381       builder->AppendCString("<anonymous>");
382     }
383   } else if (IsNonEmptyString(function_name)) {
384     builder->AppendString(Handle<String>::cast(function_name));
385   } else {
386     AppendFileLocation(isolate, frame, builder);
387     return;
388   }
389 
390   builder->AppendCString(" (");
391   AppendFileLocation(isolate, frame, builder);
392   builder->AppendCString(")");
393 }
394 
SerializeAsmJsWasmStackFrame(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)395 void SerializeAsmJsWasmStackFrame(Isolate* isolate,
396                                   Handle<StackTraceFrame> frame,
397                                   IncrementalStringBuilder* builder) {
398   // The string should look exactly as the respective javascript frame string.
399   // Keep this method in line to
400   // JSStackFrame::ToString(IncrementalStringBuilder&).
401   Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
402 
403   if (IsNonEmptyString(function_name)) {
404     builder->AppendString(Handle<String>::cast(function_name));
405     builder->AppendCString(" (");
406   }
407 
408   AppendFileLocation(isolate, frame, builder);
409 
410   if (IsNonEmptyString(function_name)) builder->AppendCString(")");
411 
412   return;
413 }
414 
IsAnonymousWasmScript(Isolate * isolate,Handle<StackTraceFrame> frame,Handle<Object> url)415 bool IsAnonymousWasmScript(Isolate* isolate, Handle<StackTraceFrame> frame,
416                            Handle<Object> url) {
417   DCHECK(url->IsString());
418   Handle<String> anonymous_prefix =
419       isolate->factory()->InternalizeString(StaticCharVector("wasm://wasm/"));
420   return (StackTraceFrame::IsWasm(frame) &&
421           StringIndexOf(isolate, Handle<String>::cast(url), anonymous_prefix) >=
422               0);
423 }
424 
SerializeWasmStackFrame(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)425 void SerializeWasmStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
426                              IncrementalStringBuilder* builder) {
427   Handle<Object> module_name = StackTraceFrame::GetWasmModuleName(frame);
428   Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
429   const bool has_name = !module_name->IsNull() || !function_name->IsNull();
430   if (has_name) {
431     if (module_name->IsNull()) {
432       builder->AppendString(Handle<String>::cast(function_name));
433     } else {
434       builder->AppendString(Handle<String>::cast(module_name));
435       if (!function_name->IsNull()) {
436         builder->AppendCString(".");
437         builder->AppendString(Handle<String>::cast(function_name));
438       }
439     }
440     builder->AppendCString(" (");
441   }
442 
443   Handle<Object> url = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
444   if (IsNonEmptyString(url) && !IsAnonymousWasmScript(isolate, frame, url)) {
445     builder->AppendString(Handle<String>::cast(url));
446   } else {
447     builder->AppendCString("<anonymous>");
448   }
449   builder->AppendCString(":");
450 
451   const int wasm_func_index = StackTraceFrame::GetWasmFunctionIndex(frame);
452   builder->AppendCString("wasm-function[");
453   builder->AppendInt(wasm_func_index);
454   builder->AppendCString("]:");
455 
456   char buffer[16];
457   SNPrintF(ArrayVector(buffer), "0x%x",
458            StackTraceFrame::GetColumnNumber(frame));
459   builder->AppendCString(buffer);
460 
461   if (has_name) builder->AppendCString(")");
462 }
463 
464 }  // namespace
465 
SerializeStackTraceFrame(Isolate * isolate,Handle<StackTraceFrame> frame,IncrementalStringBuilder * builder)466 void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
467                               IncrementalStringBuilder* builder) {
468   // Ordering here is important, as AsmJs frames are also marked as Wasm.
469   if (StackTraceFrame::IsAsmJsWasm(frame)) {
470     SerializeAsmJsWasmStackFrame(isolate, frame, builder);
471   } else if (StackTraceFrame::IsWasm(frame)) {
472     SerializeWasmStackFrame(isolate, frame, builder);
473   } else {
474     SerializeJSStackFrame(isolate, frame, builder);
475   }
476 }
477 
SerializeStackTraceFrame(Isolate * isolate,Handle<StackTraceFrame> frame)478 MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
479                                              Handle<StackTraceFrame> frame) {
480   IncrementalStringBuilder builder(isolate);
481   SerializeStackTraceFrame(isolate, frame, &builder);
482   return builder.Finish();
483 }
484 
485 }  // namespace internal
486 }  // namespace v8
487