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