call-site-info.cc 27.5 KB
Newer Older
1 2 3 4
// 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.

5
#include "src/objects/call-site-info.h"
6

7
#include "src/base/strings.h"
8
#include "src/objects/call-site-info-inl.h"
9
#include "src/objects/shared-function-info.h"
10
#include "src/strings/string-builder-inl.h"
11

12 13 14 15
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects.h"
#endif  // V8_ENABLE_WEBASSEMBLY

16 17 18
namespace v8 {
namespace internal {

19
bool CallSiteInfo::IsPromiseAll() const {
20 21 22
  if (!IsAsync()) return false;
  JSFunction fun = JSFunction::cast(function());
  return fun == fun.native_context().promise_all();
23 24
}

25 26 27 28 29 30
bool CallSiteInfo::IsPromiseAllSettled() const {
  if (!IsAsync()) return false;
  JSFunction fun = JSFunction::cast(function());
  return fun == fun.native_context().promise_all_settled();
}

31
bool CallSiteInfo::IsPromiseAny() const {
32 33 34
  if (!IsAsync()) return false;
  JSFunction fun = JSFunction::cast(function());
  return fun == fun.native_context().promise_any();
35 36
}

37
bool CallSiteInfo::IsNative() const {
38 39 40 41
  if (auto script = GetScript()) {
    return script->type() == Script::TYPE_NATIVE;
  }
  return false;
42 43
}

44
bool CallSiteInfo::IsEval() const {
45 46 47 48
  if (auto script = GetScript()) {
    return script->compilation_type() == Script::COMPILATION_TYPE_EVAL;
  }
  return false;
49 50
}

51
bool CallSiteInfo::IsUserJavaScript() const {
52 53 54 55
#if V8_ENABLE_WEBASSEMBLY
  if (IsWasm()) return false;
#endif  // V8_ENABLE_WEBASSEMBLY
  return GetSharedFunctionInfo().IsUserJavaScript();
56 57
}

58
bool CallSiteInfo::IsMethodCall() const {
59 60 61 62
#if V8_ENABLE_WEBASSEMBLY
  if (IsWasm()) return false;
#endif  // V8_ENABLE_WEBASSEMBLY
  return !IsToplevel() && !IsConstructor();
63 64
}

65
bool CallSiteInfo::IsToplevel() const {
66 67
  return receiver_or_instance().IsJSGlobalProxy() ||
         receiver_or_instance().IsNullOrUndefined();
68 69
}

70
// static
71
int CallSiteInfo::GetLineNumber(Handle<CallSiteInfo> info) {
72
  Isolate* isolate = info->GetIsolate();
73
#if V8_ENABLE_WEBASSEMBLY
74 75 76
  if (info->IsWasm() && !info->IsAsmJsWasm()) {
    return 1;
  }
77
#endif  // V8_ENABLE_WEBASSEMBLY
78 79
  Handle<Script> script;
  if (GetScript(isolate, info).ToHandle(&script)) {
80
    int position = GetSourcePosition(info);
81 82 83 84 85
    int line_number = Script::GetLineNumber(script, position) + 1;
    if (script->HasSourceURLComment()) {
      line_number -= script->line_offset();
    }
    return line_number;
86 87
  }
  return Message::kNoLineNumberInfo;
88 89
}

90
// static
91
int CallSiteInfo::GetColumnNumber(Handle<CallSiteInfo> info) {
92
  Isolate* isolate = info->GetIsolate();
93
  int position = GetSourcePosition(info);
94
#if V8_ENABLE_WEBASSEMBLY
95 96 97
  if (info->IsWasm() && !info->IsAsmJsWasm()) {
    return position + 1;
  }
98
#endif  // V8_ENABLE_WEBASSEMBLY
99 100
  Handle<Script> script;
  if (GetScript(isolate, info).ToHandle(&script)) {
101 102 103 104 105 106 107
    int column_number = Script::GetColumnNumber(script, position) + 1;
    if (script->HasSourceURLComment()) {
      if (Script::GetLineNumber(script, position) == script->line_offset()) {
        column_number -= script->column_offset();
      }
    }
    return column_number;
108 109
  }
  return Message::kNoColumnInfo;
110 111
}

112
// static
113
int CallSiteInfo::GetEnclosingLineNumber(Handle<CallSiteInfo> info) {
114
  Isolate* isolate = info->GetIsolate();
115
#if V8_ENABLE_WEBASSEMBLY
116 117 118
  if (info->IsWasm() && !info->IsAsmJsWasm()) {
    return 1;
  }
119
#endif  // V8_ENABLE_WEBASSEMBLY
120
  Handle<Script> script;
121 122 123 124 125 126 127 128 129
  if (!GetScript(isolate, info).ToHandle(&script)) {
    return Message::kNoLineNumberInfo;
  }
#if V8_ENABLE_WEBASSEMBLY
  if (info->IsAsmJsWasm()) {
    auto module = info->GetWasmInstance().module();
    auto func_index = info->GetWasmFunctionIndex();
    int position = wasm::GetSourcePosition(module, func_index, 0,
                                           info->IsAsmJsAtNumberConversion());
130 131
    return Script::GetLineNumber(script, position) + 1;
  }
132 133 134
#endif  // V8_ENABLE_WEBASSEMBLY
  int position = info->GetSharedFunctionInfo().function_token_position();
  return Script::GetLineNumber(script, position) + 1;
135 136
}

137
// static
138
int CallSiteInfo::GetEnclosingColumnNumber(Handle<CallSiteInfo> info) {
139
  Isolate* isolate = info->GetIsolate();
140
#if V8_ENABLE_WEBASSEMBLY
141 142 143 144 145
  if (info->IsWasm() && !info->IsAsmJsWasm()) {
    auto module = info->GetWasmInstance().module();
    auto func_index = info->GetWasmFunctionIndex();
    return GetWasmFunctionOffset(module, func_index);
  }
146
#endif  // V8_ENABLE_WEBASSEMBLY
147
  Handle<Script> script;
148 149 150 151 152 153 154 155 156
  if (!GetScript(isolate, info).ToHandle(&script)) {
    return Message::kNoColumnInfo;
  }
#if V8_ENABLE_WEBASSEMBLY
  if (info->IsAsmJsWasm()) {
    auto module = info->GetWasmInstance().module();
    auto func_index = info->GetWasmFunctionIndex();
    int position = wasm::GetSourcePosition(module, func_index, 0,
                                           info->IsAsmJsAtNumberConversion());
157 158
    return Script::GetColumnNumber(script, position) + 1;
  }
159 160 161
#endif  // V8_ENABLE_WEBASSEMBLY
  int position = info->GetSharedFunctionInfo().function_token_position();
  return Script::GetColumnNumber(script, position) + 1;
162 163
}

164
int CallSiteInfo::GetScriptId() const {
165 166 167 168
  if (auto script = GetScript()) {
    return script->id();
  }
  return Message::kNoScriptIdInfo;
169 170
}

171
Object CallSiteInfo::GetScriptName() const {
172 173
  if (auto script = GetScript()) {
    return script->name();
174
  }
175
  return ReadOnlyRoots(GetIsolate()).null_value();
176 177
}

178
Object CallSiteInfo::GetScriptNameOrSourceURL() const {
179 180
  if (auto script = GetScript()) {
    return script->GetNameOrSourceURL();
181
  }
182
  return ReadOnlyRoots(GetIsolate()).null_value();
183 184
}

185
Object CallSiteInfo::GetScriptSource() const {
186 187 188 189 190 191 192 193
  if (auto script = GetScript()) {
    if (script->HasValidSource()) {
      return script->source();
    }
  }
  return ReadOnlyRoots(GetIsolate()).null_value();
}

194
Object CallSiteInfo::GetScriptSourceMappingURL() const {
195 196 197 198 199 200
  if (auto script = GetScript()) {
    return script->source_mapping_url();
  }
  return ReadOnlyRoots(GetIsolate()).null_value();
}

201
namespace {
202

203 204 205 206 207
MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) {
  Handle<Object> sourceURL(script->GetNameOrSourceURL(), isolate);
  if (sourceURL->IsString()) return Handle<String>::cast(sourceURL);

  IncrementalStringBuilder builder(isolate);
208
  builder.AppendCStringLiteral("eval at ");
209 210 211 212 213 214
  if (script->has_eval_from_shared()) {
    Handle<SharedFunctionInfo> eval_shared(script->eval_from_shared(), isolate);
    auto eval_name = SharedFunctionInfo::DebugName(eval_shared);
    if (eval_name->length() != 0) {
      builder.AppendString(eval_name);
    } else {
215
      builder.AppendCStringLiteral("<anonymous>");
216 217 218
    }
    if (eval_shared->script().IsScript()) {
      Handle<Script> eval_script(Script::cast(eval_shared->script()), isolate);
219
      builder.AppendCStringLiteral(" (");
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
      if (eval_script->compilation_type() == Script::COMPILATION_TYPE_EVAL) {
        // Eval script originated from another eval.
        Handle<String> str;
        ASSIGN_RETURN_ON_EXCEPTION(
            isolate, str, FormatEvalOrigin(isolate, eval_script), String);
        builder.AppendString(str);
      } else {
        // eval script originated from "real" source.
        Handle<Object> eval_script_name(eval_script->name(), isolate);
        if (eval_script_name->IsString()) {
          builder.AppendString(Handle<String>::cast(eval_script_name));
          Script::PositionInfo info;
          if (Script::GetPositionInfo(eval_script,
                                      Script::GetEvalPosition(isolate, script),
                                      &info, Script::NO_OFFSET)) {
235
            builder.AppendCharacter(':');
236
            builder.AppendInt(info.line + 1);
237
            builder.AppendCharacter(':');
238 239 240
            builder.AppendInt(info.column + 1);
          }
        } else {
241
          builder.AppendCStringLiteral("unknown source");
242 243
        }
      }
244
      builder.AppendCharacter(')');
245 246
    }
  } else {
247
    builder.AppendCStringLiteral("<anonymous>");
248 249
  }
  return builder.Finish().ToHandleChecked();
250 251
}

252 253
}  // namespace

254
// static
255 256
Handle<PrimitiveHeapObject> CallSiteInfo::GetEvalOrigin(
    Handle<CallSiteInfo> info) {
257 258 259 260 261 262 263
  auto isolate = info->GetIsolate();
  Handle<Script> script;
  if (!GetScript(isolate, info).ToHandle(&script) ||
      script->compilation_type() != Script::COMPILATION_TYPE_EVAL) {
    return isolate->factory()->undefined_value();
  }
  return FormatEvalOrigin(isolate, script).ToHandleChecked();
264 265
}

266
// static
267 268
Handle<PrimitiveHeapObject> CallSiteInfo::GetFunctionName(
    Handle<CallSiteInfo> info) {
269
  Isolate* isolate = info->GetIsolate();
270
#if V8_ENABLE_WEBASSEMBLY
271 272 273 274 275 276 277 278 279 280
  if (info->IsWasm()) {
    Handle<WasmModuleObject> module_object(
        info->GetWasmInstance().module_object(), isolate);
    uint32_t func_index = info->GetWasmFunctionIndex();
    Handle<String> name;
    if (WasmModuleObject::GetFunctionNameOrNull(isolate, module_object,
                                                func_index)
            .ToHandle(&name)) {
      return name;
    }
281
    return isolate->factory()->null_value();
282
  }
283 284 285 286 287
#endif  // V8_ENABLE_WEBASSEMBLY
  Handle<JSFunction> function(JSFunction::cast(info->function()), isolate);
  Handle<String> name = JSFunction::GetDebugName(function);
  if (name->length() != 0) return name;
  if (info->IsEval()) return isolate->factory()->eval_string();
288
  return isolate->factory()->null_value();
289 290
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
// static
Handle<String> CallSiteInfo::GetFunctionDebugName(Handle<CallSiteInfo> info) {
  Isolate* isolate = info->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
  if (info->IsWasm()) {
    return GetWasmFunctionDebugName(isolate,
                                    handle(info->GetWasmInstance(), isolate),
                                    info->GetWasmFunctionIndex());
  }
#endif  // V8_ENABLE_WEBASSEMBLY
  Handle<JSFunction> function(JSFunction::cast(info->function()), isolate);
  Handle<String> name = JSFunction::GetDebugName(function);
  if (name->length() == 0 && info->IsEval()) {
    name = isolate->factory()->eval_string();
  }
  return name;
}

309 310 311 312 313 314 315 316
namespace {

PrimitiveHeapObject InferMethodNameFromFastObject(Isolate* isolate,
                                                  JSObject receiver,
                                                  JSFunction fun,
                                                  PrimitiveHeapObject name) {
  ReadOnlyRoots roots(isolate);
  Map map = receiver.map();
317
  DescriptorArray descriptors = map.instance_descriptors(isolate);
318 319 320 321 322 323
  for (auto i : map.IterateOwnDescriptors()) {
    PrimitiveHeapObject key = descriptors.GetKey(i);
    if (key.IsSymbol()) continue;
    auto details = descriptors.GetDetails(i);
    if (details.IsDontEnum()) continue;
    Object value;
324
    if (details.location() == PropertyLocation::kField) {
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
      auto field_index = FieldIndex::ForPropertyIndex(
          map, details.field_index(), details.representation());
      if (field_index.is_double()) continue;
      value = receiver.RawFastPropertyAt(isolate, field_index);
    } else {
      value = descriptors.GetStrongValue(i);
    }
    if (value != fun) {
      if (!value.IsAccessorPair()) continue;
      auto pair = AccessorPair::cast(value);
      if (pair.getter() != fun && pair.setter() != fun) continue;
    }
    if (name != key) {
      name = name.IsUndefined(isolate) ? key : roots.null_value();
    }
  }
  return name;
}

template <typename Dictionary>
PrimitiveHeapObject InferMethodNameFromDictionary(Isolate* isolate,
                                                  Dictionary dictionary,
                                                  JSFunction fun,
                                                  PrimitiveHeapObject name) {
  ReadOnlyRoots roots(isolate);
  for (auto i : dictionary.IterateEntries()) {
    Object key;
    if (!dictionary.ToKey(roots, i, &key)) continue;
    if (key.IsSymbol()) continue;
    auto details = dictionary.DetailsAt(i);
    if (details.IsDontEnum()) continue;
    auto value = dictionary.ValueAt(i);
    if (value != fun) {
      if (!value.IsAccessorPair()) continue;
      auto pair = AccessorPair::cast(value);
      if (pair.getter() != fun && pair.setter() != fun) continue;
    }
    if (name != key) {
      name = name.IsUndefined(isolate) ? PrimitiveHeapObject::cast(key)
                                       : roots.null_value();
    }
  }
  return name;
}

PrimitiveHeapObject InferMethodName(Isolate* isolate, JSReceiver receiver,
                                    JSFunction fun) {
  DisallowGarbageCollection no_gc;
  ReadOnlyRoots roots(isolate);
  PrimitiveHeapObject name = roots.undefined_value();
  for (PrototypeIterator it(isolate, receiver, kStartAtReceiver); !it.IsAtEnd();
       it.Advance()) {
    auto current = it.GetCurrent();
    if (!current.IsJSObject()) break;
    auto object = JSObject::cast(current);
    if (object.IsAccessCheckNeeded()) break;
    if (object.HasFastProperties()) {
      name = InferMethodNameFromFastObject(isolate, object, fun, name);
    } else if (object.IsJSGlobalObject()) {
      name = InferMethodNameFromDictionary(
          isolate, JSGlobalObject::cast(object).global_dictionary(kAcquireLoad),
          fun, name);
387
    } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
388
      name = InferMethodNameFromDictionary(
389
          isolate, object.property_dictionary_swiss(), fun, name);
390 391 392 393 394 395 396
    } else {
      name = InferMethodNameFromDictionary(
          isolate, object.property_dictionary(), fun, name);
    }
  }
  if (name.IsUndefined(isolate)) return roots.null_value();
  return name;
397 398
}

399 400
}  // namespace

401
// static
402
Handle<Object> CallSiteInfo::GetMethodName(Handle<CallSiteInfo> info) {
403 404
  Isolate* isolate = info->GetIsolate();
  Handle<Object> receiver_or_instance(info->receiver_or_instance(), isolate);
405 406 407 408
#if V8_ENABLE_WEBASSEMBLY
  if (info->IsWasm()) return isolate->factory()->null_value();
#endif  // V8_ENABLE_WEBASSEMBLY
  if (receiver_or_instance->IsNullOrUndefined(isolate)) {
409 410 411 412 413 414 415 416 417 418 419 420
    return isolate->factory()->null_value();
  }

  Handle<JSReceiver> receiver =
      JSReceiver::ToObject(isolate, receiver_or_instance).ToHandleChecked();
  Handle<JSFunction> function =
      handle(JSFunction::cast(info->function()), isolate);
  Handle<String> name(function->shared().Name(), isolate);
  name = String::Flatten(isolate, name);

  // The static initializer function is not a method, so don't add a
  // class name, just return the function name.
421
  if (name->HasOneBytePrefix(base::CStrVector("<static_fields_initializer>"))) {
422 423 424 425 426
    return name;
  }

  // ES2015 gives getters and setters name prefixes which must
  // be stripped to find the property name.
427 428
  if (name->HasOneBytePrefix(base::CStrVector("get ")) ||
      name->HasOneBytePrefix(base::CStrVector("set "))) {
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    name = isolate->factory()->NewProperSubString(name, 4, name->length());
  } else if (name->length() == 0) {
    // The function doesn't have a meaningful "name" property, however
    // the parser does store an inferred name "o.foo" for the common
    // case of `o.foo = function() {...}`, so see if we can derive a
    // property name to guess from that.
    name = handle(function->shared().inferred_name(), isolate);
    for (int index = name->length(); --index >= 0;) {
      if (name->Get(index, isolate) == '.') {
        name = isolate->factory()->NewProperSubString(name, index + 1,
                                                      name->length());
        break;
      }
    }
  }

  if (name->length() != 0) {
446
    PropertyKey key(isolate, Handle<Name>::cast(name));
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    LookupIterator it(isolate, receiver, key,
                      LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
    if (it.state() == LookupIterator::DATA) {
      if (it.GetDataValue().is_identical_to(function)) {
        return name;
      }
    } else if (it.state() == LookupIterator::ACCESSOR) {
      Handle<Object> accessors = it.GetAccessors();
      if (accessors->IsAccessorPair()) {
        Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors);
        if (pair->getter() == *function || pair->setter() == *function) {
          return name;
        }
      }
    }
  }

  return handle(InferMethodName(isolate, *receiver, *function), isolate);
465 466
}

467
// static
468
Handle<Object> CallSiteInfo::GetTypeName(Handle<CallSiteInfo> info) {
469 470 471 472 473 474 475 476 477 478 479
  Isolate* isolate = info->GetIsolate();
  if (!info->IsMethodCall()) {
    return isolate->factory()->null_value();
  }
  Handle<JSReceiver> receiver =
      JSReceiver::ToObject(isolate,
                           handle(info->receiver_or_instance(), isolate))
          .ToHandleChecked();
  if (receiver->IsJSProxy()) {
    return isolate->factory()->Proxy_string();
  }
480
  return JSReceiver::GetConstructorName(isolate, receiver);
481 482
}

483
#if V8_ENABLE_WEBASSEMBLY
484
uint32_t CallSiteInfo::GetWasmFunctionIndex() const {
485 486
  DCHECK(IsWasm());
  return Smi::ToInt(Smi::cast(function()));
487 488
}

489
WasmInstanceObject CallSiteInfo::GetWasmInstance() const {
490 491
  DCHECK(IsWasm());
  return WasmInstanceObject::cast(receiver_or_instance());
492 493
}

494
// static
495
Handle<Object> CallSiteInfo::GetWasmModuleName(Handle<CallSiteInfo> info) {
496 497 498 499 500 501 502 503 504 505 506 507 508 509
  Isolate* isolate = info->GetIsolate();
  if (info->IsWasm()) {
    Handle<String> name;
    auto module_object =
        handle(info->GetWasmInstance().module_object(), isolate);
    if (WasmModuleObject::GetModuleNameOrNull(isolate, module_object)
            .ToHandle(&name)) {
      return name;
    }
  }
  return isolate->factory()->null_value();
}
#endif  // V8_ENABLE_WEBASSEMBLY

510
// static
511
int CallSiteInfo::GetSourcePosition(Handle<CallSiteInfo> info) {
512
  if (info->flags() & kIsSourcePositionComputed) {
513
    return info->code_offset_or_source_position();
514 515
  }
  DCHECK(!info->IsPromiseAll());
516
  DCHECK(!info->IsPromiseAllSettled());
517 518
  DCHECK(!info->IsPromiseAny());
  int source_position =
519 520
      ComputeSourcePosition(info, info->code_offset_or_source_position());
  info->set_code_offset_or_source_position(source_position);
521 522 523 524 525
  info->set_flags(info->flags() | kIsSourcePositionComputed);
  return source_position;
}

// static
526 527
bool CallSiteInfo::ComputeLocation(Handle<CallSiteInfo> info,
                                   MessageLocation* location) {
528
  Isolate* isolate = info->GetIsolate();
529
#if V8_ENABLE_WEBASSEMBLY
530 531 532 533 534 535 536
  if (info->IsWasm()) {
    int pos = GetSourcePosition(info);
    Handle<Script> script(info->GetWasmInstance().module_object().script(),
                          isolate);
    *location = MessageLocation(script, pos, pos + 1);
    return true;
  }
537
#endif  // V8_ENABLE_WEBASSEMBLY
538 539 540 541 542 543 544 545 546 547 548

  Handle<SharedFunctionInfo> shared(info->GetSharedFunctionInfo(), isolate);
  if (!shared->IsSubjectToDebugging()) return false;
  Handle<Script> script(Script::cast(shared->script()), isolate);
  if (script->source().IsUndefined()) return false;
  if (info->flags() & kIsSourcePositionComputed ||
      (shared->HasBytecodeArray() &&
       shared->GetBytecodeArray(isolate).HasSourcePositionTable())) {
    int pos = GetSourcePosition(info);
    *location = MessageLocation(script, pos, pos + 1, shared);
  } else {
549
    int code_offset = info->code_offset_or_source_position();
550 551 552 553 554 555
    *location = MessageLocation(script, shared, code_offset);
  }
  return true;
}

// static
556
int CallSiteInfo::ComputeSourcePosition(Handle<CallSiteInfo> info, int offset) {
557
  Isolate* isolate = info->GetIsolate();
558
#if V8_ENABLE_WEBASSEMBLY
559
  if (info->IsWasm()) {
560 561
    auto code_ref = Managed<wasm::GlobalWasmCodeRef>::cast(info->code_object());
    int byte_offset = code_ref.get()->code()->GetSourcePositionBefore(offset);
562
    auto module = info->GetWasmInstance().module();
563
    uint32_t func_index = info->GetWasmFunctionIndex();
564
    return wasm::GetSourcePosition(module, func_index, byte_offset,
565
                                   info->IsAsmJsAtNumberConversion());
566
  }
567
#endif  // V8_ENABLE_WEBASSEMBLY
568 569
  Handle<SharedFunctionInfo> shared(info->GetSharedFunctionInfo(), isolate);
  SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared);
570
  return AbstractCode::cast(info->code_object()).SourcePosition(offset);
571 572
}

