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