Skip to content

Commit a295239

Browse files
committed
src: refactor JSError stringifier
Essentially move all the logic related to how JSError stack traces are accessible to llv8 files, breaking this logic into smaller functions. Also moved the chunk of code related to printing JSObject properties to its own function, to allow reuse between JSError and JSObject stringifiers. PR-URL: #256 Reviewed-By: Joyee Cheung <[email protected]>
1 parent 9f3dce3 commit a295239

File tree

5 files changed

+220
-95
lines changed

5 files changed

+220
-95
lines changed

src/llv8-inl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,18 @@ inline bool JSArrayBuffer::WasNeutered(Error& err) {
722722
return field != 0;
723723
}
724724

725+
inline std::string JSError::stack_trace_property() {
726+
// TODO (mmarchini): once we have Symbol support we'll need to search for
727+
// <unnamed symbol>, since the stack symbol doesn't have an external name.
728+
// In the future we can add postmortem metadata on V8 regarding existing
729+
// symbols, but for now we'll use an heuristic to find the stack in the
730+
// error object.
731+
return v8()->types()->kSymbolType != -1 ? "Symbol()" : "<non-string>";
732+
}
733+
734+
inline int StackTrace::GetFrameCount() { return len_; }
735+
736+
725737
#undef ACCESSOR
726738

727739
} // namespace v8

src/llv8.cc

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,5 +1202,111 @@ v8::Value JSObject::GetArrayElement(int64_t pos, Error& err) {
12021202
}
12031203

12041204

1205+
bool JSError::HasStackTrace(Error& err) {
1206+
StackTrace stack_trace = GetStackTrace(err);
1207+
1208+
return stack_trace.GetFrameCount() > -1;
1209+
}
1210+
1211+
1212+
JSArray JSError::GetFrameArray(Error& err) {
1213+
v8::Value maybe_stack = GetProperty(stack_trace_property(), err);
1214+
1215+
if (err.Fail() || maybe_stack.raw() == -1) {
1216+
Error::PrintInDebugMode(
1217+
"Couldn't find a symbol property in the Error object.");
1218+
return JSArray();
1219+
}
1220+
1221+
int64_t type = v8::HeapObject(maybe_stack).GetType(err);
1222+
1223+
if (err.Fail()) {
1224+
Error::PrintInDebugMode("Symbol property references an invalid object.");
1225+
return JSArray();
1226+
}
1227+
1228+
// NOTE (mmarchini): The stack is stored as a JSArray
1229+
if (type != v8()->types()->kJSArrayType) {
1230+
Error::PrintInDebugMode("Symbol property doesn't have the right type.");
1231+
return JSArray();
1232+
}
1233+
1234+
v8::JSArray arr(maybe_stack);
1235+
1236+
return arr;
1237+
}
1238+
1239+
1240+
StackTrace::Iterator StackTrace::begin() {
1241+
Error err;
1242+
return StackTrace::Iterator(this, err);
1243+
}
1244+
1245+
StackTrace::Iterator StackTrace::end() {
1246+
Error err;
1247+
return StackTrace::Iterator(this, GetFrameCount(), err);
1248+
}
1249+
1250+
StackTrace::StackTrace(JSArray frame_array, Error& err)
1251+
: frame_array_(frame_array) {
1252+
v8::Value maybe_stack_len = frame_array.GetArrayElement(0, err);
1253+
1254+
if (err.Fail()) {
1255+
Error::PrintInDebugMode(
1256+
"Couldn't get the first element from the stack array");
1257+
return;
1258+
}
1259+
1260+
len_ = v8::Smi(maybe_stack_len).GetValue();
1261+
1262+
multiplier_ = 5;
1263+
// On Node.js v8.x, the first array element is the stack size, and each
1264+
// stack frame use 5 elements.
1265+
if ((len_ * multiplier_ + 1) != frame_array_.GetArrayLength(err)) {
1266+
// On Node.js v6.x, the first array element is zero, and each stack frame
1267+
// use 4 element.
1268+
multiplier_ = 4;
1269+
if ((len_ != 0) ||
1270+
((frame_array_.GetArrayLength(err) - 1) % multiplier_ != 0)) {
1271+
Error::PrintInDebugMode(
1272+
"JSArray doesn't look like a Stack Frames array. stack_len: %lld "
1273+
"array_len: %lld",
1274+
len_, frame_array_.GetArrayLength(err));
1275+
len_ = -1;
1276+
multiplier_ = -1;
1277+
return;
1278+
}
1279+
len_ = (frame_array_.GetArrayLength(err) - 1) / multiplier_;
1280+
}
1281+
}
1282+
1283+
1284+
StackTrace JSError::GetStackTrace(Error& err) {
1285+
return StackTrace(GetFrameArray(err), err);
1286+
}
1287+
1288+
1289+
StackFrame StackTrace::GetFrame(uint32_t index, Error& err) {
1290+
return StackFrame(this, index);
1291+
}
1292+
1293+
StackFrame::StackFrame(StackTrace* stack_trace, int index)
1294+
: stack_trace_(stack_trace), index_(index) {}
1295+
1296+
JSFunction StackFrame::GetFunction(Error& err) {
1297+
JSArray frame_array = stack_trace_->frame_array_;
1298+
v8::Value maybe_fn = frame_array.GetArrayElement(frame_array_index(), err);
1299+
if (err.Fail()) return JSFunction();
1300+
return JSFunction(maybe_fn);
1301+
}
1302+
1303+
int StackFrame::frame_array_index() {
1304+
int multiplier = stack_trace_->multiplier_;
1305+
const int js_function_pos = 1;
1306+
const int begin_offset = 1;
1307+
return begin_offset + js_function_pos + (index_ * multiplier);
1308+
}
1309+
1310+
12051311
} // namespace v8
12061312
} // namespace llnode