573
base::Optional<Script> CallSiteInfo::GetScript() const {
574
#if V8_ENABLE_WEBASSEMBLY
575 576
  if (IsWasm()) {
    return GetWasmInstance().module_object().script();
577
  }
578
#endif  // V8_ENABLE_WEBASSEMBLY
579 580 581 582
  Object script = GetSharedFunctionInfo().script();
  if (script.IsScript()) return Script::cast(script);
  return base::nullopt;
}
583

584
SharedFunctionInfo CallSiteInfo::GetSharedFunctionInfo() const {
585
#if V8_ENABLE_WEBASSEMBLY
586
  DCHECK(!IsWasm());
587
#endif  // V8_ENABLE_WEBASSEMBLY
588 589 590 591
  return JSFunction::cast(function()).shared();
}

// static
592 593
MaybeHandle<Script> CallSiteInfo::GetScript(Isolate* isolate,
                                            Handle<CallSiteInfo> info) {
594 595 596 597
  if (auto script = info->GetScript()) {
    return handle(*script, isolate);
  }
  return kNullMaybeHandle;
598 599
}

600 601 602 603 604 605
namespace {

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

606
void AppendFileLocation(Isolate* isolate, Handle<CallSiteInfo> frame,
607
                        IncrementalStringBuilder* builder) {
608 609 610 611
  Handle<Object> script_name_or_source_url(frame->GetScriptNameOrSourceURL(),
                                           isolate);
  if (!script_name_or_source_url->IsString() && frame->IsEval()) {
    builder->AppendString(
612
        Handle<String>::cast(CallSiteInfo::GetEvalOrigin(frame)));
613 614
    // Expecting source position to follow.
    builder->AppendCStringLiteral(", ");
615 616
  }

617 618
  if (IsNonEmptyString(script_name_or_source_url)) {
    builder->AppendString(Handle<String>::cast(script_name_or_source_url));
619 620 621 622
  } 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.
623
    builder->AppendCStringLiteral("<anonymous>");
624 625
  }

626
  int line_number = CallSiteInfo::GetLineNumber(frame);
627 628 629 630
  if (line_number != Message::kNoLineNumberInfo) {
    builder->AppendCharacter(':');
    builder->AppendInt(line_number);

631
    int column_number = CallSiteInfo::GetColumnNumber(frame);
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
    if (column_number != Message::kNoColumnInfo) {
      builder->AppendCharacter(':');
      builder->AppendInt(column_number);
    }
  }
}

