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