stack-frame-info.cc 16.5 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/objects/stack-frame-info.h"

#include "src/objects/stack-frame-info-inl.h"
8
#include "src/strings/string-builder-inl.h"
9 10 11 12

namespace v8 {
namespace internal {

13
// static
14 15 16 17 18
int StackTraceFrame::GetLineNumber(Handle<StackTraceFrame> frame) {
  int line = GetFrameInfo(frame)->line_number();
  return line != StackFrameBase::kNone ? line : Message::kNoLineNumberInfo;
}

19 20 21 22 23 24 25 26 27
// static
int StackTraceFrame::GetOneBasedLineNumber(Handle<StackTraceFrame> frame) {
  // JavaScript line numbers are already 1-based. Wasm line numbers need
  // to be adjusted.
  int line = StackTraceFrame::GetLineNumber(frame);
  if (StackTraceFrame::IsWasm(frame) && line >= 0) line++;
  return line;
}

28
// static
29 30 31 32 33
int StackTraceFrame::GetColumnNumber(Handle<StackTraceFrame> frame) {
  int column = GetFrameInfo(frame)->column_number();
  return column != StackFrameBase::kNone ? column : Message::kNoColumnInfo;
}

34 35 36 37 38 39 40 41 42
// static
int StackTraceFrame::GetOneBasedColumnNumber(Handle<StackTraceFrame> frame) {
  // JavaScript colun numbers are already 1-based. Wasm column numbers need
  // to be adjusted.
  int column = StackTraceFrame::GetColumnNumber(frame);
  if (StackTraceFrame::IsWasm(frame) && column >= 0) column++;
  return column;
}

43
// static
44
int StackTraceFrame::GetScriptId(Handle<StackTraceFrame> frame) {
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
  Isolate* isolate = frame->GetIsolate();

  // Use FrameInfo if it's already there, but avoid initializing it for just
  // the script id, as it is much more expensive than just getting this
  // directly. See GetScriptNameOrSourceUrl() for more detail.
  int id;
  if (!frame->frame_info().IsUndefined()) {
    id = GetFrameInfo(frame)->script_id();
  } else {
    FrameArrayIterator it(
        isolate, handle(FrameArray::cast(frame->frame_array()), isolate),
        frame->frame_index());
    DCHECK(it.HasFrame());
    id = it.Frame()->GetScriptId();
  }
60 61 62
  return id != StackFrameBase::kNone ? id : Message::kNoScriptIdInfo;
}

63
// static
64 65
int StackTraceFrame::GetPromiseCombinatorIndex(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->promise_combinator_index();
66 67
}

68 69 70 71 72 73
// static
int StackTraceFrame::GetFunctionOffset(Handle<StackTraceFrame> frame) {
  DCHECK(IsWasm(frame));
  return GetFrameInfo(frame)->function_offset();
}

74 75 76 77 78
// static
int StackTraceFrame::GetWasmFunctionIndex(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->wasm_function_index();
}

79
// static
80 81 82 83 84
Handle<Object> StackTraceFrame::GetFileName(Handle<StackTraceFrame> frame) {
  auto name = GetFrameInfo(frame)->script_name();
  return handle(name, frame->GetIsolate());
}

85
// static
86 87
Handle<Object> StackTraceFrame::GetScriptNameOrSourceUrl(
    Handle<StackTraceFrame> frame) {
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
  Isolate* isolate = frame->GetIsolate();
  // TODO(caseq, szuend): the logic below is a workaround for crbug.com/1057211.
  // We should probably have a dedicated API for the scenario described in the
  // bug above and make getters of this class behave consistently.
  // See https://bit.ly/2wkbuIy for further discussion.
  // Use FrameInfo if it's already there, but avoid initializing it for just
  // the script name, as it is much more expensive than just getting this
  // directly.
  if (!frame->frame_info().IsUndefined()) {
    auto name = GetFrameInfo(frame)->script_name_or_source_url();
    return handle(name, isolate);
  }
  FrameArrayIterator it(isolate,
                        handle(FrameArray::cast(frame->frame_array()), isolate),
                        frame->frame_index());
  DCHECK(it.HasFrame());
  return it.Frame()->GetScriptNameOrSourceUrl();
105 106
}

107
// static
108 109 110 111 112
Handle<Object> StackTraceFrame::GetFunctionName(Handle<StackTraceFrame> frame) {
  auto name = GetFrameInfo(frame)->function_name();
  return handle(name, frame->GetIsolate());
}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
// static
Handle<Object> StackTraceFrame::GetMethodName(Handle<StackTraceFrame> frame) {
  auto name = GetFrameInfo(frame)->method_name();
  return handle(name, frame->GetIsolate());
}

// static
Handle<Object> StackTraceFrame::GetTypeName(Handle<StackTraceFrame> frame) {
  auto name = GetFrameInfo(frame)->type_name();
  return handle(name, frame->GetIsolate());
}

// static
Handle<Object> StackTraceFrame::GetEvalOrigin(Handle<StackTraceFrame> frame) {
  auto origin = GetFrameInfo(frame)->eval_origin();
  return handle(origin, frame->GetIsolate());
}

// static
132 133 134 135 136 137
Handle<Object> StackTraceFrame::GetWasmModuleName(
    Handle<StackTraceFrame> frame) {
  auto module = GetFrameInfo(frame)->wasm_module_name();
  return handle(module, frame->GetIsolate());
}

138 139 140 141 142 143 144
// static
Handle<WasmInstanceObject> StackTraceFrame::GetWasmInstance(
    Handle<StackTraceFrame> frame) {
  Object instance = GetFrameInfo(frame)->wasm_instance();
  return handle(WasmInstanceObject::cast(instance), frame->GetIsolate());
}

145
// static
146 147 148 149
bool StackTraceFrame::IsEval(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_eval();
}

150
// static
151 152 153 154
bool StackTraceFrame::IsConstructor(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_constructor();
}

155
// static
156 157 158 159
bool StackTraceFrame::IsWasm(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_wasm();
}

160 161 162 163 164 165
// static
bool StackTraceFrame::IsAsmJsWasm(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_asmjs_wasm();
}

// static
166 167 168 169
bool StackTraceFrame::IsUserJavaScript(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_user_java_script();
}

170
// static
171 172 173 174
bool StackTraceFrame::IsToplevel(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_toplevel();
}

175
// static
176 177 178 179
bool StackTraceFrame::IsAsync(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_async();
}

180
// static
181 182 183 184
bool StackTraceFrame::IsPromiseAll(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_promise_all();
}

185 186 187 188 189
// static
bool StackTraceFrame::IsPromiseAny(Handle<StackTraceFrame> frame) {
  return GetFrameInfo(frame)->is_promise_any();
}

190
// static
191 192
Handle<StackFrameInfo> StackTraceFrame::GetFrameInfo(
    Handle<StackTraceFrame> frame) {
193
  if (frame->frame_info().IsUndefined()) InitializeFrameInfo(frame);
194 195 196
  return handle(StackFrameInfo::cast(frame->frame_info()), frame->GetIsolate());
}

197
// static
198 199 200 201 202 203 204 205 206 207 208 209 210
void StackTraceFrame::InitializeFrameInfo(Handle<StackTraceFrame> frame) {
  Isolate* isolate = frame->GetIsolate();
  Handle<StackFrameInfo> frame_info = isolate->factory()->NewStackFrameInfo(
      handle(FrameArray::cast(frame->frame_array()), isolate),
      frame->frame_index());
  frame->set_frame_info(*frame_info);

  // After initializing, we no longer need to keep a reference
  // to the frame_array.
  frame->set_frame_array(ReadOnlyRoots(isolate).undefined_value());
  frame->set_frame_index(-1);
}

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
                                               Handle<FixedArray> stack_trace) {
  // For the empty case, a empty FrameArray needs to be allocated so the rest
  // of the code doesn't has to be special cased everywhere.
  if (stack_trace->length() == 0) {
    return isolate->factory()->NewFrameArray(0);
  }

  // Retrieve the FrameArray from the first StackTraceFrame.
  DCHECK_GT(stack_trace->length(), 0);
  Handle<StackTraceFrame> frame(StackTraceFrame::cast(stack_trace->get(0)),
                                isolate);
  return handle(FrameArray::cast(frame->frame_array()), isolate);
}

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
namespace {

bool IsNonEmptyString(Handle<Object> object) {
  return (object->IsString() && String::cast(*object).length() > 0);
}

void AppendFileLocation(Isolate* isolate, Handle<StackTraceFrame> frame,
                        IncrementalStringBuilder* builder) {
  Handle<Object> file_name = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
  if (!file_name->IsString() && StackTraceFrame::IsEval(frame)) {
    Handle<Object> eval_origin = StackTraceFrame::GetEvalOrigin(frame);
    DCHECK(eval_origin->IsString());
    builder->AppendString(Handle<String>::cast(eval_origin));
    builder->AppendCString(", ");  // Expecting source position to follow.
  }

  if (IsNonEmptyString(file_name)) {
    builder->AppendString(Handle<String>::cast(file_name));
  } else {
    // Source code does not originate from a file and is not native, but we
    // can still get the source position inside the source string, e.g. in
    // an eval string.
    builder->AppendCString("<anonymous>");
  }

  int line_number = StackTraceFrame::GetLineNumber(frame);
  if (line_number != Message::kNoLineNumberInfo) {
    builder->AppendCharacter(':');
    builder->AppendInt(line_number);

    int column_number = StackTraceFrame::GetColumnNumber(frame);
    if (column_number != Message::kNoColumnInfo) {
      builder->AppendCharacter(':');
      builder->AppendInt(column_number);
    }
  }
}

int StringIndexOf(Isolate* isolate, Handle<String> subject,
                  Handle<String> pattern) {
  if (pattern->length() > subject->length()) return -1;
  return String::IndexOf(isolate, subject, pattern, 0);
}

// Returns true iff
// 1. the subject ends with '.' + pattern, or
// 2. subject == pattern.
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
                              Handle<String> pattern) {
  if (String::Equals(isolate, subject, pattern)) return true;

  FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
  FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));

  int pattern_index = pattern_reader.length() - 1;
  int subject_index = subject_reader.length() - 1;
  for (int i = 0; i <= pattern_reader.length(); i++) {  // Iterate over len + 1.
    if (subject_index < 0) {
      return false;
    }

    const uc32 subject_char = subject_reader.Get(subject_index);
    if (i == pattern_reader.length()) {
      if (subject_char != '.') return false;
    } else if (subject_char != pattern_reader.Get(pattern_index)) {
      return false;
    }

    pattern_index--;
    subject_index--;
  }

  return true;
}

