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/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#if V8_ENABLE_WEBASSEMBLY 13#include "src/debug/debug-wasm-objects.h" 14#endif // V8_ENABLE_WEBASSEMBLY 15 16namespace v8 { 17namespace internal { 18 19bool CallSiteInfo::IsPromiseAll() const { 20 if (!IsAsync()) return false; 21 JSFunction fun = JSFunction::cast(function()); 22 return fun == fun.native_context().promise_all(); 23} 24 25bool CallSiteInfo::IsPromiseAllSettled() const { 26 if (!IsAsync()) return false; 27 JSFunction fun = JSFunction::cast(function()); 28 return fun == fun.native_context().promise_all_settled(); 29} 30 31bool CallSiteInfo::IsPromiseAny() const { 32 if (!IsAsync()) return false; 33 JSFunction fun = JSFunction::cast(function()); 34 return fun == fun.native_context().promise_any(); 35} 36 37bool CallSiteInfo::IsNative() const { 38 if (auto script = GetScript()) { 39 return script->type() == Script::TYPE_NATIVE; 40 } 41 return false; 42} 43 44bool CallSiteInfo::IsEval() const { 45 if (auto script = GetScript()) { 46 return script->compilation_type() == Script::COMPILATION_TYPE_EVAL; 47 } 48 return false; 49} 50 51bool CallSiteInfo::IsUserJavaScript() const { 52#if V8_ENABLE_WEBASSEMBLY 53 if (IsWasm()) return false; 54#endif // V8_ENABLE_WEBASSEMBLY 55 return GetSharedFunctionInfo().IsUserJavaScript(); 56} 57 58bool CallSiteInfo::IsMethodCall() const { 59#if V8_ENABLE_WEBASSEMBLY 60 if (IsWasm()) return false; 61#endif // V8_ENABLE_WEBASSEMBLY 62 return !IsToplevel() && !IsConstructor(); 63} 64 65bool CallSiteInfo::IsToplevel() const { 66 return receiver_or_instance().IsJSGlobalProxy() || 67 receiver_or_instance().IsNullOrUndefined(); 68} 69 70// static 71int CallSiteInfo::GetLineNumber(Handle<CallSiteInfo> info) { 72 Isolate* isolate = info->GetIsolate(); 73#if V8_ENABLE_WEBASSEMBLY 74 if (info->IsWasm() && !info->IsAsmJsWasm()) { 75 return 1; 76 } 77#endif // V8_ENABLE_WEBASSEMBLY 78 Handle<Script> script; 79 if (GetScript(isolate, info).ToHandle(&script)) { 80 int position = GetSourcePosition(info); 81 int line_number = Script::GetLineNumber(script, position) + 1; 82 if (script->HasSourceURLComment()) { 83 line_number -= script->line_offset(); 84 } 85 return line_number; 86 } 87 return Message::kNoLineNumberInfo; 88} 89 90// static 91int CallSiteInfo::GetColumnNumber(Handle<CallSiteInfo> info) { 92 Isolate* isolate = info->GetIsolate(); 93 int position = GetSourcePosition(info); 94#if V8_ENABLE_WEBASSEMBLY 95 if (info->IsWasm() && !info->IsAsmJsWasm()) { 96 return position + 1; 97 } 98#endif // V8_ENABLE_WEBASSEMBLY 99 Handle<Script> script; 100 if (GetScript(isolate, info).ToHandle(&script)) { 101 int column_number = Script::GetColumnNumber(script, position) + 1; 102 if (script->HasSourceURLComment()) { 103 if (Script::GetLineNumber(script, position) == script->line_offset()) { 104 column_number -= script->column_offset(); 105 } 106 } 107 return column_number; 108 } 109 return Message::kNoColumnInfo; 110} 111 112// static 113int CallSiteInfo::GetEnclosingLineNumber(Handle<CallSiteInfo> info) { 114 Isolate* isolate = info->GetIsolate(); 115#if V8_ENABLE_WEBASSEMBLY 116 if (info->IsWasm() && !info->IsAsmJsWasm()) { 117 return 1; 118 } 119#endif // V8_ENABLE_WEBASSEMBLY 120 Handle<Script> script; 121 if (!GetScript(isolate, info).ToHandle(&script)) { 122 return Message::kNoLineNumberInfo; 123 } 124#if V8_ENABLE_WEBASSEMBLY 125 if (info->IsAsmJsWasm()) { 126 auto module = info->GetWasmInstance().module(); 127 auto func_index = info->GetWasmFunctionIndex(); 128 int position = wasm::GetSourcePosition(module, func_index, 0, 129 info->IsAsmJsAtNumberConversion()); 130 return Script::GetLineNumber(script, position) + 1; 131 } 132#endif // V8_ENABLE_WEBASSEMBLY 133 int position = info->GetSharedFunctionInfo().function_token_position(); 134 return Script::GetLineNumber(script, position) + 1; 135} 136 137// static 138int CallSiteInfo::GetEnclosingColumnNumber(Handle<CallSiteInfo> info) { 139 Isolate* isolate = info->GetIsolate(); 140#if V8_ENABLE_WEBASSEMBLY 141 if (info->IsWasm() && !info->IsAsmJsWasm()) { 142 auto module = info->GetWasmInstance().module(); 143 auto func_index = info->GetWasmFunctionIndex(); 144 return GetWasmFunctionOffset(module, func_index); 145 } 146#endif // V8_ENABLE_WEBASSEMBLY 147 Handle<Script> script; 148 if (!GetScript(isolate, info).ToHandle(&script)) { 149 return Message::kNoColumnInfo; 150 } 151#if V8_ENABLE_WEBASSEMBLY 152 if (info->IsAsmJsWasm()) { 153 auto module = info->GetWasmInstance().module(); 154 auto func_index = info->GetWasmFunctionIndex(); 155 int position = wasm::GetSourcePosition(module, func_index, 0, 156 info->IsAsmJsAtNumberConversion()); 157 return Script::GetColumnNumber(script, position) + 1; 158 } 159#endif // V8_ENABLE_WEBASSEMBLY 160 int position = info->GetSharedFunctionInfo().function_token_position(); 161 return Script::GetColumnNumber(script, position) + 1; 162} 163 164int CallSiteInfo::GetScriptId() const { 165 if (auto script = GetScript()) { 166 return script->id(); 167 } 168 return Message::kNoScriptIdInfo; 169} 170 171Object CallSiteInfo::GetScriptName() const { 172 if (auto script = GetScript()) { 173 return script->name(); 174 } 175 return ReadOnlyRoots(GetIsolate()).null_value(); 176} 177 178Object CallSiteInfo::GetScriptNameOrSourceURL() const { 179 if (auto script = GetScript()) { 180 return script->GetNameOrSourceURL(); 181 } 182 return ReadOnlyRoots(GetIsolate()).null_value(); 183} 184 185Object CallSiteInfo::GetScriptSource() const { 186 if (auto script = GetScript()) { 187 if (script->HasValidSource()) { 188 return script->source(); 189 } 190 } 191 return ReadOnlyRoots(GetIsolate()).null_value(); 192} 193 194Object CallSiteInfo::GetScriptSourceMappingURL() const { 195 if (auto script = GetScript()) { 196 return script->source_mapping_url(); 197 } 198 return ReadOnlyRoots(GetIsolate()).null_value(); 199} 200 201namespace { 202 203MaybeHandle<String> FormatEvalOrigin(Isolate* isolate, Handle<Script> script) { 204 Handle<Object> sourceURL(script->GetNameOrSourceURL(), isolate); 205 if (sourceURL->IsString()) return Handle<String>::cast(sourceURL); 206 207 IncrementalStringBuilder builder(isolate); 208 builder.AppendCStringLiteral("eval at "); 209 if (script->has_eval_from_shared()) { 210 Handle<SharedFunctionInfo> eval_shared(script->eval_from_shared(), isolate); 211 auto eval_name = SharedFunctionInfo::DebugName(eval_shared); 212 if (eval_name->length() != 0) { 213 builder.AppendString(eval_name); 214 } else { 215 builder.AppendCStringLiteral("<anonymous>"); 216 } 217 if (eval_shared->script().IsScript()) { 218 Handle<Script> eval_script(Script::cast(eval_shared->script()), isolate); 219 builder.AppendCStringLiteral(" ("); 220 if (eval_script->compilation_type() == Script::COMPILATION_TYPE_EVAL) { 221 // Eval script originated from another eval. 222 Handle<String> str; 223 ASSIGN_RETURN_ON_EXCEPTION( 224 isolate, str, FormatEvalOrigin(isolate, eval_script), String); 225 builder.AppendString(str); 226 } else { 227 // eval script originated from "real" source. 228 Handle<Object> eval_script_name(eval_script->name(), isolate); 229 if (eval_script_name->IsString()) { 230 builder.AppendString(Handle<String>::cast(eval_script_name)); 231 Script::PositionInfo info; 232 if (Script::GetPositionInfo(eval_script, 233 Script::GetEvalPosition(isolate, script), 234 &info, Script::NO_OFFSET)) { 235 builder.AppendCharacter(':'); 236 builder.AppendInt(info.line + 1); 237 builder.AppendCharacter(':'); 238 builder.AppendInt(info.column + 1); 239 } 240 } 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} // namespace 253 254// static 255Handle<PrimitiveHeapObject> CallSiteInfo::GetEvalOrigin( 256 Handle<CallSiteInfo> info) { 257 auto isolate = info->GetIsolate(); 258 Handle<Script> script; 259 if (!GetScript(isolate, info).ToHandle(&script) || 260 script->compilation_type() != Script::COMPILATION_TYPE_EVAL) { 261 return isolate->factory()->undefined_value(); 262 } 263 return FormatEvalOrigin(isolate, script).ToHandleChecked(); 264} 265 266// static 267Handle<PrimitiveHeapObject> CallSiteInfo::GetFunctionName( 268 Handle<CallSiteInfo> info) { 269 Isolate* isolate = info->GetIsolate(); 270#if V8_ENABLE_WEBASSEMBLY 271 if (info->IsWasm()) { 272 Handle<WasmModuleObject> module_object( 273 info->GetWasmInstance().module_object(), isolate); 274 uint32_t func_index = info->GetWasmFunctionIndex(); 275 Handle<String> name; 276 if (WasmModuleObject::GetFunctionNameOrNull(isolate, module_object, 277 func_index) 278 .ToHandle(&name)) { 279 return name; 280 } 281 return isolate->factory()->null_value(); 282 } 283#endif // V8_ENABLE_WEBASSEMBLY 284 Handle<JSFunction> function(JSFunction::cast(info->function()), isolate); 285 Handle<String> name = JSFunction::GetDebugName(function); 286 if (name->length() != 0) return name; 287 if (info->IsEval()) return isolate->factory()->eval_string(); 288 return isolate->factory()->null_value(); 289} 290 291// static 292Handle<String> CallSiteInfo::GetFunctionDebugName(Handle<CallSiteInfo> info) { 293 Isolate* isolate = info->GetIsolate(); 294#if V8_ENABLE_WEBASSEMBLY 295 if (info->IsWasm()) { 296 return GetWasmFunctionDebugName(isolate, 297 handle(info->GetWasmInstance(), isolate), 298 info->GetWasmFunctionIndex()); 299 } 300#endif // V8_ENABLE_WEBASSEMBLY 301 Handle<JSFunction> function(JSFunction::cast(info->function()), isolate); 302 Handle<String> name = JSFunction::GetDebugName(function); 303 if (name->length() == 0 && info->IsEval()) { 304 name = isolate->factory()->eval_string(); 305 } 306 return name; 307} 308 309namespace { 310 311PrimitiveHeapObject InferMethodNameFromFastObject(Isolate* isolate, 312 JSObject receiver, 313 JSFunction fun, 314 PrimitiveHeapObject name) { 315 ReadOnlyRoots roots(isolate); 316 Map map = receiver.map(); 317 DescriptorArray descriptors = map.instance_descriptors(isolate); 318 for (auto i : map.IterateOwnDescriptors()) { 319 PrimitiveHeapObject key = descriptors.GetKey(i); 320 if (key.IsSymbol()) continue; 321 auto details = descriptors.GetDetails(i); 322 if (details.IsDontEnum()) continue; 323 Object value; 324 if (details.location() == PropertyLocation::kField) { 325 auto field_index = FieldIndex::ForPropertyIndex( 326 map, details.field_index(), details.representation()); 327 if (field_index.is_double()) continue; 328 value = receiver.RawFastPropertyAt(isolate, field_index); 329 } else { 330 value = descriptors.GetStrongValue(i); 331 } 332 if (value != fun) { 333 if (!value.IsAccessorPair()) continue; 334 auto pair = AccessorPair::cast(value); 335 if (pair.getter() != fun && pair.setter() != fun) continue; 336 } 337 if (name != key) { 338 name = name.IsUndefined(isolate) ? key : roots.null_value(); 339 } 340 } 341 return name; 342} 343 344template <typename Dictionary> 345PrimitiveHeapObject InferMethodNameFromDictionary(Isolate* isolate, 346 Dictionary dictionary, 347 JSFunction fun, 348 PrimitiveHeapObject name) { 349 ReadOnlyRoots roots(isolate); 350 for (auto i : dictionary.IterateEntries()) { 351 Object key; 352 if (!dictionary.ToKey(roots, i, &key)) continue; 353 if (key.IsSymbol()) continue; 354 auto details = dictionary.DetailsAt(i); 355 if (details.IsDontEnum()) continue; 356 auto value = dictionary.ValueAt(i); 357 if (value != fun) { 358 if (!value.IsAccessorPair()) continue; 359 auto pair = AccessorPair::cast(value); 360 if (pair.getter() != fun && pair.setter() != fun) continue; 361 } 362 if (name != key) { 363 name = name.IsUndefined(isolate) ? PrimitiveHeapObject::cast(key) 364 : roots.null_value(); 365 } 366 } 367 return name; 368} 369 370PrimitiveHeapObject InferMethodName(Isolate* isolate, JSReceiver receiver, 371 JSFunction fun) { 372 DisallowGarbageCollection no_gc; 373 ReadOnlyRoots roots(isolate); 374 PrimitiveHeapObject name = roots.undefined_value(); 375 for (PrototypeIterator it(isolate, receiver, kStartAtReceiver); !it.IsAtEnd(); 376 it.Advance()) { 377 auto current = it.GetCurrent(); 378 if (!current.IsJSObject()) break; 379 auto object = JSObject::cast(current); 380 if (object.IsAccessCheckNeeded()) break; 381 if (object.HasFastProperties()) { 382 name = InferMethodNameFromFastObject(isolate, object, fun, name); 383 } else if (object.IsJSGlobalObject()) { 384 name = InferMethodNameFromDictionary( 385 isolate, JSGlobalObject::cast(object).global_dictionary(kAcquireLoad), 386 fun, name); 387 } else if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { 388 name = InferMethodNameFromDictionary( 389 isolate, object.property_dictionary_swiss(), fun, name); 390 } else { 391 name = InferMethodNameFromDictionary( 392 isolate, object.property_dictionary(), fun, name); 393 } 394 } 395 if (name.IsUndefined(isolate)) return roots.null_value(); 396 return name; 397} 398 399} // namespace 400 401// static 402Handle<Object> CallSiteInfo::GetMethodName(Handle<CallSiteInfo> info) { 403 Isolate* isolate = info->GetIsolate(); 404 Handle<Object> receiver_or_instance(info->receiver_or_instance(), isolate); 405#if V8_ENABLE_WEBASSEMBLY 406 if (info->IsWasm()) return isolate->factory()->null_value(); 407#endif // V8_ENABLE_WEBASSEMBLY 408 if (receiver_or_instance->IsNullOrUndefined(isolate)) { 409 return isolate->factory()->null_value(); 410 } 411 412 Handle<JSReceiver> receiver = 413 JSReceiver::ToObject(isolate, receiver_or_instance).ToHandleChecked(); 414 Handle<JSFunction> function = 415 handle(JSFunction::cast(info->function()), isolate); 416 Handle<String> name(function->shared().Name(), isolate); 417 name = String::Flatten(isolate, name); 418 419 // The static initializer function is not a method, so don't add a 420 // class name, just return the function name. 421 if (name->HasOneBytePrefix(base::CStrVector("<static_fields_initializer>"))) { 422 return name; 423 } 424 425 // ES2015 gives getters and setters name prefixes which must 426 // be stripped to find the property name. 427 if (name->HasOneBytePrefix(base::CStrVector("get ")) || 428 name->HasOneBytePrefix(base::CStrVector("set "))) { 429 name = isolate->factory()->NewProperSubString(name, 4, name->length()); 430 } else if (name->length() == 0) { 431 // The function doesn't have a meaningful "name" property, however 432 // the parser does store an inferred name "o.foo" for the common 433 // case of `o.foo = function() {...}`, so see if we can derive a 434 // property name to guess from that. 435 name = handle(function->shared().inferred_name(), isolate); 436 for (int index = name->length(); --index >= 0;) { 437 if (name->Get(index, isolate) == '.') { 438 name = isolate->factory()->NewProperSubString(name, index + 1, 439 name->length()); 440 break; 441 } 442 } 443 } 444 445 if (name->length() != 0) { 446 PropertyKey key(isolate, Handle<Name>::cast(name)); 447 LookupIterator it(isolate, receiver, key, 448 LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); 449 if (it.state() == LookupIterator::DATA) { 450 if (it.GetDataValue().is_identical_to(function)) { 451 return name; 452 } 453 } else if (it.state() == LookupIterator::ACCESSOR) { 454 Handle<Object> accessors = it.GetAccessors(); 455 if (accessors->IsAccessorPair()) { 456 Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors); 457 if (pair->getter() == *function || pair->setter() == *function) { 458 return name; 459 } 460 } 461 } 462 } 463 464 return handle(InferMethodName(isolate, *receiver, *function), isolate); 465} 466 467// static 468Handle<Object> CallSiteInfo::GetTypeName(Handle<CallSiteInfo> info) { 469 Isolate* isolate = info->GetIsolate(); 470 if (!info->IsMethodCall()) { 471 return isolate->factory()->null_value(); 472 } 473 Handle<JSReceiver> receiver = 474 JSReceiver::ToObject(isolate, 475 handle(info->receiver_or_instance(), isolate)) 476 .ToHandleChecked(); 477 if (receiver->IsJSProxy()) { 478 return isolate->factory()->Proxy_string(); 479 } 480 return JSReceiver::GetConstructorName(isolate, receiver); 481} 482 483#if V8_ENABLE_WEBASSEMBLY 484uint32_t CallSiteInfo::GetWasmFunctionIndex() const { 485 DCHECK(IsWasm()); 486 return Smi::ToInt(Smi::cast(function())); 487} 488 489WasmInstanceObject CallSiteInfo::GetWasmInstance() const { 490 DCHECK(IsWasm()); 491 return WasmInstanceObject::cast(receiver_or_instance()); 492} 493 494// static 495Handle<Object> CallSiteInfo::GetWasmModuleName(Handle<CallSiteInfo> info) { 496 Isolate* isolate = info->GetIsolate(); 497 if (info->IsWasm()) { 498 Handle<String> name; 499 auto module_object = 500 handle(info->GetWasmInstance().module_object(), isolate); 501 if (WasmModuleObject::GetModuleNameOrNull(isolate, module_object) 502 .ToHandle(&name)) { 503 return name; 504 } 505 } 506 return isolate->factory()->null_value(); 507} 508#endif // V8_ENABLE_WEBASSEMBLY 509 510// static 511int 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 DCHECK(!info->IsPromiseAny()); 518 int source_position = 519 ComputeSourcePosition(info, info->code_offset_or_source_position()); 520 info->set_code_offset_or_source_position(source_position); 521 info->set_flags(info->flags() | kIsSourcePositionComputed); 522 return source_position; 523} 524 525// static 526bool CallSiteInfo::ComputeLocation(Handle<CallSiteInfo> info, 527 MessageLocation* location) { 528 Isolate* isolate = info->GetIsolate(); 529#if V8_ENABLE_WEBASSEMBLY 530 if (info->IsWasm()) { 531 int pos = GetSourcePosition(info); 532 Handle<Script> script(info->GetWasmInstance().module_object().script(), 533 isolate); 534 *location = MessageLocation(script, pos, pos + 1); 535 return true; 536 } 537#endif // V8_ENABLE_WEBASSEMBLY 538 539 Handle<SharedFunctionInfo> shared(info->GetSharedFunctionInfo(), isolate); 540 if (!shared->IsSubjectToDebugging()) return false; 541 Handle<Script> script(Script::cast(shared->script()), isolate); 542 if (script->source().IsUndefined()) return false; 543 if (info->flags() & kIsSourcePositionComputed || 544 (shared->HasBytecodeArray() && 545 shared->GetBytecodeArray(isolate).HasSourcePositionTable())) { 546 int pos = GetSourcePosition(info); 547 *location = MessageLocation(script, pos, pos + 1, shared); 548 } else { 549 int code_offset = info->code_offset_or_source_position(); 550 *location = MessageLocation(script, shared, code_offset); 551 } 552 return true; 553} 554 555// static 556int CallSiteInfo::ComputeSourcePosition(Handle<CallSiteInfo> info, int offset) { 557 Isolate* isolate = info->GetIsolate(); 558#if V8_ENABLE_WEBASSEMBLY 559 if (info->IsWasm()) { 560 auto code_ref = Managed<wasm::GlobalWasmCodeRef>::cast(info->code_object()); 561 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 Handle<SharedFunctionInfo> shared(info->GetSharedFunctionInfo(), isolate); 569 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared); 570 return AbstractCode::cast(info->code_object()).SourcePosition(offset); 571} 572 573base::Optional<Script> CallSiteInfo::GetScript() const { 574#if V8_ENABLE_WEBASSEMBLY 575 if (IsWasm()) { 576 return GetWasmInstance().module_object().script(); 577 } 578#endif // V8_ENABLE_WEBASSEMBLY 579 Object script = GetSharedFunctionInfo().script(); 580 if (script.IsScript()) return Script::cast(script); 581 return base::nullopt; 582} 583 584SharedFunctionInfo CallSiteInfo::GetSharedFunctionInfo() const { 585#if V8_ENABLE_WEBASSEMBLY 586 DCHECK(!IsWasm()); 587#endif // V8_ENABLE_WEBASSEMBLY 588 return JSFunction::cast(function()).shared(); 589} 590 591// static 592MaybeHandle<Script> CallSiteInfo::GetScript(Isolate* isolate, 593 Handle<CallSiteInfo> info) { 594 if (auto script = info->GetScript()) { 595 return handle(*script, isolate); 596 } 597 return kNullMaybeHandle; 598} 599 600namespace { 601 602bool IsNonEmptyString(Handle<Object> object) { 603 return (object->IsString() && String::cast(*object).length() > 0); 604} 605 606void AppendFileLocation(Isolate* isolate, Handle<CallSiteInfo> frame, 607 IncrementalStringBuilder* builder) { 608 Handle<Object> script_name_or_source_url(frame->GetScriptNameOrSourceURL(), 609 isolate); 610 if (!script_name_or_source_url->IsString() && frame->IsEval()) { 611 builder->AppendString( 612 Handle<String>::cast(CallSiteInfo::GetEvalOrigin(frame))); 613 // Expecting source position to follow. 614 builder->AppendCStringLiteral(", "); 615 } 616 617 if (IsNonEmptyString(script_name_or_source_url)) { 618 builder->AppendString(Handle<String>::cast(script_name_or_source_url)); 619 } else { 620 // Source code does not originate from a file and is not native, but we 621 // can still get the source position inside the source string, e.g. in 622 // an eval string. 623 builder->AppendCStringLiteral("<anonymous>"); 624 } 625 626 int line_number = CallSiteInfo::GetLineNumber(frame); 627 if (line_number != Message::kNoLineNumberInfo) { 628 builder->AppendCharacter(':'); 629 builder->AppendInt(line_number); 630 631 int column_number = CallSiteInfo::GetColumnNumber(frame); 632 if (column_number != Message::kNoColumnInfo) { 633 builder->AppendCharacter(':'); 634 builder->AppendInt(column_number); 635 } 636 } 637} 638 639// Returns true iff 640// 1. the subject ends with '.' + pattern, or 641// 2. subject == pattern. 642bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject, 643 Handle<String> pattern) { 644 if (String::Equals(isolate, subject, pattern)) return true; 645 646 FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject)); 647 FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern)); 648 649 int pattern_index = pattern_reader.length() - 1; 650 int subject_index = subject_reader.length() - 1; 651 for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1. 652 if (subject_index < 0) { 653 return false; 654 } 655 656 const base::uc32 subject_char = subject_reader.Get(subject_index); 657 if (i == pattern_reader.length()) { 658 if (subject_char != '.') return false; 659 } else if (subject_char != pattern_reader.Get(pattern_index)) { 660 return false; 661 } 662 663 pattern_index--; 664 subject_index--; 665 } 666 667 return true; 668} 669 670void AppendMethodCall(Isolate* isolate, Handle<CallSiteInfo> frame, 671 IncrementalStringBuilder* builder) { 672 Handle<Object> type_name = CallSiteInfo::GetTypeName(frame); 673 Handle<Object> method_name = CallSiteInfo::GetMethodName(frame); 674 Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame); 675 676 Handle<Object> receiver(frame->receiver_or_instance(), isolate); 677 if (receiver->IsJSClassConstructor()) { 678 Handle<JSFunction> function = Handle<JSFunction>::cast(receiver); 679 Handle<String> class_name = JSFunction::GetDebugName(function); 680 if (class_name->length() != 0) { 681 type_name = class_name; 682 } 683 } 684 if (IsNonEmptyString(function_name)) { 685 Handle<String> function_string = Handle<String>::cast(function_name); 686 if (IsNonEmptyString(type_name)) { 687 Handle<String> type_string = Handle<String>::cast(type_name); 688 if (String::IsIdentifier(isolate, function_string) && 689 !String::Equals(isolate, function_string, type_string)) { 690 builder->AppendString(type_string); 691 builder->AppendCharacter('.'); 692 } 693 } 694 builder->AppendString(function_string); 695 696 if (IsNonEmptyString(method_name)) { 697 Handle<String> method_string = Handle<String>::cast(method_name); 698 if (!StringEndsWithMethodName(isolate, function_string, method_string)) { 699 builder->AppendCStringLiteral(" [as "); 700 builder->AppendString(method_string); 701 builder->AppendCharacter(']'); 702 } 703 } 704 } else { 705 if (IsNonEmptyString(type_name)) { 706 builder->AppendString(Handle<String>::cast(type_name)); 707 builder->AppendCharacter('.'); 708 } 709 if (IsNonEmptyString(method_name)) { 710 builder->AppendString(Handle<String>::cast(method_name)); 711 } else { 712 builder->AppendCStringLiteral("<anonymous>"); 713 } 714 } 715} 716 717void SerializeJSStackFrame(Isolate* isolate, Handle<CallSiteInfo> frame, 718 IncrementalStringBuilder* builder) { 719 Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame); 720 if (frame->IsAsync()) { 721 builder->AppendCStringLiteral("async "); 722 if (frame->IsPromiseAll() || frame->IsPromiseAny() || 723 frame->IsPromiseAllSettled()) { 724 builder->AppendCStringLiteral("Promise."); 725 builder->AppendString(Handle<String>::cast(function_name)); 726 builder->AppendCStringLiteral(" (index "); 727 builder->AppendInt(CallSiteInfo::GetSourcePosition(frame)); 728 builder->AppendCharacter(')'); 729 return; 730 } 731 } 732 if (frame->IsMethodCall()) { 733 AppendMethodCall(isolate, frame, builder); 734 } else if (frame->IsConstructor()) { 735 builder->AppendCStringLiteral("new "); 736 if (IsNonEmptyString(function_name)) { 737 builder->AppendString(Handle<String>::cast(function_name)); 738 } else { 739 builder->AppendCStringLiteral("<anonymous>"); 740 } 741 } else if (IsNonEmptyString(function_name)) { 742 builder->AppendString(Handle<String>::cast(function_name)); 743 } else { 744 AppendFileLocation(isolate, frame, builder); 745 return; 746 } 747 builder->AppendCStringLiteral(" ("); 748 AppendFileLocation(isolate, frame, builder); 749 builder->AppendCharacter(')'); 750} 751 752#if V8_ENABLE_WEBASSEMBLY 753void SerializeWasmStackFrame(Isolate* isolate, Handle<CallSiteInfo> frame, 754 IncrementalStringBuilder* builder) { 755 Handle<Object> module_name = CallSiteInfo::GetWasmModuleName(frame); 756 Handle<Object> function_name = CallSiteInfo::GetFunctionName(frame); 757 const bool has_name = !module_name->IsNull() || !function_name->IsNull(); 758 if (has_name) { 759 if (module_name->IsNull()) { 760 builder->AppendString(Handle<String>::cast(function_name)); 761 } else { 762 builder->AppendString(Handle<String>::cast(module_name)); 763 if (!function_name->IsNull()) { 764 builder->AppendCharacter('.'); 765 builder->AppendString(Handle<String>::cast(function_name)); 766 } 767 } 768 builder->AppendCStringLiteral(" ("); 769 } 770 771 Handle<Object> url(frame->GetScriptNameOrSourceURL(), isolate); 772 if (IsNonEmptyString(url)) { 773 builder->AppendString(Handle<String>::cast(url)); 774 } else { 775 builder->AppendCStringLiteral("<anonymous>"); 776 } 777 builder->AppendCharacter(':'); 778 779 const int wasm_func_index = frame->GetWasmFunctionIndex(); 780 builder->AppendCStringLiteral("wasm-function["); 781 builder->AppendInt(wasm_func_index); 782 builder->AppendCStringLiteral("]:"); 783 784 char buffer[16]; 785 SNPrintF(base::ArrayVector(buffer), "0x%x", 786 CallSiteInfo::GetColumnNumber(frame) - 1); 787 builder->AppendCString(buffer); 788 789 if (has_name) builder->AppendCharacter(')'); 790} 791#endif // V8_ENABLE_WEBASSEMBLY 792 793} // namespace 794 795void SerializeCallSiteInfo(Isolate* isolate, Handle<CallSiteInfo> frame, 796 IncrementalStringBuilder* builder) { 797#if V8_ENABLE_WEBASSEMBLY 798 if (frame->IsWasm() && !frame->IsAsmJsWasm()) { 799 SerializeWasmStackFrame(isolate, frame, builder); 800 return; 801 } 802#endif // V8_ENABLE_WEBASSEMBLY 803 SerializeJSStackFrame(isolate, frame, builder); 804} 805 806MaybeHandle<String> SerializeCallSiteInfo(Isolate* isolate, 807 Handle<CallSiteInfo> frame) { 808 IncrementalStringBuilder builder(isolate); 809 SerializeCallSiteInfo(isolate, frame, &builder); 810 return builder.Finish(); 811} 812 813} // namespace internal 814} // namespace v8 815