[stack-trace] Separate stack-trace symbolization and serialization
This CL moves the code responsible for serializing a stack trace frame into a string, out of messages.cc and into stack-frame-info.cc. Instead of symbolizing the stack trace frame while serializing, the code is changed to work on top of StackTraceFrame and StackFrameInfo objects. The result is that the serialization code no longer cares when a stack trace frame is symbolized. Symbolization could happen eagerly during capturing, or lazily the first time any of StackFrameInfo fields are accessed. Drive-by: Existing users of StackFrameBase::ToString are adapted to the new SerializeStackTraceFrame API. This includes Isolate::PrintCurrentStackTrace, which is changed to re-use the existing capturing and serializing mechanism. Bug: v8:8742 Change-Id: Ic7fd80668c9d993e99d586ef7fe022850104c34f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1631414 Commit-Queue: Simon Zünd <szuend@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#62522}
This commit is contained in:
parent
dc5d7eddaf
commit
db24e2000a
@ -2945,11 +2945,11 @@ Local<StackTrace> StackTrace::CurrentStackTrace(Isolate* isolate,
|
||||
// --- S t a c k F r a m e ---
|
||||
|
||||
int StackFrame::GetLineNumber() const {
|
||||
return i::StackTraceFrame::GetLineNumber(Utils::OpenHandle(this));
|
||||
return i::StackTraceFrame::GetOneBasedLineNumber(Utils::OpenHandle(this));
|
||||
}
|
||||
|
||||
int StackFrame::GetColumn() const {
|
||||
return i::StackTraceFrame::GetColumnNumber(Utils::OpenHandle(this));
|
||||
return i::StackTraceFrame::GetOneBasedColumnNumber(Utils::OpenHandle(this));
|
||||
}
|
||||
|
||||
int StackFrame::GetScriptId() const {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/objects/frame-array-inl.h"
|
||||
#include "src/objects/objects-inl.h"
|
||||
#include "src/objects/stack-frame-info.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -203,9 +204,9 @@ BUILTIN(CallSitePrototypeIsToplevel) {
|
||||
BUILTIN(CallSitePrototypeToString) {
|
||||
HandleScope scope(isolate);
|
||||
CHECK_CALLSITE(recv, "toString");
|
||||
FrameArrayIterator it(isolate, GetFrameArray(isolate, recv),
|
||||
GetFrameIndex(isolate, recv));
|
||||
RETURN_RESULT_OR_FAILURE(isolate, it.Frame()->ToString());
|
||||
Handle<StackTraceFrame> frame = isolate->factory()->NewStackTraceFrame(
|
||||
GetFrameArray(isolate, recv), GetFrameIndex(isolate, recv));
|
||||
RETURN_RESULT_OR_FAILURE(isolate, SerializeStackTraceFrame(isolate, frame));
|
||||
}
|
||||
|
||||
#undef CHECK_CALLSITE
|
||||
|
@ -2033,33 +2033,23 @@ Object Isolate::PromoteScheduledException() {
|
||||
}
|
||||
|
||||
void Isolate::PrintCurrentStackTrace(FILE* out) {
|
||||
CaptureStackTraceOptions options;
|
||||
options.limit = 0;
|
||||
options.skip_mode = SKIP_NONE;
|
||||
options.capture_builtin_exit_frames = true;
|
||||
options.async_stack_trace = FLAG_async_stack_traces;
|
||||
options.filter_mode = FrameArrayBuilder::CURRENT_SECURITY_CONTEXT;
|
||||
options.capture_only_frames_subject_to_debugging = false;
|
||||
options.enable_frame_caching = false;
|
||||
|
||||
Handle<FixedArray> frames = Handle<FixedArray>::cast(
|
||||
CaptureStackTrace(this, this->factory()->undefined_value(), options));
|
||||
|
||||
IncrementalStringBuilder builder(this);
|
||||
for (StackTraceFrameIterator it(this); !it.done(); it.Advance()) {
|
||||
if (!it.is_javascript()) continue;
|
||||
for (int i = 0; i < frames->length(); ++i) {
|
||||
Handle<StackTraceFrame> frame(StackTraceFrame::cast(frames->get(i)), this);
|
||||
|
||||
HandleScope scope(this);
|
||||
JavaScriptFrame* frame = it.javascript_frame();
|
||||
|
||||
Handle<Object> receiver(frame->receiver(), this);
|
||||
Handle<JSFunction> function(frame->function(), this);
|
||||
Handle<AbstractCode> code;
|
||||
int offset;
|
||||
if (frame->is_interpreted()) {
|
||||
InterpretedFrame* interpreted_frame = InterpretedFrame::cast(frame);
|
||||
code = handle(AbstractCode::cast(interpreted_frame->GetBytecodeArray()),
|
||||
this);
|
||||
offset = interpreted_frame->GetBytecodeOffset();
|
||||
} else {
|
||||
code = handle(AbstractCode::cast(frame->LookupCode()), this);
|
||||
offset = static_cast<int>(frame->pc() - code->InstructionStart());
|
||||
}
|
||||
|
||||
// To preserve backwards compatiblity, only append a newline when
|
||||
// the current stringified frame actually has characters.
|
||||
const int old_length = builder.Length();
|
||||
JSStackFrame site(this, receiver, function, code, offset);
|
||||
site.ToString(builder);
|
||||
if (old_length != builder.Length()) builder.AppendCharacter('\n');
|
||||
SerializeStackTraceFrame(this, frame, builder);
|
||||
}
|
||||
|
||||
Handle<String> stack_trace = builder.Finish().ToHandleChecked();
|
||||
|
@ -322,12 +322,6 @@ bool StackFrameBase::IsEval() {
|
||||
GetScript()->compilation_type() == Script::COMPILATION_TYPE_EVAL;
|
||||
}
|
||||
|
||||
MaybeHandle<String> StackFrameBase::ToString() {
|
||||
IncrementalStringBuilder builder(isolate_);
|
||||
ToString(builder);
|
||||
return builder.Finish();
|
||||
}
|
||||
|
||||
void JSStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
|
||||
int frame_ix) {
|
||||
DCHECK(!array->IsWasmFrame(frame_ix));
|
||||
@ -366,7 +360,7 @@ Handle<Object> JSStackFrame::GetFileName() {
|
||||
}
|
||||
|
||||
Handle<Object> JSStackFrame::GetFunctionName() {
|
||||
Handle<String> result = JSFunction::GetName(function_);
|
||||
Handle<String> result = JSFunction::GetDebugName(function_);
|
||||
if (result->length() != 0) return result;
|
||||
|
||||
if (HasScript() &&
|
||||
@ -515,171 +509,6 @@ bool JSStackFrame::IsToplevel() {
|
||||
return receiver_->IsJSGlobalProxy() || receiver_->IsNullOrUndefined(isolate_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsNonEmptyString(Handle<Object> object) {
|
||||
return (object->IsString() && String::cast(*object).length() > 0);
|
||||
}
|
||||
|
||||
void AppendFileLocation(Isolate* isolate, StackFrameBase* call_site,
|
||||
IncrementalStringBuilder* builder) {
|
||||
if (call_site->IsNative()) {
|
||||
builder->AppendCString("native");
|
||||
return;
|
||||
}
|
||||
|
||||
Handle<Object> file_name = call_site->GetScriptNameOrSourceUrl();
|
||||
if (!file_name->IsString() && call_site->IsEval()) {
|
||||
Handle<Object> eval_origin = call_site->GetEvalOrigin();
|
||||
DCHECK(eval_origin->IsString());
|
||||
builder->AppendString(Handle<String>::cast(eval_origin));
|
||||
builder->AppendCString(", "); // Expecting source position to follow.
|
||||
}
|
||||
|
||||
if (IsNonEmptyString(file_name)) {
|
||||
builder->AppendString(Handle<String>::cast(file_name));
|
||||
} else {
|
||||
// Source code does not originate from a file and is not native, but we
|
||||
// can still get the source position inside the source string, e.g. in
|
||||
// an eval string.
|
||||
builder->AppendCString("<anonymous>");
|
||||
}
|
||||
|
||||
int line_number = call_site->GetLineNumber();
|
||||
if (line_number != StackFrameBase::kNone) {
|
||||
builder->AppendCharacter(':');
|
||||
builder->AppendInt(line_number);
|
||||
|
||||
int column_number = call_site->GetColumnNumber();
|
||||
if (column_number != StackFrameBase::kNone) {
|
||||
builder->AppendCharacter(':');
|
||||
builder->AppendInt(column_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int StringIndexOf(Isolate* isolate, Handle<String> subject,
|
||||
Handle<String> pattern) {
|
||||
if (pattern->length() > subject->length()) return -1;
|
||||
return String::IndexOf(isolate, subject, pattern, 0);
|
||||
}
|
||||
|
||||
// Returns true iff
|
||||
// 1. the subject ends with '.' + pattern, or
|
||||
// 2. subject == pattern.
|
||||
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
|
||||
Handle<String> pattern) {
|
||||
if (String::Equals(isolate, subject, pattern)) return true;
|
||||
|
||||
FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
|
||||
FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));
|
||||
|
||||
int pattern_index = pattern_reader.length() - 1;
|
||||
int subject_index = subject_reader.length() - 1;
|
||||
for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1.
|
||||
if (subject_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uc32 subject_char = subject_reader.Get(subject_index);
|
||||
if (i == pattern_reader.length()) {
|
||||
if (subject_char != '.') return false;
|
||||
} else if (subject_char != pattern_reader.Get(pattern_index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pattern_index--;
|
||||
subject_index--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppendMethodCall(Isolate* isolate, JSStackFrame* call_site,
|
||||
IncrementalStringBuilder* builder) {
|
||||
Handle<Object> type_name = call_site->GetTypeName();
|
||||
Handle<Object> method_name = call_site->GetMethodName();
|
||||
Handle<Object> function_name = call_site->GetFunctionName();
|
||||
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
Handle<String> function_string = Handle<String>::cast(function_name);
|
||||
if (IsNonEmptyString(type_name)) {
|
||||
Handle<String> type_string = Handle<String>::cast(type_name);
|
||||
bool starts_with_type_name =
|
||||
(StringIndexOf(isolate, function_string, type_string) == 0);
|
||||
if (!starts_with_type_name) {
|
||||
builder->AppendString(type_string);
|
||||
builder->AppendCharacter('.');
|
||||
}
|
||||
}
|
||||
builder->AppendString(function_string);
|
||||
|
||||
if (IsNonEmptyString(method_name)) {
|
||||
Handle<String> method_string = Handle<String>::cast(method_name);
|
||||
if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
|
||||
builder->AppendCString(" [as ");
|
||||
builder->AppendString(method_string);
|
||||
builder->AppendCharacter(']');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IsNonEmptyString(type_name)) {
|
||||
builder->AppendString(Handle<String>::cast(type_name));
|
||||
builder->AppendCharacter('.');
|
||||
}
|
||||
if (IsNonEmptyString(method_name)) {
|
||||
builder->AppendString(Handle<String>::cast(method_name));
|
||||
} else {
|
||||
builder->AppendCString("<anonymous>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void JSStackFrame::ToString(IncrementalStringBuilder& builder) {
|
||||
Handle<Object> function_name = GetFunctionName();
|
||||
|
||||
const bool is_toplevel = IsToplevel();
|
||||
const bool is_async = IsAsync();
|
||||
const bool is_promise_all = IsPromiseAll();
|
||||
const bool is_constructor = IsConstructor();
|
||||
const bool is_method_call = !(is_toplevel || is_constructor);
|
||||
|
||||
if (is_async) {
|
||||
builder.AppendCString("async ");
|
||||
}
|
||||
if (is_promise_all) {
|
||||
// For `Promise.all(iterable)` frames we interpret the {offset_}
|
||||
// as the element index into `iterable` where the error occurred.
|
||||
builder.AppendCString("Promise.all (index ");
|
||||
builder.AppendInt(offset_);
|
||||
builder.AppendCString(")");
|
||||
return;
|
||||
}
|
||||
if (is_method_call) {
|
||||
AppendMethodCall(isolate_, this, &builder);
|
||||
} else if (is_constructor) {
|
||||
builder.AppendCString("new ");
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
} else {
|
||||
builder.AppendCString("<anonymous>");
|
||||
}
|
||||
} else if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
} else {
|
||||
AppendFileLocation(isolate_, this, &builder);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendCString(" (");
|
||||
AppendFileLocation(isolate_, this, &builder);
|
||||
builder.AppendCString(")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int JSStackFrame::GetPosition() const {
|
||||
Handle<SharedFunctionInfo> shared = handle(function_->shared(), isolate_);
|
||||
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
|
||||
@ -744,41 +573,6 @@ Handle<Object> WasmStackFrame::GetWasmModuleName() {
|
||||
return module_name;
|
||||
}
|
||||
|
||||
void WasmStackFrame::ToString(IncrementalStringBuilder& builder) {
|
||||
Handle<WasmModuleObject> module_object(wasm_instance_->module_object(),
|
||||
isolate_);
|
||||
MaybeHandle<String> module_name =
|
||||
WasmModuleObject::GetModuleNameOrNull(isolate_, module_object);
|
||||
MaybeHandle<String> function_name = WasmModuleObject::GetFunctionNameOrNull(
|
||||
isolate_, module_object, wasm_func_index_);
|
||||
bool has_name = !module_name.is_null() || !function_name.is_null();
|
||||
if (has_name) {
|
||||
if (module_name.is_null()) {
|
||||
builder.AppendString(function_name.ToHandleChecked());
|
||||
} else {
|
||||
builder.AppendString(module_name.ToHandleChecked());
|
||||
if (!function_name.is_null()) {
|
||||
builder.AppendCString(".");
|
||||
builder.AppendString(function_name.ToHandleChecked());
|
||||
}
|
||||
}
|
||||
builder.AppendCString(" (");
|
||||
}
|
||||
|
||||
builder.AppendCString("wasm-function[");
|
||||
DCHECK(wasm_func_index_ <= kMaxInt);
|
||||
builder.AppendInt(static_cast<int>(wasm_func_index_));
|
||||
builder.AppendCString("]:");
|
||||
|
||||
char buffer[16];
|
||||
SNPrintF(ArrayVector(buffer), "0x%x", GetModuleOffset());
|
||||
builder.AppendCString(buffer);
|
||||
|
||||
if (has_name) builder.AppendCString(")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int WasmStackFrame::GetPosition() const {
|
||||
return IsInterpreted()
|
||||
? offset_
|
||||
@ -861,24 +655,6 @@ int AsmJsWasmStackFrame::GetColumnNumber() {
|
||||
return Script::GetColumnNumber(script, GetPosition()) + 1;
|
||||
}
|
||||
|
||||
void AsmJsWasmStackFrame::ToString(IncrementalStringBuilder& builder) {
|
||||
// The string should look exactly as the respective javascript frame string.
|
||||
// Keep this method in line to
|
||||
// JSStackFrame::ToString(IncrementalStringBuilder&).
|
||||
Handle<Object> function_name = GetFunctionName();
|
||||
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
builder.AppendCString(" (");
|
||||
}
|
||||
|
||||
AppendFileLocation(isolate_, this, &builder);
|
||||
|
||||
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FrameArrayIterator::FrameArrayIterator(Isolate* isolate,
|
||||
Handle<FrameArray> array, int frame_ix)
|
||||
: isolate_(isolate), array_(array), frame_ix_(frame_ix) {}
|
||||
@ -1094,13 +870,13 @@ MaybeHandle<Object> ErrorUtils::FormatStackTrace(Isolate* isolate,
|
||||
|
||||
wasm::WasmCodeRefScope wasm_code_ref_scope;
|
||||
|
||||
Handle<FrameArray> frame_array = GetFrameArrayFromStackTrace(isolate, elems);
|
||||
for (FrameArrayIterator it(isolate, frame_array); it.HasFrame();
|
||||
it.Advance()) {
|
||||
for (int i = 0; i < elems->length(); ++i) {
|
||||
builder.AppendCString("\n at ");
|
||||
|
||||
StackFrameBase* frame = it.Frame();
|
||||
frame->ToString(builder);
|
||||
Handle<StackTraceFrame> frame(StackTraceFrame::cast(elems->get(i)),
|
||||
isolate);
|
||||
SerializeStackTraceFrame(isolate, frame, builder);
|
||||
|
||||
if (isolate->has_pending_exception()) {
|
||||
// CallSite.toString threw. Parts of the current frame might have been
|
||||
// stringified already regardless. Still, try to append a string
|
||||
|
@ -24,7 +24,6 @@ class WasmCode;
|
||||
// Forward declarations.
|
||||
class AbstractCode;
|
||||
class FrameArray;
|
||||
class IncrementalStringBuilder;
|
||||
class JSMessageObject;
|
||||
class LookupIterator;
|
||||
class SharedFunctionInfo;
|
||||
@ -94,9 +93,6 @@ class StackFrameBase {
|
||||
virtual bool IsConstructor() = 0;
|
||||
virtual bool IsStrict() const = 0;
|
||||
|
||||
MaybeHandle<String> ToString();
|
||||
virtual void ToString(IncrementalStringBuilder& builder) = 0;
|
||||
|
||||
// Used to signal that the requested field is unknown.
|
||||
static const int kNone = -1;
|
||||
|
||||
@ -139,8 +135,6 @@ class JSStackFrame : public StackFrameBase {
|
||||
bool IsConstructor() override { return is_constructor_; }
|
||||
bool IsStrict() const override { return is_strict_; }
|
||||
|
||||
void ToString(IncrementalStringBuilder& builder) override;
|
||||
|
||||
private:
|
||||
JSStackFrame() = default;
|
||||
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
|
||||
@ -189,8 +183,6 @@ class WasmStackFrame : public StackFrameBase {
|
||||
bool IsStrict() const override { return false; }
|
||||
bool IsInterpreted() const { return code_ == nullptr; }
|
||||
|
||||
void ToString(IncrementalStringBuilder& builder) override;
|
||||
|
||||
protected:
|
||||
Handle<Object> Null() const;
|
||||
|
||||
@ -226,8 +218,6 @@ class AsmJsWasmStackFrame : public WasmStackFrame {
|
||||
int GetLineNumber() override;
|
||||
int GetColumnNumber() override;
|
||||
|
||||
void ToString(IncrementalStringBuilder& builder) override;
|
||||
|
||||
private:
|
||||
friend class FrameArrayIterator;
|
||||
AsmJsWasmStackFrame() = default;
|
||||
|
@ -3689,13 +3689,8 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
|
||||
const bool is_wasm = frame_array->IsAnyWasmFrame(index);
|
||||
|
||||
// Line numbers are 1-based, for Wasm we need to adjust.
|
||||
int line = it.Frame()->GetLineNumber();
|
||||
if (is_wasm && line >= 0) line++;
|
||||
|
||||
// Column numbers are 1-based, for Wasm we need to adjust.
|
||||
int column = it.Frame()->GetColumnNumber();
|
||||
if (is_wasm && column >= 0) column++;
|
||||
|
||||
const int script_id = it.Frame()->GetScriptId();
|
||||
|
||||
@ -3711,7 +3706,6 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
|
||||
Handle<Object> function = it.Frame()->GetFunction();
|
||||
if (function->IsJSFunction()) {
|
||||
Handle<JSFunction> fun = Handle<JSFunction>::cast(function);
|
||||
function_name = JSFunction::GetDebugName(fun);
|
||||
|
||||
is_user_java_script = fun->shared().IsUserJavaScript();
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "src/objects/stack-frame-info.h"
|
||||
|
||||
#include "src/objects/stack-frame-info-inl.h"
|
||||
#include "src/strings/string-builder-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -15,12 +16,30 @@ int StackTraceFrame::GetLineNumber(Handle<StackTraceFrame> frame) {
|
||||
return line != StackFrameBase::kNone ? line : Message::kNoLineNumberInfo;
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetOneBasedLineNumber(Handle<StackTraceFrame> frame) {
|
||||
// JavaScript line numbers are already 1-based. Wasm line numbers need
|
||||
// to be adjusted.
|
||||
int line = StackTraceFrame::GetLineNumber(frame);
|
||||
if (StackTraceFrame::IsWasm(frame) && line >= 0) line++;
|
||||
return line;
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetColumnNumber(Handle<StackTraceFrame> frame) {
|
||||
int column = GetFrameInfo(frame)->column_number();
|
||||
return column != StackFrameBase::kNone ? column : Message::kNoColumnInfo;
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetOneBasedColumnNumber(Handle<StackTraceFrame> frame) {
|
||||
// JavaScript colun numbers are already 1-based. Wasm column numbers need
|
||||
// to be adjusted.
|
||||
int column = StackTraceFrame::GetColumnNumber(frame);
|
||||
if (StackTraceFrame::IsWasm(frame) && column >= 0) column++;
|
||||
return column;
|
||||
}
|
||||
|
||||
// static
|
||||
int StackTraceFrame::GetScriptId(Handle<StackTraceFrame> frame) {
|
||||
int id = GetFrameInfo(frame)->script_id();
|
||||
@ -152,5 +171,233 @@ Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
|
||||
return handle(FrameArray::cast(frame->frame_array()), isolate);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsNonEmptyString(Handle<Object> object) {
|
||||
return (object->IsString() && String::cast(*object).length() > 0);
|
||||
}
|
||||
|
||||
void AppendFileLocation(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder* builder) {
|
||||
Handle<Object> file_name = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
|
||||
if (!file_name->IsString() && StackTraceFrame::IsEval(frame)) {
|
||||
Handle<Object> eval_origin = StackTraceFrame::GetEvalOrigin(frame);
|
||||
DCHECK(eval_origin->IsString());
|
||||
builder->AppendString(Handle<String>::cast(eval_origin));
|
||||
builder->AppendCString(", "); // Expecting source position to follow.
|
||||
}
|
||||
|
||||
if (IsNonEmptyString(file_name)) {
|
||||
builder->AppendString(Handle<String>::cast(file_name));
|
||||
} else {
|
||||
// Source code does not originate from a file and is not native, but we
|
||||
// can still get the source position inside the source string, e.g. in
|
||||
// an eval string.
|
||||
builder->AppendCString("<anonymous>");
|
||||
}
|
||||
|
||||
int line_number = StackTraceFrame::GetLineNumber(frame);
|
||||
if (line_number != Message::kNoLineNumberInfo) {
|
||||
builder->AppendCharacter(':');
|
||||
builder->AppendInt(line_number);
|
||||
|
||||
int column_number = StackTraceFrame::GetColumnNumber(frame);
|
||||
if (column_number != Message::kNoColumnInfo) {
|
||||
builder->AppendCharacter(':');
|
||||
builder->AppendInt(column_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int StringIndexOf(Isolate* isolate, Handle<String> subject,
|
||||
Handle<String> pattern) {
|
||||
if (pattern->length() > subject->length()) return -1;
|
||||
return String::IndexOf(isolate, subject, pattern, 0);
|
||||
}
|
||||
|
||||
// Returns true iff
|
||||
// 1. the subject ends with '.' + pattern, or
|
||||
// 2. subject == pattern.
|
||||
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
|
||||
Handle<String> pattern) {
|
||||
if (String::Equals(isolate, subject, pattern)) return true;
|
||||
|
||||
FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
|
||||
FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));
|
||||
|
||||
int pattern_index = pattern_reader.length() - 1;
|
||||
int subject_index = subject_reader.length() - 1;
|
||||
for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1.
|
||||
if (subject_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uc32 subject_char = subject_reader.Get(subject_index);
|
||||
if (i == pattern_reader.length()) {
|
||||
if (subject_char != '.') return false;
|
||||
} else if (subject_char != pattern_reader.Get(pattern_index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pattern_index--;
|
||||
subject_index--;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppendMethodCall(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder* builder) {
|
||||
Handle<Object> type_name = StackTraceFrame::GetTypeName(frame);
|
||||
Handle<Object> method_name = StackTraceFrame::GetMethodName(frame);
|
||||
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
|
||||
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
Handle<String> function_string = Handle<String>::cast(function_name);
|
||||
if (IsNonEmptyString(type_name)) {
|
||||
Handle<String> type_string = Handle<String>::cast(type_name);
|
||||
bool starts_with_type_name =
|
||||
(StringIndexOf(isolate, function_string, type_string) == 0);
|
||||
if (!starts_with_type_name) {
|
||||
builder->AppendString(type_string);
|
||||
builder->AppendCharacter('.');
|
||||
}
|
||||
}
|
||||
builder->AppendString(function_string);
|
||||
|
||||
if (IsNonEmptyString(method_name)) {
|
||||
Handle<String> method_string = Handle<String>::cast(method_name);
|
||||
if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
|
||||
builder->AppendCString(" [as ");
|
||||
builder->AppendString(method_string);
|
||||
builder->AppendCharacter(']');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IsNonEmptyString(type_name)) {
|
||||
builder->AppendString(Handle<String>::cast(type_name));
|
||||
builder->AppendCharacter('.');
|
||||
}
|
||||
if (IsNonEmptyString(method_name)) {
|
||||
builder->AppendString(Handle<String>::cast(method_name));
|
||||
} else {
|
||||
builder->AppendCString("<anonymous>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeJSStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder& builder) {
|
||||
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
|
||||
|
||||
const bool is_toplevel = StackTraceFrame::IsToplevel(frame);
|
||||
const bool is_async = StackTraceFrame::IsAsync(frame);
|
||||
const bool is_promise_all = StackTraceFrame::IsPromiseAll(frame);
|
||||
const bool is_constructor = StackTraceFrame::IsConstructor(frame);
|
||||
const bool is_method_call = !(is_toplevel || is_constructor);
|
||||
|
||||
if (is_async) {
|
||||
builder.AppendCString("async ");
|
||||
}
|
||||
if (is_promise_all) {
|
||||
builder.AppendCString("Promise.all (index ");
|
||||
builder.AppendInt(StackTraceFrame::GetPromiseAllIndex(frame));
|
||||
builder.AppendCString(")");
|
||||
return;
|
||||
}
|
||||
if (is_method_call) {
|
||||
AppendMethodCall(isolate, frame, &builder);
|
||||
} else if (is_constructor) {
|
||||
builder.AppendCString("new ");
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
} else {
|
||||
builder.AppendCString("<anonymous>");
|
||||
}
|
||||
} else if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
} else {
|
||||
AppendFileLocation(isolate, frame, &builder);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendCString(" (");
|
||||
AppendFileLocation(isolate, frame, &builder);
|
||||
builder.AppendCString(")");
|
||||
}
|
||||
|
||||
void SerializeAsmJsWasmStackFrame(Isolate* isolate,
|
||||
Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder& builder) {
|
||||
// The string should look exactly as the respective javascript frame string.
|
||||
// Keep this method in line to
|
||||
// JSStackFrame::ToString(IncrementalStringBuilder&).
|
||||
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
|
||||
|
||||
if (IsNonEmptyString(function_name)) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
builder.AppendCString(" (");
|
||||
}
|
||||
|
||||
AppendFileLocation(isolate, frame, &builder);
|
||||
|
||||
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void SerializeWasmStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder& builder) {
|
||||
Handle<Object> module_name = StackTraceFrame::GetWasmModuleName(frame);
|
||||
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
|
||||
const bool has_name = !module_name->IsNull() || !function_name->IsNull();
|
||||
if (has_name) {
|
||||
if (module_name->IsNull()) {
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
} else {
|
||||
builder.AppendString(Handle<String>::cast(module_name));
|
||||
if (!function_name->IsNull()) {
|
||||
builder.AppendCString(".");
|
||||
builder.AppendString(Handle<String>::cast(function_name));
|
||||
}
|
||||
}
|
||||
builder.AppendCString(" (");
|
||||
}
|
||||
|
||||
const int wasm_func_index = StackTraceFrame::GetLineNumber(frame);
|
||||
|
||||
builder.AppendCString("wasm-function[");
|
||||
builder.AppendInt(wasm_func_index);
|
||||
builder.AppendCString("]:");
|
||||
|
||||
char buffer[16];
|
||||
SNPrintF(ArrayVector(buffer), "0x%x",
|
||||
StackTraceFrame::GetColumnNumber(frame));
|
||||
builder.AppendCString(buffer);
|
||||
|
||||
if (has_name) builder.AppendCString(")");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder& builder) {
|
||||
// Ordering here is important, as AsmJs frames are also marked as Wasm.
|
||||
if (StackTraceFrame::IsAsmJsWasm(frame)) {
|
||||
SerializeAsmJsWasmStackFrame(isolate, frame, builder);
|
||||
} else if (StackTraceFrame::IsWasm(frame)) {
|
||||
SerializeWasmStackFrame(isolate, frame, builder);
|
||||
} else {
|
||||
SerializeJSStackFrame(isolate, frame, builder);
|
||||
}
|
||||
}
|
||||
|
||||
MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
|
||||
Handle<StackTraceFrame> frame) {
|
||||
IncrementalStringBuilder builder(isolate);
|
||||
SerializeStackTraceFrame(isolate, frame, builder);
|
||||
return builder.Finish();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -85,7 +85,9 @@ class StackTraceFrame : public Struct {
|
||||
TORQUE_GENERATED_STACK_TRACE_FRAME_FIELDS)
|
||||
|
||||
static int GetLineNumber(Handle<StackTraceFrame> frame);
|
||||
static int GetOneBasedLineNumber(Handle<StackTraceFrame> frame);
|
||||
static int GetColumnNumber(Handle<StackTraceFrame> frame);
|
||||
static int GetOneBasedColumnNumber(Handle<StackTraceFrame> frame);
|
||||
static int GetScriptId(Handle<StackTraceFrame> frame);
|
||||
static int GetPromiseAllIndex(Handle<StackTraceFrame> frame);
|
||||
|
||||
@ -121,6 +123,12 @@ V8_EXPORT_PRIVATE
|
||||
Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
|
||||
Handle<FixedArray> stack_trace);
|
||||
|
||||
class IncrementalStringBuilder;
|
||||
void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
|
||||
IncrementalStringBuilder& builder);
|
||||
MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
|
||||
Handle<StackTraceFrame> frame);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -190,7 +190,9 @@ static void checkStackFrame(const char* expected_script_name,
|
||||
} else {
|
||||
CHECK_NOT_NULL(strstr(*script_name, expected_script_name));
|
||||
}
|
||||
CHECK_NOT_NULL(strstr(*func_name, expected_func_name));
|
||||
if (!frame->GetFunctionName().IsEmpty()) {
|
||||
CHECK_NOT_NULL(strstr(*func_name, expected_func_name));
|
||||
}
|
||||
CHECK_EQ(expected_line_number, frame->GetLineNumber());
|
||||
CHECK_EQ(expected_column, frame->GetColumn());
|
||||
CHECK_EQ(is_eval, frame->IsEval());
|
||||
|
@ -105,7 +105,7 @@ Running test: testConsoleLog
|
||||
callFrames : [
|
||||
[0] : {
|
||||
columnNumber : 8
|
||||
functionName :
|
||||
functionName : eval
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url :
|
||||
|
@ -96,7 +96,7 @@ Test runtime stack trace:
|
||||
}
|
||||
[1] : {
|
||||
columnNumber : 0
|
||||
functionName :
|
||||
functionName : eval
|
||||
lineNumber : 0
|
||||
scriptId : <scriptId>
|
||||
url : boo.js
|
||||
|
Loading…
Reference in New Issue
Block a user