void AppendMethodCall(Isolate* isolate, Handle<StackTraceFrame> frame,
                      IncrementalStringBuilder* builder) {
  Handle<Object> type_name = StackTraceFrame::GetTypeName(frame);
  Handle<Object> method_name = StackTraceFrame::GetMethodName(frame);
  Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);

  if (IsNonEmptyString(function_name)) {
    Handle<String> function_string = Handle<String>::cast(function_name);
    if (IsNonEmptyString(type_name)) {
      Handle<String> type_string = Handle<String>::cast(type_name);
      bool starts_with_type_name =
          (StringIndexOf(isolate, function_string, type_string) == 0);
      if (!starts_with_type_name) {
        builder->AppendString(type_string);
        builder->AppendCharacter('.');
      }
    }
    builder->AppendString(function_string);

    if (IsNonEmptyString(method_name)) {
      Handle<String> method_string = Handle<String>::cast(method_name);
      if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
        builder->AppendCString(" [as ");
        builder->AppendString(method_string);
        builder->AppendCharacter(']');
      }
    }
  } else {
    if (IsNonEmptyString(type_name)) {
      builder->AppendString(Handle<String>::cast(type_name));
      builder->AppendCharacter('.');
    }
    if (IsNonEmptyString(method_name)) {
      builder->AppendString(Handle<String>::cast(method_name));
    } else {
      builder->AppendCString("<anonymous>");
    }
  }
}

