diff --git a/packages/bun-native-plugin-rs/src/lib.rs b/packages/bun-native-plugin-rs/src/lib.rs index a9baf5e0dd..f2ec39487c 100644 --- a/packages/bun-native-plugin-rs/src/lib.rs +++ b/packages/bun-native-plugin-rs/src/lib.rs @@ -290,7 +290,7 @@ impl<'a> OnBeforeParse<'a> { } /// Get the output loader for the current file. - pub fn output_loader(&self) -> BunLogLevel { + pub fn output_loader(&self) -> BunLoader { unsafe { std::mem::transmute(self.result_raw.loader as u32) } } diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 0eb1e93bd5..1b8ebe4829 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -430,7 +430,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, // TODO: think about the finalizer here - Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), globalObject->m_pendingNapiModuleDlopenHandle, nullptr, nullptr); + auto *meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle); + Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr); bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); ASSERT(success); diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index a1a0f1df76..e8941ddbea 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -252,15 +252,18 @@ void BundlerPlugin::NativePluginList::append(JSC::VM& vm, JSC::RegExp* filter, S } if (index == std::numeric_limits::max()) { - this->fileCallbacks.append(std::make_pair(callback, external)); + this->fileCallbacks.append(NativePluginCallback{callback, external}); } else { if (this->namespaceCallbacks.size() <= index) { this->namespaceCallbacks.grow(index + 1); } - this->namespaceCallbacks[index].append(std::make_pair(callback, external)); + this->namespaceCallbacks[index].append(NativePluginCallback{callback, external}); } } +extern "C" void CrashHandler__setInsideNativePlugin(bool value); + + int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult) { unsigned index = 0; @@ -284,11 +287,16 @@ int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void { std::lock_guard lock(*group->at(i).second); if (group->at(i).first.match(path) > -1) { - Bun::NapiExternal* external = callbacks[i].second; + Bun::NapiExternal* external = callbacks[i].external; if (external) { ((OnBeforeParseArguments*) (onBeforeParseArgs))->external = external->value(); } - callbacks[i].first(onBeforeParseArgs, onBeforeParseResult); + + JSBundlerPluginNativeOnBeforeParseCallback callback = callbacks[i].callback; + CrashHandler__setInsideNativePlugin(true); + callback(onBeforeParseArgs, onBeforeParseResult); + CrashHandler__setInsideNativePlugin(false); + count++; } } @@ -335,12 +343,14 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onBeforeParse, (JSC::JSGlobalOb } WTF::String on_before_parse_symbol = on_before_parse_symbol_js.toWTFString(globalObject); + // The dlopen *void handle is attached to the node_addon as a NapiExternal Bun::NapiExternal* napi_external = jsDynamicCast(node_addon.getObject()->get(globalObject, WebCore::builtinNames(vm).napiDlopenHandlePrivateName())); if (UNLIKELY(!napi_external)) { Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_TYPE, "Expected node_addon (2nd argument) to have a napiDlopenHandle property"_s); return {}; } - void* dlopen_handle = napi_external->value(); + Bun::NapiModuleMeta* meta = (Bun::NapiModuleMeta*)napi_external->value(); + void* dlopen_handle = meta->dlopenHandle; #if OS(WINDOWS) BunString onbefore_parse_symbol_str = Bun::toString(on_before_parse_symbol); diff --git a/src/bun.js/bindings/JSBundlerPlugin.h b/src/bun.js/bindings/JSBundlerPlugin.h index 7e20a1adc2..bfe76b8a0b 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.h +++ b/src/bun.js/bindings/JSBundlerPlugin.h @@ -52,9 +52,14 @@ public: /// Therefore, we need a mutex to synchronize access. typedef std::pair> NativeFilterRegexp; + struct NativePluginCallback { + JSBundlerPluginNativeOnBeforeParseCallback callback; + Bun::NapiExternal* external; + }; + class NativePluginList { public: - using PerNamespaceCallbackList = Vector>; + using PerNamespaceCallbackList = Vector; Vector fileNamespace = {}; Vector namespaces = {}; diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 74345cc50b..caa5980f85 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -1005,8 +1005,10 @@ extern "C" void napi_module_register(napi_module* mod) return; } + auto *meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle); + // TODO: think about the finalizer here - Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), globalObject->m_pendingNapiModuleDlopenHandle, nullptr, nullptr); + Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr); bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); ASSERT(success); diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index 99a50648dc..90c7146d9e 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -12,6 +12,11 @@ namespace Bun { using namespace JSC; using namespace WebCore; +typedef struct { + /// The result of call to dlopen to load the module + void* dlopenHandle; +} NapiModuleMeta; + class NapiExternal : public JSC::JSDestructibleObject { using Base = JSC::JSDestructibleObject; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index d9cb08f989..6030e138b1 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -50,6 +50,12 @@ var panic_mutex = std.Thread.Mutex{}; /// This is used to catch and handle panics triggered by the panic handler. threadlocal var panic_stage: usize = 0; +threadlocal var inside_native_plugin: bool = false; + +export fn CrashHandler__setInsideNativePlugin(value: bool) callconv(.C) void { + inside_native_plugin = value; +} + /// This can be set by various parts of the codebase to indicate a broader /// action being taken. It is printed when a crash happens, which can help /// narrow down what the bug is. Example: "Crashed while parsing /path/to/file.js" @@ -226,6 +232,15 @@ pub fn crashHandler( writer.writeAll("=" ** 60 ++ "\n") catch std.posix.abort(); printMetadata(writer) catch std.posix.abort(); + + writer.print( + \\ + \\Bun has encountered a crash while running the a native plugin. + \\ + \\This indicates either a bug in the native plugin or in Bun. + \\ + \\ + , .{}) catch std.posix.abort(); } else { if (Output.enable_ansi_colors) { writer.writeAll(Output.prettyFmt("", true)) catch std.posix.abort(); @@ -308,7 +323,16 @@ pub fn crashHandler( } else { writer.writeAll(Output.prettyFmt(": ", true)) catch std.posix.abort(); } - if (reason == .out_of_memory) { + if (inside_native_plugin) { + writer.print( + \\Bun has encountered a crash while running a native plugin. + \\ + \\To send a redacted crash report to Bun's team, + \\please file a GitHub issue using the link below: + \\ + \\ + , .{}) catch std.posix.abort(); + } else if (reason == .out_of_memory) { writer.writeAll( \\Bun has ran out of memory. \\ @@ -737,6 +761,7 @@ fn handleSegfaultPosix(sig: i32, info: *const std.posix.siginfo_t, _: ?*const an std.posix.SIG.ILL => .{ .illegal_instruction = addr }, std.posix.SIG.BUS => .{ .bus_error = addr }, std.posix.SIG.FPE => .{ .floating_point_error = addr }, + std.posix.SIG.KILL => .{ .segmentation_fault = addr }, // we do not register this handler for other signals else => unreachable,