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