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