aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp188
-rw-r--r--src/bun.js/bindings/bindings.cpp28
-rw-r--r--src/bun.js/javascript.zig22
3 files changed, 200 insertions, 38 deletions
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index b27e0aafc..4b4edf097 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -219,7 +219,9 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
JSC::Options::useJITCage() = false;
JSC::Options::useShadowRealm() = true;
JSC::Options::useResizableArrayBuffer() = true;
+#ifdef BUN_DEBUG
JSC::Options::showPrivateScriptsInStackTraces() = true;
+#endif
JSC::Options::useSetMethods() = true;
/*
@@ -312,6 +314,123 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c
}
extern "C" void* Bun__getVM();
+extern "C" JSGlobalObject* Bun__getDefaultGlobal();
+
+static String computeErrorInfoWithoutPrepareStackTrace(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsigned& line, unsigned& column, String& sourceURL, JSObject* errorInstance)
+{
+ if (!errorInstance) {
+ return String();
+ }
+
+ Zig::GlobalObject* globalObject = jsDynamicCast<Zig::GlobalObject*>(errorInstance->globalObject());
+ if (!globalObject) {
+ // Happens in node:vm
+ globalObject = jsDynamicCast<Zig::GlobalObject*>(Bun__getDefaultGlobal());
+ }
+
+ WTF::String name = "Error"_s;
+ WTF::String message;
+
+ if (errorInstance) {
+ // Note that we are not allowed to allocate memory in here. It's called inside a finalizer.
+ if (auto* instance = jsDynamicCast<ErrorInstance*>(errorInstance)) {
+ name = instance->sanitizedNameString(globalObject);
+ message = instance->sanitizedMessageString(globalObject);
+ }
+ }
+
+ WTF::StringBuilder sb;
+
+ if (!name.isEmpty()) {
+ sb.append(name);
+ sb.append(": "_s);
+ }
+
+ if (!message.isEmpty()) {
+ sb.append(message);
+ }
+
+ if (stackTrace.isEmpty()) {
+ return sb.toString();
+ }
+
+ if ((!message.isEmpty() || !name.isEmpty())) {
+ sb.append("\n"_s);
+ }
+
+ size_t framesCount = stackTrace.size();
+ ZigStackFrame remappedFrames[framesCount];
+ bool hasSet = false;
+ for (size_t i = 0; i < framesCount; i++) {
+ StackFrame& frame = stackTrace.at(i);
+
+ sb.append(" at "_s);
+
+ WTF::String functionName = frame.functionName(vm);
+
+ if (auto codeblock = frame.codeBlock()) {
+ if (codeblock->isConstructor()) {
+ sb.append("new "_s);
+ }
+
+ // TODO: async
+ }
+
+ if (functionName.isEmpty()) {
+ sb.append("<anonymous>"_s);
+ } else {
+ sb.append(functionName);
+ }
+
+ sb.append(" ("_s);
+
+ if (frame.hasLineAndColumnInfo()) {
+ unsigned int thisLine = 0;
+ unsigned int thisColumn = 0;
+ frame.computeLineAndColumn(thisLine, thisColumn);
+ remappedFrames[i].position.line = thisLine;
+ remappedFrames[i].position.column_start = thisColumn;
+ remappedFrames[i].source_url = Zig::toZigString(frame.sourceURL(vm));
+
+ // This ensures the lifetime of the sourceURL is accounted for correctly
+ Bun__remapStackFramePositions(globalObject, remappedFrames + i, 1);
+
+ if (!hasSet) {
+ hasSet = true;
+ line = thisLine;
+ column = thisColumn;
+ sourceURL = frame.sourceURL(vm);
+
+ if (errorInstance) {
+ if (remappedFrames[i].remapped) {
+ errorInstance->putDirect(vm, Identifier::fromString(vm, "originalLine"_s), jsNumber(thisLine), 0);
+ errorInstance->putDirect(vm, Identifier::fromString(vm, "originalColumn"_s), jsNumber(thisColumn), 0);
+ }
+ }
+ }
+
+ sb.append(frame.sourceURL(vm));
+ sb.append(":"_s);
+ sb.append(remappedFrames[i].position.line);
+ sb.append(":"_s);
+ sb.append(remappedFrames[i].position.column_start);
+ } else {
+ sb.append("native"_s);
+ }
+ sb.append(")"_s);
+
+ if (i != framesCount - 1) {
+ sb.append("\n"_s);
+ }
+ }
+
+ return sb.toString();
+}
+
+static String computeErrorInfo(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsigned& line, unsigned& column, String& sourceURL, JSObject* errorInstance)
+{
+ return computeErrorInfoWithoutPrepareStackTrace(vm, stackTrace, line, column, sourceURL, errorInstance);
+}
extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObjectClass, int count,
void* console_client)
@@ -329,6 +448,9 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObje
Zig::GlobalObject* globalObject = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), JSC::jsNull()));
globalObject->setConsole(globalObject);
globalObject->isThreadLocalDefaultGlobalObject = true;
+ globalObject->setStackTraceLimit(DEFAULT_ERROR_STACK_TRACE_LIMIT); // Node.js defaults to 10
+ vm.setOnComputeErrorInfo(computeErrorInfo);
+
if (count > 0) {
globalObject->installAPIGlobals(globalObjectClass, count, vm);
}
@@ -2585,7 +2707,32 @@ JSC::JSValue GlobalObject::formatStackTrace(JSC::VM& vm, JSC::JSGlobalObject* le
extern "C" EncodedJSValue JSPasswordObject__create(JSC::JSGlobalObject*, bool);
-JSC_DECLARE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace);
+JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncAppendStackTrace, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
+{
+ GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
+ JSC::VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSC::ErrorInstance* source = jsDynamicCast<JSC::ErrorInstance*>(callFrame->argument(0));
+ JSC::ErrorInstance* destination = jsDynamicCast<JSC::ErrorInstance*>(callFrame->argument(1));
+
+ if (!source || !destination) {
+ throwTypeError(lexicalGlobalObject, scope, "First & second argument must be an Error object"_s);
+ return JSC::JSValue::encode(jsUndefined());
+ }
+
+ if (!destination->stackTrace()) {
+ destination->captureStackTrace(vm, globalObject, 1);
+ }
+
+ if (source->stackTrace()) {
+ destination->stackTrace()->appendVector(*source->stackTrace());
+ source->stackTrace()->clear();
+ }
+
+ return JSC::JSValue::encode(jsUndefined());
+}
+
JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
{
GlobalObject* globalObject = reinterpret_cast<GlobalObject*>(lexicalGlobalObject);
@@ -2600,18 +2747,15 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb
JSC::JSObject* errorObject = objectArg.asCell()->getObject();
JSC::JSValue caller = callFrame->argument(1);
+ // We cannot use our ErrorInstance::captureStackTrace() fast path here unfortunately.
+ // We need to return these CallSite array objects which means we need to create them
JSValue errorValue = lexicalGlobalObject->get(lexicalGlobalObject, vm.propertyNames->Error);
auto* errorConstructor = jsDynamicCast<JSC::JSObject*>(errorValue);
-
- size_t stackTraceLimit = DEFAULT_ERROR_STACK_TRACE_LIMIT;
- if (JSC::JSValue stackTraceLimitProp = errorConstructor->getIfPropertyExists(lexicalGlobalObject, vm.propertyNames->stackTraceLimit)) {
- if (stackTraceLimitProp.isNumber()) {
- stackTraceLimit = std::min(std::max(static_cast<size_t>(stackTraceLimitProp.toIntegerOrInfinity(lexicalGlobalObject)), 0ul), 2048ul);
- if (stackTraceLimit == 0) {
- stackTraceLimit = 2048;
- }
- }
+ size_t stackTraceLimit = globalObject->stackTraceLimit().value();
+ if (stackTraceLimit == 0) {
+ stackTraceLimit = DEFAULT_ERROR_STACK_TRACE_LIMIT;
}
+
JSCStackTrace stackTrace = JSCStackTrace::captureCurrentJSStackTrace(globalObject, callFrame, stackTraceLimit, caller);
// Create an (uninitialized) array for our "call sites"
@@ -2672,9 +2816,20 @@ JSC_DEFINE_HOST_FUNCTION(errorConstructorFuncCaptureStackTrace, (JSC::JSGlobalOb
errorObject->deleteProperty(lexicalGlobalObject, vm.propertyNames->stack);
}
if (formattedStackTrace.isUndefinedOrNull()) {
- errorObject->putDirect(vm, vm.propertyNames->stack, jsUndefined(), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
- } else {
- errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum);
+ formattedStackTrace = JSC::jsUndefined();
+ }
+
+ errorObject->putDirect(vm, vm.propertyNames->stack, formattedStackTrace, 0);
+
+ if (!(caller && caller.isObject())) {
+ if (auto* instance = jsDynamicCast<JSC::ErrorInstance*>(errorObject)) {
+ // we make a separate copy of the StackTrace unfortunately so that we
+ // can later console.log it without losing the info
+ //
+ // This is not good. We should remove this in the future as it strictly makes this function
+ // already slower than necessary.
+ instance->captureStackTrace(vm, globalObject, 1, false);
+ }
}
RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSValue {}));
@@ -3118,11 +3273,8 @@ void GlobalObject::finishCreation(VM& vm)
RELEASE_ASSERT(classInfo());
JSC::JSObject* errorConstructor = this->errorConstructor();
- errorConstructor->putDirectNativeFunctionWithoutTransition(vm, this, JSC::Identifier::fromString(vm, "captureStackTrace"_s), 2, errorConstructorFuncCaptureStackTrace, ImplementationVisibility::Public, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0);
-
- // JSC default is 100
- errorConstructor->putDirect(vm, vm.propertyNames->stackTraceLimit, jsNumber(DEFAULT_ERROR_STACK_TRACE_LIMIT), JSC::PropertyAttribute::DontEnum | 0);
-
+ errorConstructor->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "captureStackTrace"_s), 2, errorConstructorFuncCaptureStackTrace, ImplementationVisibility::Public, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0);
+ errorConstructor->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "appendStackTrace"_s), 2, errorConstructorFuncAppendStackTrace, ImplementationVisibility::Private, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0);
JSC::JSValue console = this->get(this, JSC::Identifier::fromString(vm, "console"_s));
JSC::JSObject* consoleObject = console.getObject();
consoleObject->putDirectBuiltinFunction(vm, this, vm.propertyNames->asyncIteratorSymbol, consoleObjectAsyncIteratorCodeGenerator(vm), PropertyAttribute::Builtin | PropertyAttribute::DontDelete);
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index 3f3d82dc4..68aef29dd 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -3528,12 +3528,13 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
JSC::JSValue val)
{
JSC::JSObject* obj = JSC::jsDynamicCast<JSC::JSObject*>(val);
+ JSC::VM& vm = global->vm();
bool getFromSourceURL = false;
if (stackTrace != nullptr && stackTrace->size() > 0) {
- populateStackTrace(global->vm(), *stackTrace, &except->stack);
+ populateStackTrace(vm, *stackTrace, &except->stack);
} else if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) {
- populateStackTrace(global->vm(), *err->stackTrace(), &except->stack);
+ populateStackTrace(vm, *err->stackTrace(), &except->stack);
} else {
getFromSourceURL = true;
}
@@ -3546,7 +3547,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
}
if (except->code == SYNTAX_ERROR_CODE) {
except->message = Zig::toZigString(err->sanitizedMessageString(global));
- } else if (JSC::JSValue message = obj->getIfPropertyExists(global, global->vm().propertyNames->message)) {
+ } else if (JSC::JSValue message = obj->getIfPropertyExists(global, vm.propertyNames->message)) {
except->message = Zig::toZigString(message, global);
@@ -3556,7 +3557,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
except->name = Zig::toZigString(err->sanitizedNameString(global));
except->runtime_type = err->runtimeTypeForCause();
- auto clientData = WebCore::clientData(global->vm());
+ auto clientData = WebCore::clientData(vm);
if (except->code != SYNTAX_ERROR_CODE) {
if (JSC::JSValue syscall = obj->getIfPropertyExists(global, clientData->builtinNames().syscallPublicName())) {
@@ -3571,7 +3572,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
except->path = Zig::toZigString(path, global);
}
- if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(global->vm(), "fd"_s))) {
+ if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(vm, "fd"_s))) {
if (fd.isAnyInt()) {
except->fd = fd.toInt32(global);
}
@@ -3583,17 +3584,17 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
}
if (getFromSourceURL) {
- if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, global->vm().propertyNames->sourceURL)) {
+ if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, vm.propertyNames->sourceURL)) {
except->stack.frames_ptr[0].source_url = Zig::toZigString(sourceURL, global);
- if (JSC::JSValue column = obj->getIfPropertyExists(global, global->vm().propertyNames->column)) {
+ if (JSC::JSValue column = obj->getIfPropertyExists(global, vm.propertyNames->column)) {
except->stack.frames_ptr[0].position.column_start = column.toInt32(global);
}
- if (JSC::JSValue line = obj->getIfPropertyExists(global, global->vm().propertyNames->line)) {
+ if (JSC::JSValue line = obj->getIfPropertyExists(global, vm.propertyNames->line)) {
except->stack.frames_ptr[0].position.line = line.toInt32(global);
- if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(global->vm(), "lineText"_s))) {
+ if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "lineText"_s))) {
if (JSC::JSString* jsStr = lineText.toStringOrNull(global)) {
auto str = jsStr->value(global);
except->stack.source_lines_ptr[0] = Zig::toZigString(str);
@@ -3603,7 +3604,9 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global,
}
}
}
+
except->stack.frames_len = 1;
+ except->stack.frames_ptr[0].remapped = obj->hasProperty(global, JSC::Identifier::fromString(vm, "originalLine"_s));
}
}
@@ -3654,7 +3657,12 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal
if (JSC::JSValue line = obj->getIfPropertyExists(global, global->vm().propertyNames->line)) {
if (line) {
- except->stack.frames_ptr[0].position.line = line.toInt32(global);
+ // TODO: don't sourcemap it twice
+ if (auto originalLine = obj->getIfPropertyExists(global, JSC::Identifier::fromString(global->vm(), "originalLine"_s))) {
+ except->stack.frames_ptr[0].position.line = originalLine.toInt32(global);
+ } else {
+ except->stack.frames_ptr[0].position.line = line.toInt32(global);
+ }
except->stack.frames_len = 1;
}
}
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index cb1a50f1d..5c58eff60 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -1981,19 +1981,21 @@ pub const VirtualMachine = struct {
}
pub fn remapStackFramePositions(this: *VirtualMachine, frames: [*]JSC.ZigStackFrame, frames_count: usize) void {
- var i: usize = 0;
- while (i < frames_count) : (i += 1) {
- if (frames[i].position.isInvalid()) continue;
+ for (frames[0..frames_count]) |*frame| {
+ if (frame.position.isInvalid() or frame.remapped) continue;
+ var sourceURL = frame.source_url.toSlice(bun.default_allocator);
+ defer sourceURL.deinit();
+
if (this.source_mappings.resolveMapping(
- frames[i].source_url.slice(),
- @max(frames[i].position.line, 0),
- @max(frames[i].position.column_start, 0),
+ sourceURL.slice(),
+ @max(frame.position.line, 0),
+ @max(frame.position.column_start, 0),
)) |mapping| {
- frames[i].position.line = mapping.original.lines;
- frames[i].position.column_start = mapping.original.columns;
- frames[i].remapped = true;
+ frame.position.line = mapping.original.lines;
+ frame.position.column_start = mapping.original.columns;
+ frame.remapped = true;
} else {
- frames[i].remapped = true;
+ frame.remapped = true;
}
}
}