src/llv8.h

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class Value {
5959

6060
protected:
6161
LLV8* v8_;
62-
int64_t raw_;
62+
int64_t raw_ = 0x0;
6363
};
6464

6565
class Smi : public Value {
@@ -267,18 +267,88 @@ class JSObject : public HeapObject {
267267
Value GetDescriptorProperty(std::string key_name, Map map, Error& err);
268268
};
269269

270+
class StackTrace;
271+
class StackFrame;
272+
class JSFunction;
273+
class JSArray : public JSObject {
274+
public:
275+
V8_VALUE_DEFAULT_METHODS(JSArray, JSObject);
276+
277+
inline Smi Length(Error& err);
278+
};
279+
270280
class JSError : public JSObject {
271281
public:
272282
V8_VALUE_DEFAULT_METHODS(JSError, JSObject);
283+
284+
bool HasStackTrace(Error& err);
285+
StackTrace GetStackTrace(Error& err);
286+
287+
private:
288+
JSArray GetFrameArray(Error& err);
289+
290+
inline std::string stack_trace_property();
273291
};
274292

275-
class JSArray : public JSObject {
293+
class StackFrame {
276294
public:
277-
V8_VALUE_DEFAULT_METHODS(JSArray, JSObject);
295+
JSFunction GetFunction(Error& err);
278296

279-
inline Smi Length(Error& err);
297+
private:
298+
friend StackTrace;
299+
StackFrame() = delete;
300+
StackFrame(StackTrace*, int);
301+
302+
StackTrace* stack_trace_;
303+
int index_ = -1;
304+
305+
int frame_array_index();
306+
};
307+
308+
309+
class StackTrace {
310+
public:
311+
class Iterator {
312+
public:
313+
StackFrame operator*() { return stack_trace_->GetFrame(index_, err_); };
314+
inline const StackTrace::Iterator operator++() {
315+
return StackTrace::Iterator(stack_trace_, ++index_, err_);
316+
};
317+
bool operator!=(StackTrace::Iterator that) {
318+
return index() != that.index();
319+
};
320+
321+
inline Iterator(StackTrace* stack_trace, Error& err)
322+
: stack_trace_(stack_trace), err_(err){};
323+
324+
inline Iterator(StackTrace* stack_trace, int index, Error& err)
325+
: stack_trace_(stack_trace), index_(index), err_(err){};
326+
327+
int index() { return index_; };
328+
329+
private:
330+
StackTrace* stack_trace_;
331+
int index_ = 0;
332+
Error err_;
333+
};
334+
Iterator begin();
335+
Iterator end();
336+
337+
StackTrace() = delete;
338+
StackTrace(JSArray frame_array, Error& err);
339+
340+
StackFrame GetFrame(uint32_t index, Error& err);
341+
342+
inline int GetFrameCount();
343+
344+
private:
345+
friend StackFrame;
346+
JSArray frame_array_;
347+
int multiplier_ = -1;
348+
int len_ = -1;
280349
};
281350

351+
282352
class JSFunction : public JSObject {
283353
public:
284354
V8_VALUE_DEFAULT_METHODS(JSFunction, JSObject)

src/printer.cc

Lines changed: 27 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -547,99 +547,25 @@ std::string Printer::Stringify(v8::JSError js_error, Error& err) {
547547

548548
// Print properties in detailed mode
549549
if (options_.detailed) {
550-
output << rang::fg::reset << " " << StringifyProperties(js_error, err);
551-
if (err.Fail()) return std::string();
552-
553-
std::string fields = StringifyInternalFields(js_error, err);
554-
if (err.Fail()) return std::string();
555-
556-
if (!fields.empty()) {
557-
output << std::endl << rang::fg::magenta << " internal fields"
558-
<< rang::fg::reset << " {" << std::endl << fields << "}";
559-
}
560-
}
561-
562-
if (options_.detailed) {
563-
PrinterOptions simple;
564-
565-
// TODO (mmarchini): once we have Symbol support we'll need to search for
566-
// <unnamed symbol>, since the stack symbol doesn't have an external name.
567-
// In the future we can add postmortem metadata on V8 regarding existing
568-
// symbols, but for now we'll use an heuristic to find the stack in the
569-
// error object.
570-
std::string stack_property = llv8_->types()->kSymbolType != -1 ? "Symbol()" : "<non-string>";
571-
v8::Value maybe_stack = js_error.GetProperty(stack_property, err);
572-
573-
if (err.Fail() || maybe_stack.raw() == -1) {
574-
Error::PrintInDebugMode(
575-
"Couldn't find a symbol property in the Error object.");
576-
output << rang::fg::yellow << ">" << rang::fg::reset;
577-
return output.str();
578-
}
579-
580-
int64_t type = v8::HeapObject(maybe_stack).GetType(err);
550+
output << StringifyJSObjectFields(js_error, err);
581551

582-
if (err.Fail()) {
583-
Error::PrintInDebugMode("Symbol property references an invalid object.");
584-
output << rang::fg::yellow << ">" << rang::fg::reset;
585-
return output.str();
586-
}
587-
588-
// NOTE (mmarchini): The stack is stored as a JSArray
589-
if (type != llv8_->types()->kJSArrayType) {
590-
Error::PrintInDebugMode("Symbol property doesn't have the right type.");
591-
output << rang::fg::yellow << ">" << rang::fg::reset;
592-
return output.str();
593-
}
594-
595-
v8::JSArray arr(maybe_stack);
596-
597-
v8::Value maybe_stack_len = arr.GetArrayElement(0, err);
598-
599-
if (err.Fail()) {
600-
Error::PrintInDebugMode(
601-
"Couldn't get the first element from the stack array");
602-
output << rang::fg::yellow << ">" << rang::fg::reset;
603-
return output.str();
604-
}
605-
606-
int64_t stack_len = v8::Smi(maybe_stack_len).GetValue();
607-
608-
int multiplier = 5;
609-
// On Node.js v8.x, the first array element is the stack size, and each
610-
// stack frame use 5 elements.
611-
if ((stack_len * multiplier + 1) != arr.GetArrayLength(err)) {
612-
// On Node.js v6.x, the first array element is zero, and each stack frame
613-
// use 4 element.
614-
multiplier = 4;
615-
if ((stack_len != 0) ||
616-
((arr.GetArrayLength(err) - 1) % multiplier != 0)) {
617-
Error::PrintInDebugMode(
618-
"JSArray doesn't look like a Stack Frames array. stack_len: %lld "
619-
"array_len: %lld",
620-
stack_len, arr.GetArrayLength(err));
621-
output << rang::fg::yellow << ">" << rang::fg::reset;
622-
return output.str();
623-
}
624-
stack_len = (arr.GetArrayLength(err) - 1) / multiplier;
625-
}
552+
v8::StackTrace stack_trace = js_error.GetStackTrace(err);
626553

627554
std::stringstream error_stack;
628555
error_stack << std::endl
629556
<< rang::fg::red << " error stack" << rang::fg::reset << " {"
630557
<< std::endl;
631558

632-
// TODO (mmarchini): Refactor: create an StackIterator which returns
633-
// StackFrame objects
634-
for (int64_t i = 0; i < stack_len; i++) {
635-
v8::Value maybe_fn = arr.GetArrayElement(2 + (i * multiplier), err);
559+
Printer printer(llv8_);
560+
for (v8::StackFrame frame : stack_trace) {
561+
v8::JSFunction js_function = frame.GetFunction(err);
636562
if (err.Fail()) {
637563
error_stack << rang::fg::gray << " <unknown>" << std::endl;
638564
continue;
639565
}
640566

641-
Printer printer(llv8_);
642-
error_stack << " " << printer.Stringify(v8::HeapObject(maybe_fn), err)
567+
error_stack << " "
568+
<< printer.Stringify<v8::HeapObject>(js_function, err)
643569
<< std::endl;
644570
}
645571
error_stack << " }";
@@ -661,19 +587,29 @@ std::string Printer::Stringify(v8::JSObject js_object, Error& err) {
661587
output << rang::fg::yellow << "<Object: " << name;
662588

663589
// Print properties in detailed mode
664-
if (options_.detailed) {
665-
output << rang::fg::reset << " " << StringifyProperties(js_object, err);
666-
if (err.Fail()) return std::string();
590+
if (options_.detailed) output << StringifyJSObjectFields(js_object, err);
667591

668-
std::string fields = StringifyInternalFields(js_object, err);
669-
if (err.Fail()) return std::string();
592+
output << rang::fg::yellow << ">" << rang::fg::reset;
670593

671-
if (!fields.empty()) {
672-
output << std::endl << rang::fg::magenta << " internal fields"
673-
<< rang::fg::reset << " {" << std::endl << fields << "}";
674-
}
594+
return output.str();
595+
}
596+
597+
std::string Printer::StringifyJSObjectFields(v8::JSObject js_object,
598+
Error& err) {
599+
std::stringstream output;
600+
601+
output << rang::fg::reset << " " << StringifyProperties(js_object, err);
602+
if (err.Fail()) return std::string();
603+
604+
std::string fields = StringifyInternalFields(js_object, err);
605+
if (err.Fail()) return std::string();
606+
607+
if (!fields.empty()) {
608+
output << std::endl
609+
<< rang::fg::magenta << " internal fields" << rang::fg::reset
610+
<< " {" << std::endl
611+
<< fields << "}";
675612
}
676-
output << rang::fg::yellow << ">" << rang::fg::reset;
677613

678614
return output.str();
679615
}

src/printer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class Printer {
5555
std::string StringifyDescriptors(v8::JSObject js_obj, v8::Map map,
5656
Error& err);
5757

58+
std::string StringifyJSObjectFields(v8::JSObject js_obj, Error& err);
5859

5960
// FixedArray Specific Methods
6061
std::string StringifyContents(v8::FixedArray fixed_array, int length,

0 commit comments

Comments
 (0)