// 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;
    }

656
    const base::uc32 subject_char = subject_reader.Get(subject_index);
657 658 659 660 661 662 663 664 665 666 667 668 669
    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;
}

670
void AppendMethodCall(Isolate* isolate, Handle<CallSiteInfo> frame,
671
                      IncrementalStringBuilder* builder) {
672 673 674
  Handle<Object> type_name = CallSiteInfo::GetTypeName(frame);
  Handle<Object> method_name = CallSiteInfo::GetMethodName(frame);
  Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame);
675 676 677 678 679

  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);
680 681
      if (String::IsIdentifier(isolate, function_string) &&
          !String::Equals(isolate, function_string, type_string)) {
682 683 684 685 686 687 688 689 690
        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)) {
691
        builder->AppendCStringLiteral(" [as ");
692 693 694 695 696 697 698 699 700 701 702 703
        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 {
704
      builder->AppendCStringLiteral("<anonymous>");
705 706 707 708
    }
  }
}

709
void SerializeJSStackFrame(Isolate* isolate, Handle<CallSiteInfo> frame,
710
                           IncrementalStringBuilder* builder) {
711
  Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame);
712
  if (frame->IsAsync()) {
713
    builder->AppendCStringLiteral("async ");
714 715
    if (frame->IsPromiseAll() || frame->IsPromiseAny() ||
        frame->IsPromiseAllSettled()) {
716
      builder->AppendCStringLiteral("Promise.");
717
      builder->AppendString(Handle<String>::cast(function_name));
718
      builder->AppendCStringLiteral(" (index ");
719
      builder->AppendInt(CallSiteInfo::GetSourcePosition(frame));
720
      builder->AppendCharacter(')');
721 722
      return;
    }
723
  }
