• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 namespace v8 {
17 namespace internal {
18 
IsPromiseAll() const19 bool CallSiteInfo::IsPromiseAll() const {
20   if (!IsAsync()) return false;
21   JSFunction fun = JSFunction::cast(function());
22   return fun == fun.native_context().promise_all();
23 }
24 
IsPromiseAllSettled() const25 bool 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 
IsPromiseAny() const31 bool CallSiteInfo::IsPromiseAny() const {
32   if (!IsAsync()) return false;
33   JSFunction fun = JSFunction::cast(function());
34   return fun == fun.native_context().promise_any();
35 }
36 
IsNative() const37 bool CallSiteInfo::IsNative() const {
38   if (auto script = GetScript()) {
39     return script->type() == Script::TYPE_NATIVE;
40   }
41   return false;
42 }
43 
IsEval() const44 bool CallSiteInfo::IsEval() const {
45   if (auto script = GetScript()) {
46     return script->compilation_type() == Script::COMPILATION_TYPE_EVAL;
47   }
48   return false;
49 }
50 
IsUserJavaScript() const51 bool CallSiteInfo::IsUserJavaScript() const {
52 #if V8_ENABLE_WEBASSEMBLY
53   if (IsWasm()) return false;
54 #endif  // V8_ENABLE_WEBASSEMBLY
55   return GetSharedFunctionInfo().IsUserJavaScript();
56 }
57 
IsMethodCall() const58 bool CallSiteInfo::IsMethodCall() const {
59 #if V8_ENABLE_WEBASSEMBLY
60   if (IsWasm()) return false;
61 #endif  // V8_ENABLE_WEBASSEMBLY
62   return !IsToplevel() && !IsConstructor();
63 }
64 
IsToplevel() const65 bool CallSiteInfo::IsToplevel() const {
66   return receiver_or_instance().IsJSGlobalProxy() ||
67          receiver_or_instance().IsNullOrUndefined();
68 }
69 
70 // static
GetLineNumber(Handle<CallSiteInfo> info)71 int 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
GetColumnNumber(Handle<CallSiteInfo> info)91 int 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
GetEnclosingLineNumber(Handle<CallSiteInfo> info)113 int 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
GetEnclosingColumnNumber(Handle<CallSiteInfo> info)138 int 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 
GetScriptId() const164 int CallSiteInfo::GetScriptId() const {
165   if (auto script = GetScript()) {
166     return script->id();
167   }
168   return Message::kNoScriptIdInfo;
169 }
170 
GetScriptName() const171 Object CallSiteInfo::GetScriptName() const {
172   if (auto script = GetScript()) {
173     return script->name();
174   }
175   return ReadOnlyRoots(GetIsolate()).null_value();
176 }
177 
GetScriptNameOrSourceURL() const178 Object CallSiteInfo::GetScriptNameOrSourceURL() const {
179   if (auto script = GetScript()) {
180     return script->GetNameOrSourceURL();
181   }
182   return ReadOnlyRoots(GetIsolate()).null_value();
183 }
184 
GetScriptSource() const185 Object 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 
GetScriptSourceMappingURL() const194 Object CallSiteInfo::GetScriptSourceMappingURL() const {
195   if (auto script = GetScript()) {
196     return script->source_mapping_url();
197   }
198   return ReadOnlyRoots(GetIsolate()).null_value();
199 }
200 
201 namespace {
202 
FormatEvalOrigin(Isolate * isolate,Handle<Script> script)203 MaybeHandle<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
GetEvalOrigin(Handle<CallSiteInfo> info)255 Handle<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
GetFunctionName(Handle<CallSiteInfo> info)267 Handle<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
GetFunctionDebugName(Handle<CallSiteInfo> info)292 Handle<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 
309 namespace {
310 
InferMethodNameFromFastObject(Isolate * isolate,JSObject receiver,JSFunction fun,PrimitiveHeapObject name)311 PrimitiveHeapObject 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 
344 template <typename Dictionary>
InferMethodNameFromDictionary(Isolate * isolate,Dictionary dictionary,JSFunction fun,PrimitiveHeapObject name)345 PrimitiveHeapObject 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 
InferMethodName(Isolate * isolate,JSReceiver receiver,JSFunction fun)370 PrimitiveHeapObject 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
GetMethodName(Handle<CallSiteInfo> info)402 Handle<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
GetTypeName(Handle<CallSiteInfo> info)468 Handle<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
GetWasmFunctionIndex() const484 uint32_t CallSiteInfo::GetWasmFunctionIndex() const {
485   DCHECK(IsWasm());
486   return Smi::ToInt(Smi::cast(function()));
487 }
488 
GetWasmInstance() const489 WasmInstanceObject CallSiteInfo::GetWasmInstance() const {
490   DCHECK(IsWasm());
491   return WasmInstanceObject::cast(receiver_or_instance());
492 }
493 
494 // static
GetWasmModuleName(Handle<CallSiteInfo> info)495 Handle<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
GetSourcePosition(Handle<CallSiteInfo> info)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   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
ComputeLocation(Handle<CallSiteInfo> info,MessageLocation * location)526 bool 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
ComputeSourcePosition(Handle<CallSiteInfo> info,int offset)556 int 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 
GetScript() const573 base::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 
GetSharedFunctionInfo() const584 SharedFunctionInfo 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
GetScript(Isolate * isolate,Handle<CallSiteInfo> info)592 MaybeHandle<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 
600 namespace {
601 
IsNonEmptyString(Handle<Object> object)602 bool IsNonEmptyString(Handle<Object> object) {
603   return (object->IsString() && String::cast(*object).length() > 0);
604 }
605 
AppendFileLocation(Isolate * isolate,Handle<CallSiteInfo> frame,IncrementalStringBuilder * builder)606 void 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.
StringEndsWithMethodName(Isolate * isolate,Handle<String> subject,Handle<String> pattern)642 bool 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 
AppendMethodCall(Isolate * isolate,Handle<CallSiteInfo> frame,IncrementalStringBuilder * builder)670 void 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 
SerializeJSStackFrame(Isolate * isolate,Handle<CallSiteInfo> frame,IncrementalStringBuilder * builder)717 void 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
SerializeWasmStackFrame(Isolate * isolate,Handle<CallSiteInfo> frame,IncrementalStringBuilder * builder)753 void 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 
SerializeCallSiteInfo(Isolate * isolate,Handle<CallSiteInfo> frame,IncrementalStringBuilder * builder)795 void 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 
SerializeCallSiteInfo(Isolate * isolate,Handle<CallSiteInfo> frame)806 MaybeHandle<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