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