724
  if (frame->IsMethodCall()) {
725
    AppendMethodCall(isolate, frame, builder);
726
  } else if (frame->IsConstructor()) {
727
    builder->AppendCStringLiteral("new ");
728
    if (IsNonEmptyString(function_name)) {
729
      builder->AppendString(Handle<String>::cast(function_name));
730
    } else {
731
      builder->AppendCStringLiteral("<anonymous>");
732 733
    }
  } else if (IsNonEmptyString(function_name)) {
734
    builder->AppendString(Handle<String>::cast(function_name));
735
  } else {
736
    AppendFileLocation(isolate, frame, builder);
737 738
    return;
  }
739
  builder->AppendCStringLiteral(" (");
740
  AppendFileLocation(isolate, frame, builder);
741
  builder->AppendCharacter(')');
742 743
}

744
#if V8_ENABLE_WEBASSEMBLY
745
void SerializeWasmStackFrame(Isolate* isolate, Handle<CallSiteInfo> frame,
746
                             IncrementalStringBuilder* builder) {
747 748
  Handle<Object> module_name = CallSiteInfo::GetWasmModuleName(frame);
  Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame);
749 750 751
  const bool has_name = !module_name->IsNull() || !function_name->IsNull();
  if (has_name) {
    if (module_name->IsNull()) {
752
      builder->AppendString(Handle<String>::cast(function_name));
753
    } else {
754
      builder->AppendString(Handle<String>::cast(module_name));
755
      if (!function_name->IsNull()) {
756
        builder->AppendCharacter('.');
757
        builder->AppendString(Handle<String>::cast(function_name));
758 759
      }
    }
760
    builder->AppendCStringLiteral(" (");
761 762
  }

