Commit b4c1aefb authored by jgruber's avatar jgruber Committed by Commit bot

Refactor data structures for simple stack traces

Simple stack traces are captured through Isolate::CaptureSimpleStackTrace.
Captured frames are stored in a FixedArray, which in turn is stored as a
property (using a private symbol) on the error object itself. Actual formatting
of the textual stack trace is done lazily when the user reads the stack
property of the error object.

This would involve many conversions back and forth between index-encoded raw
data (receiver, function, offset and code), JS CallSite objects, and C++
CallSite objects.

This commit refactors the C++ CallSite class into a Struct class called
StackTraceFrame, which is the new single point of truth frame information.
Isolate::CaptureSimpleStackTrace stores an array of StackTraceFrames, and JS
CallSite objects (now created only when the user specifies custom stack trace
formatting through Error.prepareStackTrace) internally only store a reference
to a StackTraceFrame.

BUG=

Review-Url: https://codereview.chromium.org/2230953002
Cr-Commit-Position: refs/heads/master@{#38645}
parent 0686c414
......@@ -7623,8 +7623,8 @@ class Internals {
static const int kNodeIsPartiallyDependentShift = 4;
static const int kNodeIsActiveShift = 4;
static const int kJSObjectType = 0xb7;
static const int kJSApiObjectType = 0xb6;
static const int kJSObjectType = 0xb8;
static const int kJSApiObjectType = 0xb7;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x83;
static const int kForeignType = 0x87;
......
......@@ -11,15 +11,22 @@
namespace v8 {
namespace internal {
#define CHECK_CALLSITE(recv, method) \
CHECK_RECEIVER(JSObject, recv, method); \
if (!JSReceiver::HasOwnProperty( \
recv, isolate->factory()->call_site_position_symbol()) \
.FromMaybe(false)) { \
THROW_NEW_ERROR_RETURN_FAILURE( \
isolate, \
NewTypeError(MessageTemplate::kCallSiteMethod, \
isolate->factory()->NewStringFromAsciiChecked(method))); \
#define CHECK_CALLSITE(recv, method) \
CHECK_RECEIVER(JSObject, recv, method); \
Handle<StackTraceFrame> frame; \
{ \
Handle<Object> frame_obj; \
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( \
isolate, frame_obj, \
JSObject::GetProperty(recv, \
isolate->factory()->call_site_frame_symbol())); \
if (!frame_obj->IsStackTraceFrame()) { \
THROW_NEW_ERROR_RETURN_FAILURE( \
isolate, NewTypeError(MessageTemplate::kCallSiteMethod, \
isolate->factory()->NewStringFromAsciiChecked( \
method))); \
} \
frame = Handle<StackTraceFrame>::cast(frame_obj); \
}
namespace {
......@@ -34,167 +41,107 @@ Object* PositiveNumberOrNull(int value, Isolate* isolate) {
BUILTIN(CallSitePrototypeGetColumnNumber) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getColumnNumber");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return PositiveNumberOrNull(call_site.GetColumnNumber(), isolate);
return PositiveNumberOrNull(frame->GetColumnNumber(), isolate);
}
BUILTIN(CallSitePrototypeGetEvalOrigin) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getEvalOrigin");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetEvalOrigin();
return *frame->GetEvalOrigin();
}
BUILTIN(CallSitePrototypeGetFileName) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getFileName");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetFileName();
}
namespace {
bool CallSiteIsStrict(Isolate* isolate, Handle<JSObject> receiver) {
Handle<Object> strict;
Handle<Symbol> symbol = isolate->factory()->call_site_strict_symbol();
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, strict,
JSObject::GetProperty(receiver, symbol));
return strict->BooleanValue();
return *frame->GetFileName();
}
} // namespace
BUILTIN(CallSitePrototypeGetFunction) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getFunction");
if (CallSiteIsStrict(isolate, recv))
if (frame->IsStrict() || frame->IsWasmFrame()) {
return *isolate->factory()->undefined_value();
}
Handle<Symbol> symbol = isolate->factory()->call_site_function_symbol();
RETURN_RESULT_OR_FAILURE(isolate, JSObject::GetProperty(recv, symbol));
return frame->function();
}
BUILTIN(CallSitePrototypeGetFunctionName) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getFunctionName");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetFunctionName();
return *frame->GetFunctionName();
}
BUILTIN(CallSitePrototypeGetLineNumber) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getLineNumber");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
int line_number = call_site.IsWasm() ? call_site.wasm_func_index()
: call_site.GetLineNumber();
return PositiveNumberOrNull(line_number, isolate);
return PositiveNumberOrNull(frame->GetLineNumber(), isolate);
}
BUILTIN(CallSitePrototypeGetMethodName) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getMethodName");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetMethodName();
return *frame->GetMethodName();
}
BUILTIN(CallSitePrototypeGetPosition) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getPosition");
Handle<Symbol> symbol = isolate->factory()->call_site_position_symbol();
RETURN_RESULT_OR_FAILURE(isolate, JSObject::GetProperty(recv, symbol));
return Smi::FromInt(frame->GetPosition());
}
BUILTIN(CallSitePrototypeGetScriptNameOrSourceURL) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getScriptNameOrSourceUrl");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetScriptNameOrSourceUrl();
return *frame->GetScriptNameOrSourceUrl();
}
BUILTIN(CallSitePrototypeGetThis) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getThis");
if (CallSiteIsStrict(isolate, recv))
return *isolate->factory()->undefined_value();
Handle<Object> receiver;
Handle<Symbol> symbol = isolate->factory()->call_site_receiver_symbol();
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
JSObject::GetProperty(recv, symbol));
if (*receiver == isolate->heap()->call_site_constructor_symbol())
if (frame->IsStrict() || frame->ForceConstructor() || frame->IsWasmFrame()) {
return *isolate->factory()->undefined_value();
}
return *receiver;
return frame->receiver();
}
BUILTIN(CallSitePrototypeGetTypeName) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "getTypeName");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return *call_site.GetTypeName();
return *frame->GetTypeName();
}
BUILTIN(CallSitePrototypeIsConstructor) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "isConstructor");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return isolate->heap()->ToBoolean(call_site.IsConstructor());
return isolate->heap()->ToBoolean(frame->IsConstructor());
}
BUILTIN(CallSitePrototypeIsEval) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "isEval");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return isolate->heap()->ToBoolean(call_site.IsEval());
return isolate->heap()->ToBoolean(frame->IsEval());
}
BUILTIN(CallSitePrototypeIsNative) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "isNative");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return isolate->heap()->ToBoolean(call_site.IsNative());
return isolate->heap()->ToBoolean(frame->IsNative());
}
BUILTIN(CallSitePrototypeIsToplevel) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "isToplevel");
CallSite call_site(isolate, recv);
CHECK(call_site.IsJavaScript() || call_site.IsWasm());
return isolate->heap()->ToBoolean(call_site.IsToplevel());
return isolate->heap()->ToBoolean(frame->IsToplevel());
}
BUILTIN(CallSitePrototypeToString) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "toString");
RETURN_RESULT_OR_FAILURE(isolate, CallSiteUtils::ToString(isolate, recv));
return *frame->ToString();
}
#undef CHECK_CALLSITE
......
......@@ -434,31 +434,6 @@ void StackGuard::InitThread(const ExecutionAccess& lock) {
// --- C a l l s t o n a t i v e s ---
Handle<String> Execution::GetStackTraceLine(Handle<Object> recv,
Handle<JSFunction> fun,
Handle<Object> pos,
Handle<Object> is_global) {
Isolate* isolate = fun->GetIsolate();
Handle<Object> strict_mode = isolate->factory()->ToBoolean(false);
MaybeHandle<Object> maybe_callsite =
CallSiteUtils::Construct(isolate, recv, fun, pos, strict_mode);
if (maybe_callsite.is_null()) {
isolate->clear_pending_exception();
return isolate->factory()->empty_string();
}
MaybeHandle<String> maybe_to_string =
CallSiteUtils::ToString(isolate, maybe_callsite.ToHandleChecked());
if (maybe_to_string.is_null()) {
isolate->clear_pending_exception();
return isolate->factory()->empty_string();
}
return maybe_to_string.ToHandleChecked();
}
void StackGuard::HandleGCInterrupt() {
if (CheckAndClearInterrupt(GC_REQUEST)) {
isolate_->heap()->HandleGCRequest();
......
......@@ -48,11 +48,6 @@ class Execution final : public AllStatic {
Handle<Object> receiver, int argc,
Handle<Object> argv[],
MaybeHandle<Object>* exception_out = NULL);
static Handle<String> GetStackTraceLine(Handle<Object> recv,
Handle<JSFunction> fun,
Handle<Object> pos,
Handle<Object> is_global);
};
......
......@@ -917,6 +917,13 @@ Handle<Script> Factory::NewScript(Handle<String> source) {
return script;
}
Handle<StackTraceFrame> Factory::NewStackTraceFrame() {
Handle<StackTraceFrame> frame =
Handle<StackTraceFrame>::cast(NewStruct(STACK_TRACE_FRAME_TYPE));
frame->set_flags(0);
frame->set_offset(0);
return frame;
}
Handle<Foreign> Factory::NewForeign(Address addr, PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(isolate(),
......
......@@ -292,6 +292,8 @@ class Factory final {
Handle<Script> NewScript(Handle<String> source);
Handle<StackTraceFrame> NewStackTraceFrame();
// Foreign objects are pretenured when allocated by the bootstrapper.
Handle<Foreign> NewForeign(Address addr,
PretenureFlag pretenure = NOT_TENURED);
......
......@@ -161,13 +161,7 @@
V(array_iteration_kind_symbol) \
V(array_iterator_next_symbol) \
V(array_iterator_object_symbol) \
V(call_site_constructor_symbol) \
V(call_site_function_symbol) \
V(call_site_position_symbol) \
V(call_site_receiver_symbol) \
V(call_site_strict_symbol) \
V(call_site_wasm_obj_symbol) \
V(call_site_wasm_func_index_symbol) \
V(call_site_frame_symbol) \
V(class_end_position_symbol) \
V(class_start_position_symbol) \
V(detailed_stack_trace_symbol) \
......
This diff is collapsed.
This diff is collapsed.
......@@ -42,40 +42,6 @@ class MessageLocation {
Handle<JSFunction> function_;
};
class CallSite {
public:
CallSite(Isolate* isolate, Handle<JSObject> call_site_obj);
Handle<Object> GetFileName();
Handle<Object> GetFunctionName();
Handle<Object> GetScriptNameOrSourceUrl();
Handle<Object> GetMethodName();
Handle<Object> GetTypeName();
Handle<Object> GetEvalOrigin();
// Return 1-based line number, including line offset.
int GetLineNumber();
// Return 1-based column number, including column offset if first line.
int GetColumnNumber();
bool IsNative();
bool IsToplevel();
bool IsEval();
bool IsConstructor();
bool IsJavaScript() { return !fun_.is_null(); }
bool IsWasm() { return !wasm_obj_.is_null(); }
int wasm_func_index() const { return wasm_func_index_; }
private:
Isolate* isolate_;
Handle<Object> receiver_;
Handle<JSFunction> fun_;
int32_t pos_ = -1;
Handle<JSObject> wasm_obj_;
uint32_t wasm_func_index_ = static_cast<uint32_t>(-1);
};
// Determines how stack trace collection skips frames.
enum FrameSkipMode {
// Unconditionally skips the first frame. Used e.g. when the Error constructor
......@@ -107,16 +73,6 @@ class ErrorUtils : public AllStatic {
Handle<Object> stack_trace);
};
class CallSiteUtils : public AllStatic {
public:
static MaybeHandle<Object> Construct(Isolate* isolate,
Handle<Object> receiver,
Handle<Object> fun, Handle<Object> pos,
Handle<Object> strict_mode);
static MaybeHandle<String> ToString(Isolate* isolate, Handle<Object> recv);
};
#define MESSAGE_TEMPLATES(T) \
/* Error */ \
T(None, "") \
......
......@@ -1016,6 +1016,10 @@ void Script::ScriptVerify() {
VerifyPointer(line_ends());
}
void StackTraceFrame::StackTraceFrameVerify() {
CHECK(IsStackTraceFrame());
VerifyPointer(abstract_code());
}
void NormalizedMapCache::NormalizedMapCacheVerify() {
FixedArray::cast(this)->FixedArrayVerify();
......
......@@ -5809,6 +5809,29 @@ void Script::set_origin_options(ScriptOriginOptions origin_options) {
(origin_options.Flags() << kOriginOptionsShift));
}
SMI_ACCESSORS(StackTraceFrame, flags, kFlagsOffset)
ACCESSORS(StackTraceFrame, abstract_code, AbstractCode, kAbstractCodeOffset)
SMI_ACCESSORS(StackTraceFrame, offset, kOffsetOffset)
ACCESSORS_CHECKED(StackTraceFrame, receiver, Object, kReceiverOffset,
!IsWasmFrame())
ACCESSORS_CHECKED(StackTraceFrame, function, JSFunction, kFunctionOffset,
!IsWasmFrame())
ACCESSORS_CHECKED(StackTraceFrame, wasm_object, Object, kWasmObjectOffset,
IsWasmFrame())
SMI_ACCESSORS_CHECKED(StackTraceFrame, wasm_function_index,
kWasmFunctionIndexOffset, IsWasmFrame())
bool StackTraceFrame::IsWasmFrame() const {
return ((flags() & kIsWasmFrame) != 0);
}
bool StackTraceFrame::IsJavaScriptFrame() const { return !IsWasmFrame(); }
bool StackTraceFrame::IsStrict() const { return ((flags() & kIsStrict) != 0); }
bool StackTraceFrame::ForceConstructor() const {
return ((flags() & kForceConstructor) != 0);
}
ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex)
ACCESSORS(DebugInfo, debug_bytecode_array, Object, kDebugBytecodeArrayIndex)
......
......@@ -1274,6 +1274,21 @@ void Script::ScriptPrint(std::ostream& os) { // NOLINT
os << "\n";
}
void StackTraceFrame::StackTraceFramePrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "StackTraceFrame");
os << "\n - flags: " << flags();
os << "\n - abstract_code: " << Brief(abstract_code());
os << "\n - offset: " << offset();
if (IsWasmFrame()) {
os << "\n - wasm_object: " << Brief(wasm_object());
os << "\n - wasm_function_index data: " << wasm_function_index();
} else {
DCHECK(IsJavaScriptFrame());
os << "\n - receiver: " << Brief(receiver());
os << "\n - function: " << Brief(function());
}
os << "\n";
}
void DebugInfo::DebugInfoPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "DebugInfo");
......
This diff is collapsed.
......@@ -388,6 +388,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(ALLOCATION_MEMENTO_TYPE) \
V(ALLOCATION_SITE_TYPE) \
V(SCRIPT_TYPE) \
V(STACK_TRACE_FRAME_TYPE) \
V(TYPE_FEEDBACK_INFO_TYPE) \
V(ALIASED_ARGUMENTS_ENTRY_TYPE) \
V(BOX_TYPE) \
......@@ -503,6 +504,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(FUNCTION_TEMPLATE_INFO, FunctionTemplateInfo, function_template_info) \
V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
V(SCRIPT, Script, script) \
V(STACK_TRACE_FRAME, StackTraceFrame, stack_trace_frame) \
V(ALLOCATION_SITE, AllocationSite, allocation_site) \
V(ALLOCATION_MEMENTO, AllocationMemento, allocation_memento) \
V(TYPE_FEEDBACK_INFO, TypeFeedbackInfo, type_feedback_info) \
......@@ -674,6 +676,7 @@ enum InstanceType {
ALLOCATION_SITE_TYPE,
ALLOCATION_MEMENTO_TYPE,
SCRIPT_TYPE,
STACK_TRACE_FRAME_TYPE,
TYPE_FEEDBACK_INFO_TYPE,
ALIASED_ARGUMENTS_ENTRY_TYPE,
BOX_TYPE,
......@@ -6491,7 +6494,6 @@ class Box : public Struct {
DISALLOW_IMPLICIT_CONSTRUCTORS(Box);
};
// Container for metadata stored on each prototype map.
class PrototypeInfo : public Struct {
public:
......@@ -6776,6 +6778,88 @@ class Script: public Struct {
DISALLOW_IMPLICIT_CONSTRUCTORS(Script);
};
class StackTraceFrame : public Struct {
public:
// --- Fields for JS and Wasm frames. ---
static const int kIsWasmFrame = 1 << 0;
static const int kForceConstructor = 1 << 1;
static const int kIsStrict = 1 << 2;
// [flags]
DECL_INT_ACCESSORS(flags)
// [abstract_code]
DECL_ACCESSORS(abstract_code, AbstractCode)
// [offset]
DECL_INT_ACCESSORS(offset)
// --- Fields for JS frames. ---
// [receiver]
DECL_ACCESSORS(receiver, Object)
// [function]
DECL_ACCESSORS(function, JSFunction)
// --- Fields for Wasm frames. ---
// [wasm_object]
DECL_ACCESSORS(wasm_object, Object)
// [wasm_function_index]
DECL_INT_ACCESSORS(wasm_function_index)
inline bool IsWasmFrame() const;
inline bool IsJavaScriptFrame() const;
inline bool IsStrict() const;
inline bool ForceConstructor() const;
Handle<Object> GetFileName();
Handle<Object> GetFunctionName();
Handle<Object> GetScriptNameOrSourceUrl();
Handle<Object> GetMethodName();
Handle<Object> GetTypeName();
Handle<Object> GetEvalOrigin();
int GetPosition();
// Return 1-based line number, including line offset.
int GetLineNumber();
// Return 1-based column number, including column offset if first line.
int GetColumnNumber();
bool IsNative();
bool IsToplevel();
bool IsEval();
bool IsConstructor();
Handle<String> ToString();
DECLARE_CAST(StackTraceFrame)
// Dispatched behavior.
DECLARE_PRINTER(StackTraceFrame)
DECLARE_VERIFIER(StackTraceFrame)
// Fields for all frame types.
static const int kFlagsOffset = HeapObject::kHeaderSize;
static const int kAbstractCodeOffset = kFlagsOffset + kPointerSize;
static const int kOffsetOffset = kAbstractCodeOffset + kPointerSize;
// Fields for JS frames.
static const int kReceiverOffset = kOffsetOffset + kPointerSize;
static const int kFunctionOffset = kReceiverOffset + kPointerSize;
// Fields for Wasm frames.
static const int kWasmObjectOffset = kOffsetOffset + kPointerSize;
static const int kWasmFunctionIndexOffset = kWasmObjectOffset + kPointerSize;
static const int kSize = kFunctionOffset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(StackTraceFrame);
};
// List of builtin functions we want to identify to improve code
// generation.
......
......@@ -115,22 +115,27 @@ RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
// TODO(wasm): This implementation is temporary, see bug #5007:
// https://bugs.chromium.org/p/v8/issues/detail?id=5007
Handle<JSObject> error = Handle<JSObject>::cast(error_obj);
// Patch the stack trace (array of <receiver, function, code, position>).
Handle<Object> stack_trace_obj = JSReceiver::GetDataProperty(
error, isolate->factory()->stack_trace_symbol());
// Patch the stack trace (array of <receiver, function, code, position>).
if (stack_trace_obj->IsJSArray()) {
Handle<FixedArray> stack_elements(
FixedArray::cast(JSArray::cast(*stack_trace_obj)->elements()));
DCHECK_EQ(1, stack_elements->length() % 4);
DCHECK(Code::cast(stack_elements->get(3))->kind() == Code::WASM_FUNCTION);
DCHECK(stack_elements->get(4)->IsSmi() &&
Smi::cast(stack_elements->get(4))->value() >= 0);
stack_elements->set(4, Smi::FromInt(-1 - byte_offset));
Handle<StackTraceFrame> frame =
handle(StackTraceFrame::cast(stack_elements->get(0)));
DCHECK(frame->IsWasmFrame());
DCHECK(frame->offset() >= 0);
frame->set_offset(-1 - byte_offset);
}
Handle<Object> detailed_stack_trace_obj = JSReceiver::GetDataProperty(
error, isolate->factory()->detailed_stack_trace_symbol());
// Patch the detailed stack trace (array of JSObjects with various
// properties).
Handle<Object> detailed_stack_trace_obj = JSReceiver::GetDataProperty(
error, isolate->factory()->detailed_stack_trace_symbol());
if (detailed_stack_trace_obj->IsJSArray()) {
Handle<FixedArray> stack_elements(
FixedArray::cast(JSArray::cast(*detailed_stack_trace_obj)->elements()));
......
......@@ -248,6 +248,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case TRANSITION_ARRAY_TYPE:
case FOREIGN_TYPE:
case SCRIPT_TYPE:
case STACK_TRACE_FRAME_TYPE:
case CODE_TYPE:
case PROPERTY_CELL_TYPE:
return kOtherInternal & kTaggedPointer;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment