diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index 28c9d5ef01..3d95d5905a 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -122,14 +122,27 @@ pub const JSBundler = struct { return globalThis.throwInvalidArguments("Expected plugin to be an object", .{}); } - if (try plugin.getOptional(globalThis, "name", ZigString.Slice)) |slice| { - defer slice.deinit(); - if (slice.len == 0) { + const name = name: { + var raw_val = try plugin.get(globalThis, "name") orelse { + return globalThis.throwInvalidArguments("Expected plugin to have a name", .{}); + }; + if (!raw_val.isString()) { + return globalThis.throwInvalidArguments("Expected plugin to have a name", .{}); + } + const bunstr: bun.String = try raw_val.toBunString2(globalThis); + if (bunstr.length() == 0) { + bunstr.deref(); return globalThis.throwInvalidArguments("Expected plugin to have a non-empty name", .{}); } - } else { - return globalThis.throwInvalidArguments("Expected plugin to have a name", .{}); - } + if (bunstr.tag != .WTFStringImpl) { + var new_bun_str = raw_val.toBunString(globalThis); + new_bun_str.toWTF(); + break :name new_bun_str; + } + break :name bunstr; + }; + defer name.deref(); + bun.debugAssert(name.tag == .WTFStringImpl); const function = try plugin.getFunction(globalThis, "setup") orelse { return globalThis.throwInvalidArguments("Expected plugin to have a setup() function", .{}); @@ -138,6 +151,7 @@ pub const JSBundler = struct { var bun_plugins: *Plugin = plugins.* orelse brk: { plugins.* = Plugin.create( globalThis, + name.value.WTFStringImpl, switch (this.target) { .bun, .bun_macro => JSC.JSGlobalObject.BunPluginTarget.bun, .node => JSC.JSGlobalObject.BunPluginTarget.node, @@ -924,10 +938,10 @@ pub const JSBundler = struct { }; pub const Plugin = opaque { - extern fn JSBundlerPlugin__create(*JSC.JSGlobalObject, JSC.JSGlobalObject.BunPluginTarget) *Plugin; - pub fn create(globalObject: *JSC.JSGlobalObject, target: JSC.JSGlobalObject.BunPluginTarget) *Plugin { + extern fn JSBundlerPlugin__create(*JSC.JSGlobalObject, bun.WTF.StringImpl, JSC.JSGlobalObject.BunPluginTarget) *Plugin; + pub fn create(globalObject: *JSC.JSGlobalObject, name: bun.WTF.StringImpl, target: JSC.JSGlobalObject.BunPluginTarget) *Plugin { JSC.markBinding(@src()); - const plugin = JSBundlerPlugin__create(globalObject, target); + const plugin = JSBundlerPlugin__create(globalObject, name, target); JSC.JSValue.fromCell(plugin).protect(); return plugin; } diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp index e8941ddbea..3b52a591f8 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.cpp +++ b/src/bun.js/bindings/JSBundlerPlugin.cpp @@ -113,20 +113,20 @@ static const HashTableValue JSBundlerPluginHashTable[] = { { "generateDeferPromise"_s, static_cast(JSC::PropertyAttribute::Function | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBundlerPluginFunction_generateDeferPromise, 0 } }, }; - class JSBundlerPlugin final : public JSC::JSNonFinalObject { public: using Base = JSC::JSNonFinalObject; static JSBundlerPlugin* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, + WTF::StringImpl* name, void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError = JSBundlerPlugin__addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync = JSBundlerPlugin__onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync = JSBundlerPlugin__onResolveAsync) { - JSBundlerPlugin* ptr = new (NotNull, JSC::allocateCell(vm)) JSBundlerPlugin(vm, globalObject, structure, config, target, + JSBundlerPlugin* ptr = new (NotNull, JSC::allocateCell(vm)) JSBundlerPlugin(vm, globalObject, structure, name, config, target, addError, onLoadAsync, onResolveAsync); @@ -161,10 +161,10 @@ public: WTF::HashMap onBeforeParseHandles; private: - JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target, + JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, WTF::StringImpl* name, void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync) : JSC::JSNonFinalObject(vm, structure) - , plugin(BundlerPlugin(config, target, addError, onLoadAsync, onResolveAsync)) + , plugin(BundlerPlugin(config, name, target, addError, onLoadAsync, onResolveAsync)) { } @@ -250,21 +250,20 @@ void BundlerPlugin::NativePluginList::append(JSC::VM& vm, JSC::RegExp* filter, S nsGroup->append(nativeFilterRegexp); } - + if (index == std::numeric_limits::max()) { - this->fileCallbacks.append(NativePluginCallback{callback, external}); + this->fileCallbacks.append(NativePluginCallback { callback, external }); } else { if (this->namespaceCallbacks.size() <= index) { this->namespaceCallbacks.grow(index + 1); } - this->namespaceCallbacks[index].append(NativePluginCallback{callback, external}); + this->namespaceCallbacks[index].append(NativePluginCallback { callback, external }); } } -extern "C" void CrashHandler__setInsideNativePlugin(bool value); +extern "C" void CrashHandler__setInsideNativePlugin(bool value, const char* plugin_name); - -int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult) +int BundlerPlugin::NativePluginList::call(JSC::VM& vm, BundlerPlugin* plugin, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult) { unsigned index = 0; const auto* group = this->group(namespaceStr->toWTFString(BunString::ZeroCopy), index); @@ -281,6 +280,11 @@ int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void int count = 0; constexpr bool usesPatternContextBuffer = false; const WTF::String& path = pathString->toWTFString(BunString::ZeroCopy); + if (!plugin->name_c.has_value()) { + auto plugin_name_c = plugin->name->utf8(); + plugin->name_c = std::make_optional(plugin_name_c); + } + CString& plugin_name = plugin->name_c.value(); for (size_t i = 0, total = callbacks.size(); i < total && *shouldContinue; ++i) { Yarr::MatchingContextHolder regExpContext(vm, usesPatternContextBuffer, nullptr, Yarr::MatchFrom::CompilerThread); @@ -289,13 +293,13 @@ int BundlerPlugin::NativePluginList::call(JSC::VM& vm, int* shouldContinue, void if (group->at(i).first.match(path) > -1) { Bun::NapiExternal* external = callbacks[i].external; if (external) { - ((OnBeforeParseArguments*) (onBeforeParseArgs))->external = external->value(); + ((OnBeforeParseArguments*)(onBeforeParseArgs))->external = external->value(); } JSBundlerPluginNativeOnBeforeParseCallback callback = callbacks[i].callback; - CrashHandler__setInsideNativePlugin(true); + CrashHandler__setInsideNativePlugin(true, plugin_name.data()); callback(onBeforeParseArgs, onBeforeParseResult); - CrashHandler__setInsideNativePlugin(false); + CrashHandler__setInsideNativePlugin(false, nullptr); count++; } @@ -352,7 +356,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onBeforeParse, (JSC::JSGlobalOb Bun::NapiModuleMeta* meta = (Bun::NapiModuleMeta*)napi_external->value(); void* dlopen_handle = meta->dlopenHandle; - #if OS(WINDOWS) +#if OS(WINDOWS) BunString onbefore_parse_symbol_str = Bun::toString(on_before_parse_symbol); void* on_before_parse_symbol_ptr = GetProcAddress(&onbefore_parse_symbol_str); #else @@ -560,7 +564,7 @@ extern "C" void JSBundlerPlugin__matchOnResolve(JSC::JSGlobalObject* globalObjec } } -extern "C" Bun::JSBundlerPlugin* JSBundlerPlugin__create(Zig::GlobalObject* globalObject, BunPluginTarget target) +extern "C" Bun::JSBundlerPlugin* JSBundlerPlugin__create(Zig::GlobalObject* globalObject, WTF::StringImpl* name, BunPluginTarget target) { return JSBundlerPlugin::create( globalObject->vm(), @@ -570,6 +574,7 @@ extern "C" Bun::JSBundlerPlugin* JSBundlerPlugin__create(Zig::GlobalObject* glob globalObject->vm(), globalObject, globalObject->objectPrototype()), + name, nullptr, target); } @@ -634,7 +639,7 @@ extern "C" int JSBundlerPlugin__callOnBeforeParsePlugins( void* onBeforeParseResult, int* shouldContinue) { - return plugin->plugin.onBeforeParse.call(plugin->vm(), shouldContinue, bunContextPtr, namespaceStr, pathString, onBeforeParseArgs, onBeforeParseResult); + return plugin->plugin.onBeforeParse.call(plugin->vm(), &plugin->plugin, shouldContinue, bunContextPtr, namespaceStr, pathString, onBeforeParseArgs, onBeforeParseResult); } extern "C" int JSBundlerPlugin__hasOnBeforeParsePlugins(Bun::JSBundlerPlugin* plugin) diff --git a/src/bun.js/bindings/JSBundlerPlugin.h b/src/bun.js/bindings/JSBundlerPlugin.h index bfe76b8a0b..a3ea0c1dc8 100644 --- a/src/bun.js/bindings/JSBundlerPlugin.h +++ b/src/bun.js/bindings/JSBundlerPlugin.h @@ -10,7 +10,7 @@ typedef void (*JSBundlerPluginAddErrorCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); typedef void (*JSBundlerPluginOnLoadAsyncCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue); typedef void (*JSBundlerPluginOnResolveAsyncCallback)(void*, void*, JSC::EncodedJSValue, JSC::EncodedJSValue, JSC::EncodedJSValue); -typedef void (*JSBundlerPluginNativeOnBeforeParseCallback)( void*, void*); +typedef void (*JSBundlerPluginNativeOnBeforeParseCallback)(void*, void*); namespace Bun { @@ -46,7 +46,7 @@ public: void append(JSC::VM& vm, JSC::RegExp* filter, String& namespaceString, unsigned& index); }; - #include "../../../packages/bun-native-bundler-plugin-api/bundler_plugin.h" +#include "../../../packages/bun-native-bundler-plugin-api/bundler_plugin.h" /// In native plugins, the regular expression could be called concurrently on multiple threads. /// Therefore, we need a mutex to synchronize access. @@ -69,7 +69,7 @@ public: PerNamespaceCallbackList fileCallbacks = {}; Vector namespaceCallbacks = {}; - int call(JSC::VM& vm, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult); + int call(JSC::VM& vm, BundlerPlugin* plugin, int* shouldContinue, void* bunContextPtr, const BunString* namespaceStr, const BunString* pathString, void* onBeforeParseArgs, void* onBeforeParseResult); void append(JSC::VM& vm, JSC::RegExp* filter, String& namespaceString, JSBundlerPluginNativeOnBeforeParseCallback callback, NapiExternal* external); Vector* group(const String& namespaceStr, unsigned& index) @@ -95,15 +95,18 @@ public: bool anyMatchesCrossThread(JSC::VM&, const BunString* namespaceStr, const BunString* path, bool isOnLoad); void tombstone() { tombstoned = true; } - BundlerPlugin(void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync) + BundlerPlugin(void* config, WTF::StringImpl* name, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync) : addError(addError) , onLoadAsync(onLoadAsync) , onResolveAsync(onResolveAsync) { + this->name = name; this->target = target; this->config = config; } + WTF::StringImpl* name; + std::optional name_c = {}; NamespaceList onLoad = {}; NamespaceList onResolve = {}; NativePluginList onBeforeParse = {}; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index 6030e138b1..8c4e468377 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -51,9 +51,11 @@ var panic_mutex = std.Thread.Mutex{}; threadlocal var panic_stage: usize = 0; threadlocal var inside_native_plugin: bool = false; +threadlocal var inside_native_plugin_str: ?[*:0]const u8 = null; -export fn CrashHandler__setInsideNativePlugin(value: bool) callconv(.C) void { +export fn CrashHandler__setInsideNativePlugin(value: bool, name: ?[*:0]const u8) callconv(.C) void { inside_native_plugin = value; + inside_native_plugin_str = name; } /// This can be set by various parts of the codebase to indicate a broader @@ -233,14 +235,17 @@ 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(); + if (inside_native_plugin) { + const native_plugin_name = inside_native_plugin_str orelse ""; + writer.print( + \\ + \\Bun has encountered a crash while running the "{s}" native plugin. + \\ + \\This indicates either a bug in the native plugin or in Bun. + \\ + \\ + , .{native_plugin_name}) catch std.posix.abort(); + } } else { if (Output.enable_ansi_colors) { writer.writeAll(Output.prettyFmt("", true)) catch std.posix.abort(); @@ -324,14 +329,15 @@ pub fn crashHandler( writer.writeAll(Output.prettyFmt(": ", true)) catch std.posix.abort(); } if (inside_native_plugin) { + const native_plugin_name = inside_native_plugin_str orelse ""; writer.print( - \\Bun has encountered a crash while running a native plugin. + \\Bun has encountered a crash while running the "{s}" 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(); + , .{native_plugin_name}) catch std.posix.abort(); } else if (reason == .out_of_memory) { writer.writeAll( \\Bun has ran out of memory.