763
  Handle<Object> url(frame->GetScriptNameOrSourceURL(), isolate);
764
  if (IsNonEmptyString(url)) {
765 766
    builder->AppendString(Handle<String>::cast(url));
  } else {
767
    builder->AppendCStringLiteral("<anonymous>");
768
  }
769
  builder->AppendCharacter(':');
770

771
  const int wasm_func_index = frame->GetWasmFunctionIndex();
772
  builder->AppendCStringLiteral("wasm-function[");
773
  builder->AppendInt(wasm_func_index);
774
  builder->AppendCStringLiteral("]:");
775 776

  char buffer[16];
777
  SNPrintF(base::ArrayVector(buffer), "0x%x",
778
           CallSiteInfo::GetColumnNumber(frame) - 1);
779
  builder->AppendCString(buffer);
780

781
  if (has_name) builder->AppendCharacter(')');
782
}
783
#endif  // V8_ENABLE_WEBASSEMBLY
784 785 786

}  // namespace

787 788
void SerializeCallSiteInfo(Isolate* isolate, Handle<CallSiteInfo> frame,
                           IncrementalStringBuilder* builder) {
789 790
#if V8_ENABLE_WEBASSEMBLY
  if (frame->IsWasm() && !frame->IsAsmJsWasm()) {
791
    SerializeWasmStackFrame(isolate, frame, builder);
792
    return;
793
  }
794 795
#endif  // V8_ENABLE_WEBASSEMBLY
  SerializeJSStackFrame(isolate, frame, builder);
796 797
}

798 799
MaybeHandle<String> SerializeCallSiteInfo(Isolate* isolate,
                                          Handle<CallSiteInfo> frame) {
800
  IncrementalStringBuilder builder(isolate);
801
  SerializeCallSiteInfo(isolate, frame, &builder);
802 803 804
  return builder.Finish();
}

805 806
}  // namespace internal
}  // namespace v8