341 342
void SerializeJSStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
                           IncrementalStringBuilder* builder) {
343 344 345 346 347
  Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);

  const bool is_toplevel = StackTraceFrame::IsToplevel(frame);
  const bool is_async = StackTraceFrame::IsAsync(frame);
  const bool is_promise_all = StackTraceFrame::IsPromiseAll(frame);
348
  const bool is_promise_any = StackTraceFrame::IsPromiseAny(frame);
349
  const bool is_constructor = StackTraceFrame::IsConstructor(frame);
350 351 352 353
  // Note: Keep the {is_method_call} predicate in sync with the corresponding
  //       predicate in factory.cc where the StackFrameInfo is created.
  //       Otherwise necessary fields for serialzing this frame might be
  //       missing.
354 355 356
  const bool is_method_call = !(is_toplevel || is_constructor);

  if (is_async) {
357
    builder->AppendCString("async ");
358 359
  }
  if (is_promise_all) {
360
    builder->AppendCString("Promise.all (index ");
361 362 363 364 365 366 367
    builder->AppendInt(StackTraceFrame::GetPromiseCombinatorIndex(frame));
    builder->AppendCString(")");
    return;
  }
  if (is_promise_any) {
    builder->AppendCString("Promise.any (index ");
    builder->AppendInt(StackTraceFrame::GetPromiseCombinatorIndex(frame));
368
    builder->AppendCString(")");
369 370 371
    return;
  }
  if (is_method_call) {
372
    AppendMethodCall(isolate, frame, builder);
373
  } else if (is_constructor) {
374
    builder->AppendCString("new ");
375
    if (IsNonEmptyString(function_name)) {
376
      builder->AppendString(Handle<String>::cast(function_name));
377
    } else {
378
      builder->AppendCString("<anonymous>");
379 380
    }
  } else if (IsNonEmptyString(function_name)) {
381
    builder->AppendString(Handle<String>::cast(function_name));
382
  } else {
383
    AppendFileLocation(isolate, frame, builder);
384 385 386
    return;
  }

