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