aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Keyhan Vakil <kvakil@sylph.kvakil.me> 2023-06-19 23:28:40 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-19 23:28:40 -0700
commitbdbb637b3d870f1955cadd342eeae0147f50c3de (patch)
tree41ef0eaadf53f5fbc9cce96bfec0bada3c53b13d /src
parente9e0e051569d3858cfc18b21a6aa6d1b7184f7e7 (diff)
downloadbun-bdbb637b3d870f1955cadd342eeae0147f50c3de.tar.gz
bun-bdbb637b3d870f1955cadd342eeae0147f50c3de.tar.zst
bun-bdbb637b3d870f1955cadd342eeae0147f50c3de.zip
implement more of V8's stack trace API (#3359)
- fix source map positions for getLineNumber / getColumnNumber - fix return value getting coerced to a string - implement CallFrame.p.toString - add tests for getFunction, getThis, isConstructor, isNative, toString, getLineNumber, getColumnNumber still not implemented: - isPromiseAll/getPromiseIndex - getEvalOrigin - getScriptHash - getPosition - getEnclosingColumnNumber/getEnclosingLineNumber - isAsync - accessing Error.stack should call prepareStackTrace still broken: - isEval: often returns false when it should return true - isToplevel: often returns true when it should return false Refs: https://v8.dev/docs/stack-trace-api Refs: v8/src/objects/call-site-info.cc Fixes: https://github.com/oven-sh/bun/issues/2883
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/CallSite.cpp36
-rw-r--r--src/bun.js/bindings/CallSite.h5
-rw-r--r--src/bun.js/bindings/CallSitePrototype.cpp14
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp57
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h2
-rw-r--r--src/bun.js/bindings/headers-handwritten.h2
-rw-r--r--src/bun.js/javascript.zig3
7 files changed, 80 insertions, 39 deletions
diff --git a/src/bun.js/bindings/CallSite.cpp b/src/bun.js/bindings/CallSite.cpp
index 02ac35168..e4b2fc3a7 100644
--- a/src/bun.js/bindings/CallSite.cpp
+++ b/src/bun.js/bindings/CallSite.cpp
@@ -86,6 +86,42 @@ void CallSite::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisCallSite->m_sourceURL);
}
+void CallSite::formatAsString(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WTF::StringBuilder &sb) {
+ JSString* myTypeName = jsTypeStringForValue(globalObject, thisValue());
+ JSString* myFunction = functionName().toString(globalObject);
+ JSString* myFunctionName = functionName().toString(globalObject);
+ JSString* mySourceURL = sourceURL().toString(globalObject);
+
+ JSString* myColumnNumber = columnNumber().toInt32(globalObject) != -1 ? columnNumber().toString(globalObject) : jsEmptyString(vm);
+ JSString* myLineNumber = lineNumber().toInt32(globalObject) != -1 ? lineNumber().toString(globalObject) : jsEmptyString(vm);
+
+ bool myIsConstructor = isConstructor();
+
+ if (myFunctionName->length() > 0) {
+ if (myIsConstructor) {
+ sb.append("new "_s);
+ } else {
+ // TODO: print type or class name if available
+ // sb.append(myTypeName->getString(globalObject));
+ // sb.append(" "_s);
+ }
+ sb.append(myFunctionName->getString(globalObject));
+ } else {
+ sb.append("<anonymous>"_s);
+ }
+ sb.append(" ("_s);
+ if (isNative()) {
+ sb.append("native"_s);
+ } else {
+ sb.append(mySourceURL->getString(globalObject));
+ sb.append(":"_s);
+ sb.append(myLineNumber->getString(globalObject));
+ sb.append(":"_s);
+ sb.append(myColumnNumber->getString(globalObject));
+ }
+ sb.append(")"_s);
+}
+
DEFINE_VISIT_CHILDREN(CallSite);
}
diff --git a/src/bun.js/bindings/CallSite.h b/src/bun.js/bindings/CallSite.h
index 432725b8a..8780eb8fb 100644
--- a/src/bun.js/bindings/CallSite.h
+++ b/src/bun.js/bindings/CallSite.h
@@ -77,6 +77,11 @@ public:
bool isStrict() const { return m_flags & static_cast<unsigned int>(Flags::IsStrict); }
bool isNative() const { return m_flags & static_cast<unsigned int>(Flags::IsNative); }
+ void setLineNumber(JSC::JSValue lineNumber) { m_lineNumber = lineNumber; }
+ void setColumnNumber(JSC::JSValue columnNumber) { m_columnNumber = columnNumber; }
+
+ void formatAsString(JSC::VM& vm, JSC::JSGlobalObject* globalObject, WTF::StringBuilder &sb);
+
private:
CallSite(JSC::VM& vm, JSC::Structure* structure)
: Base(vm, structure)
diff --git a/src/bun.js/bindings/CallSitePrototype.cpp b/src/bun.js/bindings/CallSitePrototype.cpp
index f3e365cb0..1ea1a2c86 100644
--- a/src/bun.js/bindings/CallSitePrototype.cpp
+++ b/src/bun.js/bindings/CallSitePrototype.cpp
@@ -35,6 +35,7 @@ static JSC_DECLARE_HOST_FUNCTION(callSiteProtoFuncIsConstructor);
static JSC_DECLARE_HOST_FUNCTION(callSiteProtoFuncIsAsync);
static JSC_DECLARE_HOST_FUNCTION(callSiteProtoFuncIsPromiseAll);
static JSC_DECLARE_HOST_FUNCTION(callSiteProtoFuncGetPromiseIndex);
+static JSC_DECLARE_HOST_FUNCTION(callSiteProtoFuncToString);
ALWAYS_INLINE static CallSite* getCallSite(JSGlobalObject* globalObject, JSC::JSValue thisValue)
{
@@ -82,6 +83,7 @@ static const HashTableValue CallSitePrototypeTableValues[]
{ "isAsync"_s, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Function, NoIntrinsic, { HashTableValue::NativeFunctionType, callSiteProtoFuncIsAsync, 0 } },
{ "isPromiseAll"_s, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Function, NoIntrinsic, { HashTableValue::NativeFunctionType, callSiteProtoFuncIsPromiseAll, 0 } },
{ "getPromiseIndex"_s, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Function, NoIntrinsic, { HashTableValue::NativeFunctionType, callSiteProtoFuncGetPromiseIndex, 0 } },
+ { "toString"_s, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::Function, NoIntrinsic, { HashTableValue::NativeFunctionType, callSiteProtoFuncToString, 0 } },
};
const JSC::ClassInfo CallSitePrototype::s_info = { "CallSite"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(CallSitePrototype) };
@@ -222,7 +224,15 @@ JSC_DEFINE_HOST_FUNCTION(callSiteProtoFuncGetPromiseIndex, (JSGlobalObject * glo
{
ENTER_PROTO_FUNC();
- return JSC::JSValue::encode(JSC::jsNumber(0));
+ return JSC::JSValue::encode(JSC::jsNull());
}
-} \ No newline at end of file
+JSC_DEFINE_HOST_FUNCTION(callSiteProtoFuncToString, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
+{
+ ENTER_PROTO_FUNC();
+ WTF::StringBuilder sb;
+ callSite->formatAsString(vm, globalObject, sb);
+ return JSC::JSValue::encode(JSC::JSValue(jsString(vm, sb.toString())));
+}
+
+}
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index ec4976bc4..9b3bfd2a2 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -2502,7 +2502,7 @@ void GlobalObject::createCallSitesFromFrames(JSC::JSGlobalObject* lexicalGlobalO
}
}
-JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSObject* errorObject, JSC::JSArray* callSites, ZigStackFrame remappedStackFrames[])
+JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSObject* errorObject, JSC::JSArray* callSites)
{
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue errorValue = this->get(this, JSC::Identifier::fromString(vm, "Error"_s));
@@ -2557,39 +2557,8 @@ JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* le
for (size_t i = 0; i < framesCount; i++) {
JSC::JSValue callSiteValue = callSites->getIndex(lexicalGlobalObject, i);
CallSite* callSite = JSC::jsDynamicCast<CallSite*>(callSiteValue);
-
- JSString* typeName = jsTypeStringForValue(lexicalGlobalObject, callSite->thisValue());
- JSString* function = callSite->functionName().toString(lexicalGlobalObject);
- JSString* functionName = callSite->functionName().toString(lexicalGlobalObject);
- JSString* sourceURL = callSite->sourceURL().toString(lexicalGlobalObject);
- JSString* columnNumber = remappedStackFrames[i].position.column_start >= 0 ? jsNontrivialString(vm, String::number(remappedStackFrames[i].position.column_start)) : jsEmptyString(vm);
- JSString* lineNumber = remappedStackFrames[i].position.line >= 0 ? jsNontrivialString(vm, String::number(remappedStackFrames[i].position.line)) : jsEmptyString(vm);
- bool isConstructor = callSite->isConstructor();
-
sb.append(" at "_s);
- if (functionName->length() > 0) {
- if (isConstructor) {
- sb.append("new "_s);
- } else {
- // TODO: print type or class name if available
- // sb.append(typeName->getString(lexicalGlobalObject));
- // sb.append(" "_s);
- }
- sb.append(functionName->getString(lexicalGlobalObject));
- } else {
- sb.append("<anonymous>"_s);
- }
- sb.append(" ("_s);
- if (callSite->isNative()) {
- sb.append("native"_s);
- } else {
- sb.append(sourceURL->getString(lexicalGlobalObject));
- sb.append(":"_s);
- sb.append(lineNumber->getString(lexicalGlobalObject));
- sb.append(":"_s);
- sb.append(columnNumber->getString(lexicalGlobalObject));
- }
- sb.append(")"_s);
+ callSite->formatAsString(vm, lexicalGlobalObject, sb);
if (i != framesCount - 1) {
sb.append("\n"_s);
}
@@ -2598,7 +2567,6 @@ JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* le
return JSC::JSValue(jsString(vm, sb.toString()));
}
-extern "C" void Bun__remapStackFramePositions(JSC::JSGlobalObject*, ZigStackFrame*, size_t);
extern "C" EncodedJSValue JSPasswordObject__create(JSC::JSGlobalObject*, bool);
JSC_DECLARE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace);
@@ -2662,9 +2630,26 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb
}
// remap line and column start to original source
+ // XXX: this function does not fully populate the fields of ZigStackFrame,
+ // be careful reading the fields below.
Bun__remapStackFramePositions(lexicalGlobalObject, remappedFrames, framesCount);
- JSC::JSValue formattedStackTrace = globalObject->formatStackTrace(vm, lexicalGlobalObject, errorObject, callSites, remappedFrames);
+ // write the remapped lines back to the CallSites
+ for (size_t i = 0; i < framesCount; i++) {
+ JSC::JSValue callSiteValue = callSites->getIndex(lexicalGlobalObject, i);
+ CallSite* callSite = JSC::jsDynamicCast<CallSite*>(callSiteValue);
+ if (remappedFrames[i].remapped) {
+ int32_t remappedColumnStart = remappedFrames[i].position.column_start;
+ JSC::JSValue columnNumber = JSC::jsNumber(remappedColumnStart);
+ callSite->setColumnNumber(columnNumber);
+
+ int32_t remappedLine = remappedFrames[i].position.line;
+ JSC::JSValue lineNumber = JSC::jsNumber(remappedLine);
+ callSite->setLineNumber(lineNumber);
+ }
+ }
+
+ JSC::JSValue formattedStackTrace = globalObject->formatStackTrace(vm, lexicalGlobalObject, errorObject, callSites);
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode({}));
if (errorObject->hasProperty(lexicalGlobalObject, vm.propertyNames->stack)) {
@@ -2673,7 +2658,7 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb
if (formattedStackTrace.isUndefinedOrNull()) {
errorObject->putDirect(vm, vm.propertyNames->stack, jsUndefined(), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
} else {
- errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace.toString(lexicalGlobalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
}
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSValue {}));
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index dda1c8330..2d69e764f 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -167,7 +167,7 @@ public:
void clearDOMGuardedObjects();
static void createCallSitesFromFrames(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ObjectInitializationScope& objectScope, JSCStackTrace& stackTrace, JSC::JSArray* callSites);
- JSC::JSValue formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSObject* errorObject, JSC::JSArray* callSites, ZigStackFrame remappedStackFrames[]);
+ JSC::JSValue formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSObject* errorObject, JSC::JSArray* callSites);
static void reportUncaughtExceptionAtEventLoop(JSGlobalObject*, JSC::Exception*);
static JSGlobalObject* deriveShadowRealmGlobalObject(JSGlobalObject* globalObject);
diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h
index b845c4e00..a4287cf2e 100644
--- a/src/bun.js/bindings/headers-handwritten.h
+++ b/src/bun.js/bindings/headers-handwritten.h
@@ -343,6 +343,8 @@ bool Bun__deepEquals(JSC::JSGlobalObject* globalObject, JSC::JSValue v1, JSC::JS
bool Bun__deepMatch(JSC::JSValue object, JSC::JSValue subset, JSC::JSGlobalObject* globalObject, JSC::ThrowScope* throwScope, bool replacePropsWithAsymmetricMatchers);
+extern "C" void Bun__remapStackFramePositions(JSC::JSGlobalObject*, ZigStackFrame*, size_t);
+
namespace Inspector {
class ScriptArguments;
}
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 63d024ad7..d458b6e7e 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -1983,6 +1983,9 @@ pub const VirtualMachine = struct {
)) |mapping| {
frames[i].position.line = mapping.original.lines;
frames[i].position.column_start = mapping.original.columns;
+ frames[i].remapped = true;
+ } else {
+ frames[i].remapped = true;
}
}
}