387 388 389
  builder->AppendCString(" (");
  AppendFileLocation(isolate, frame, builder);
  builder->AppendCString(")");
390 391
}

392 393 394
void SerializeAsmJsWasmStackFrame(Isolate* isolate,
                                  Handle<StackTraceFrame> frame,
                                  IncrementalStringBuilder* builder) {
395 396 397 398 399 400
  // The string should look exactly as the respective javascript frame string.
  // Keep this method in line to
  // JSStackFrame::ToString(IncrementalStringBuilder&).
  Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);

  if (IsNonEmptyString(function_name)) {
401 402
    builder->AppendString(Handle<String>::cast(function_name));
    builder->AppendCString(" (");
403 404
  }

405
  AppendFileLocation(isolate, frame, builder);
406

407
  if (IsNonEmptyString(function_name)) builder->AppendCString(")");
408 409 410 411

  return;
}

412 413 414 415 416 417 418 419 420 421
bool IsAnonymousWasmScript(Isolate* isolate, Handle<StackTraceFrame> frame,
                           Handle<Object> url) {
  DCHECK(url->IsString());
  Handle<String> anonymous_prefix =
      isolate->factory()->InternalizeString(StaticCharVector("wasm://wasm/"));
  return (StackTraceFrame::IsWasm(frame) &&
          StringIndexOf(isolate, Handle<String>::cast(url), anonymous_prefix) >=
              0);
}

422 423
void SerializeWasmStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
                             IncrementalStringBuilder* builder) {
424 425 426 427 428
  Handle<Object> module_name = StackTraceFrame::GetWasmModuleName(frame);
  Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
  const bool has_name = !module_name->IsNull() || !function_name->IsNull();
  if (has_name) {
    if (module_name->IsNull()) {
429
      builder->AppendString(Handle<String>::cast(function_name));
430
    } else {
431
      builder->AppendString(Handle<String>::cast(module_name));
432
      if (!function_name->IsNull()) {
433 434
        builder->AppendCString(".");
        builder->AppendString(Handle<String>::cast(function_name));
435 436
      }
    }
437
    builder->AppendCString(" (");
438 439
  }

440 441 442 443 444 445 446
  Handle<Object> url = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
  if (IsNonEmptyString(url) && !IsAnonymousWasmScript(isolate, frame, url)) {
    builder->AppendString(Handle<String>::cast(url));
  } else {
    builder->AppendCString("<anonymous>");
  }
  builder->AppendCString(":");
447

448
  const int wasm_func_index = StackTraceFrame::GetWasmFunctionIndex(frame);
449 450 451
  builder->AppendCString("wasm-function[");
  builder->AppendInt(wasm_func_index);
  builder->AppendCString("]:");
452 453 454 455

  char buffer[16];
  SNPrintF(ArrayVector(buffer), "0x%x",
           StackTraceFrame::GetColumnNumber(frame));
456
  builder->AppendCString(buffer);
457

458
  if (has_name) builder->AppendCString(")");
459 460 461 462
}

}  // namespace

463 464
void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
                              IncrementalStringBuilder* builder) {
465 466 467 468 469 470 471 472 473 474 475 476 477
  // Ordering here is important, as AsmJs frames are also marked as Wasm.
  if (StackTraceFrame::IsAsmJsWasm(frame)) {
    SerializeAsmJsWasmStackFrame(isolate, frame, builder);
  } else if (StackTraceFrame::IsWasm(frame)) {
    SerializeWasmStackFrame(isolate, frame, builder);
  } else {
    SerializeJSStackFrame(isolate, frame, builder);
  }
}

MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
                                             Handle<StackTraceFrame> frame) {
  IncrementalStringBuilder builder(isolate);
478
  SerializeStackTraceFrame(isolate, frame, &builder);
479 480 481
  return builder.Finish();
}

482 483
}  // namespace internal
}  // namespace v8