diff --git a/CMakeLists.txt b/CMakeLists.txt index cfc84d8f90..1d1248557a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -704,6 +704,32 @@ add_custom_command( ) list(APPEND BUN_RAW_SOURCES "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.cpp") +if(NOT NO_CODEGEN) + # --- ErrorCode Generator --- + file(GLOB NODE_ERRORS_TS ${CONFIGURE_DEPENDS} + "${BUN_SRC}/bun.js/bindings/ErrorCode.ts" + ) + add_custom_command( + OUTPUT "${BUN_WORKDIR}/codegen/ErrorCode+List.h" "${BUN_WORKDIR}/codegen/ErrorCode+Data.h" "${BUN_WORKDIR}/codegen/ErrorCode.zig" + COMMAND ${BUN_EXECUTABLE} run "${BUN_CODEGEN_SRC}/generate-node-errors.ts" "${BUN_WORKDIR}/codegen" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + MAIN_DEPENDENCY "${BUN_CODEGEN_SRC}/generate-node-errors.ts" + DEPENDS ${NODE_ERRORS_TS} + VERBATIM + COMMENT "Generating ErrorCode.zig" + ) + + # This needs something to force it to be regenerated + WEBKIT_ADD_SOURCE_DEPENDENCIES( + "${BUN_SRC}/bun.js/bindings/ErrorCode.cpp" + "${BUN_WORKDIR}/codegen/ErrorCode+List.h" + ) + WEBKIT_ADD_SOURCE_DEPENDENCIES( + "${BUN_SRC}/bun.js/bindings/ErrorCode.h" + "${BUN_WORKDIR}/codegen/ErrorCode+Data.h" + ) +endif() + # --- JSSink Generator --- add_custom_command( OUTPUT "${BUN_WORKDIR}/codegen/JSSink.cpp" @@ -932,6 +958,7 @@ if(NOT BUN_LINK_ONLY AND NOT BUN_CPP_ONLY) "${CMAKE_CURRENT_SOURCE_DIR}/build.zig" "${ZIG_FILES}" "${BUN_WORKDIR}/codegen/ZigGeneratedClasses.zig" + "${BUN_WORKDIR}/codegen/ErrorCode.zig" "${BUN_WORKDIR}/codegen/ResolvedSourceTag.zig" "${BUN_IDENTIFIER_CACHE_OUT}" "${BUN_SRC}/api/schema.zig" diff --git a/build.zig b/build.zig index cdf321c248..f545187ddc 100644 --- a/build.zig +++ b/build.zig @@ -453,6 +453,12 @@ fn addInternalPackages(b: *Build, obj: *Compile, opts: *BunBuildOptions) void { .root_source_file = .{ .cwd_relative = resolved_source_tag_path }, }); + const error_code_path = b.pathJoin(&.{ opts.generated_code_dir, "ErrorCode.zig" }); + validateGeneratedPath(error_code_path); + obj.root_module.addAnonymousImport("ErrorCode", .{ + .root_source_file = .{ .cwd_relative = error_code_path }, + }); + if (os == .windows) { obj.root_module.addAnonymousImport("bun_shim_impl.exe", .{ .root_source_file = opts.windowsShim(b).exe.getEmittedBin(), diff --git a/packages/bun-internal-test/src/banned.json b/packages/bun-internal-test/src/banned.json index 0340357dfa..8030c0409e 100644 --- a/packages/bun-internal-test/src/banned.json +++ b/packages/bun-internal-test/src/banned.json @@ -13,5 +13,6 @@ "std.StringArrayHashMap(": "bun.StringArrayHashMap has a faster `eql`", "std.StringHashMapUnmanaged(": "bun.StringHashMapUnmanaged has a faster `eql`", "std.StringHashMap(": "bun.StringHashMaphas a faster `eql`", + "std.enums.tagName(": "Use bun.tagName instead", "": "" } diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index 1389e021cf..85d283d9e8 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -64,17 +64,45 @@ pub const StandaloneModuleGraph = struct { pub const CompiledModuleGraphFile = struct { name: Schema.StringPointer = .{}, - loader: bun.options.Loader = .file, contents: Schema.StringPointer = .{}, sourcemap: Schema.StringPointer = .{}, + encoding: Encoding = .latin1, + loader: bun.options.Loader = .file, + }; + + pub const Encoding = enum(u8) { + binary = 0, + + latin1 = 1, + + // Not used yet. + utf8 = 2, }; pub const File = struct { name: []const u8 = "", loader: bun.options.Loader, - contents: []const u8 = "", + contents: [:0]const u8 = "", sourcemap: LazySourceMap, cached_blob: ?*bun.JSC.WebCore.Blob = null, + encoding: Encoding = .binary, + wtf_string: bun.String = bun.String.empty, + + pub fn toWTFString(this: *File) bun.String { + if (this.wtf_string.isEmpty()) { + switch (this.encoding) { + .binary, .utf8 => { + this.wtf_string = bun.String.createUTF8(this.contents); + }, + .latin1 => { + this.wtf_string = bun.String.createStaticExternal(this.contents, true); + }, + } + } + + // We don't want this to free. + return this.wtf_string.dupeRef(); + } pub fn blob(this: *File, globalObject: *bun.JSC.JSGlobalObject) *bun.JSC.WebCore.Blob { if (this.cached_blob == null) { @@ -147,11 +175,11 @@ pub const StandaloneModuleGraph = struct { try modules.ensureTotalCapacity(modules_list.len); for (modules_list) |module| { modules.putAssumeCapacity( - sliceTo(raw_bytes, module.name), + sliceToZ(raw_bytes, module.name), File{ - .name = sliceTo(raw_bytes, module.name), + .name = sliceToZ(raw_bytes, module.name), .loader = module.loader, - .contents = sliceTo(raw_bytes, module.contents), + .contents = sliceToZ(raw_bytes, module.contents), .sourcemap = LazySourceMap{ .compressed = sliceTo(raw_bytes, module.sourcemap), }, @@ -172,6 +200,12 @@ pub const StandaloneModuleGraph = struct { return bytes[ptr.offset..][0..ptr.length]; } + fn sliceToZ(bytes: []const u8, ptr: bun.StringPointer) [:0]const u8 { + if (ptr.length == 0) return ""; + + return bytes[ptr.offset..][0..ptr.length :0]; + } + pub fn toBytes(allocator: std.mem.Allocator, prefix: []const u8, output_files: []const bun.options.OutputFile) ![]u8 { var serialize_trace = bun.tracy.traceNamed(@src(), "StandaloneModuleGraph.serialize"); defer serialize_trace.end(); @@ -179,8 +213,8 @@ pub const StandaloneModuleGraph = struct { var string_builder = bun.StringBuilder{}; var module_count: usize = 0; for (output_files, 0..) |output_file, i| { - string_builder.count(output_file.dest_path); - string_builder.count(prefix); + string_builder.countZ(output_file.dest_path); + string_builder.countZ(prefix); if (output_file.value == .buffer) { if (output_file.output_kind == .sourcemap) { string_builder.cap += bun.zstd.compressBound(output_file.value.buffer.bytes.len); @@ -191,7 +225,7 @@ pub const StandaloneModuleGraph = struct { } } - string_builder.count(output_file.value.buffer.bytes); + string_builder.countZ(output_file.value.buffer.bytes); module_count += 1; } } @@ -224,12 +258,16 @@ pub const StandaloneModuleGraph = struct { const dest_path = bun.strings.removeLeadingDotSlash(output_file.dest_path); var module = CompiledModuleGraphFile{ - .name = string_builder.fmtAppendCount("{s}{s}", .{ + .name = string_builder.fmtAppendCountZ("{s}{s}", .{ prefix, dest_path, }), .loader = output_file.loader, - .contents = string_builder.appendCount(output_file.value.buffer.bytes), + .contents = string_builder.appendCountZ(output_file.value.buffer.bytes), + .encoding = switch (output_file.loader) { + .js, .jsx, .ts, .tsx => .latin1, + else => .binary, + }, }; if (output_file.source_map_index != std.math.maxInt(u32)) { const remaining_slice = string_builder.allocatedSlice()[string_builder.len..]; @@ -242,7 +280,7 @@ pub const StandaloneModuleGraph = struct { modules.appendAssumeCapacity(module); } - var offsets = Offsets{ + const offsets = Offsets{ .entry_point_id = @as(u32, @truncate(entry_point_id.?)), .modules_ptr = string_builder.appendCount(std.mem.sliceAsBytes(modules.items)), .byte_count = string_builder.len, @@ -251,7 +289,16 @@ pub const StandaloneModuleGraph = struct { _ = string_builder.append(std.mem.asBytes(&offsets)); _ = string_builder.append(trailer); - return string_builder.ptr.?[0..string_builder.len]; + const output_bytes = string_builder.ptr.?[0..string_builder.len]; + + if (comptime Environment.isDebug) { + // An expensive sanity check: + var graph = try fromBytes(allocator, output_bytes, offsets); + defer graph.files.deinit(); + bun.assert_eql(graph.files.count(), modules.items.len); + } + + return output_bytes; } const page_size = if (Environment.isLinux and Environment.isAarch64) diff --git a/src/api/schema.zig b/src/api/schema.zig index 713aefc713..1207251c48 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -843,6 +843,10 @@ pub const Api = struct { try writer.writeInt(this.offset); try writer.writeInt(this.length); } + + pub fn slice(this: *const @This(), bytes: []const u8) []const u8 { + return bytes[this.offset .. this.offset + this.length]; + } }; pub const JavascriptBundledModule = struct { diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index 23f20dd25a..b93b5eab15 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -1567,7 +1567,7 @@ pub const Formatter = struct { formatter: *ConsoleObject.Formatter, writer: Writer, count: usize = 0, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); if (single_line and this.count > 0) { this.formatter.printComma(Writer, this.writer, enable_ansi_colors) catch unreachable; @@ -1631,7 +1631,7 @@ pub const Formatter = struct { formatter: *ConsoleObject.Formatter, writer: Writer, is_first: bool = true, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); if (single_line) { if (!this.is_first) { diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 7b0b70def7..d83bb196e0 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -1574,7 +1574,7 @@ pub const Crypto = struct { assert(output_slice.len == @as(usize, @intCast(this.pbkdf2.length))); const buffer_value = JSC.JSValue.createBuffer(globalThis, output_slice, bun.default_allocator); if (buffer_value == .zero) { - promise.reject(globalThis, globalThis.createTypeErrorInstance("Failed to create buffer", .{})); + promise.reject(globalThis, ZigString.init("Failed to create buffer").toErrorInstance(globalThis)); return; } @@ -1663,8 +1663,7 @@ pub const Crypto = struct { const slice = arguments[4].toSlice(globalThis, bun.default_allocator); defer slice.deinit(); const name = slice.slice(); - const err = globalThis.createTypeErrorInstanceWithCode(.ERR_CRYPTO_INVALID_DIGEST, "Unsupported algorithm \"{s}\"", .{name}); - globalThis.throwValue(err); + globalThis.ERR_CRYPTO_INVALID_DIGEST("Unsupported algorithm \"{s}\"", .{name}).throw(); } return null; }; @@ -2355,7 +2354,9 @@ pub const Crypto = struct { } const password_to_hash = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[0], bun.default_allocator) catch { - globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2389,7 +2390,9 @@ pub const Crypto = struct { } var string_or_buffer = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[0]) orelse { - globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("hash", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2515,19 +2518,21 @@ pub const Crypto = struct { const algorithm_string = arguments[2].getZigString(globalObject); algorithm = PasswordObject.Algorithm.label.getWithEql(algorithm_string, JSC.ZigString.eqlComptime) orelse { - globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + } return JSC.JSValue.undefined; }; } const owned_password = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[0], bun.default_allocator) catch |err| { - if (err != error.JSError) globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + if (err != error.JSError and !globalObject.hasException()) globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); return JSC.JSValue.undefined; }; const owned_hash = JSC.Node.StringOrBuffer.fromJSToOwnedSlice(globalObject, arguments[1], bun.default_allocator) catch |err| { bun.default_allocator.free(owned_password); - if (err != error.JSError) globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + if (err != error.JSError and !globalObject.hasException()) globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); return JSC.JSValue.undefined; }; @@ -2568,19 +2573,25 @@ pub const Crypto = struct { const algorithm_string = arguments[2].getZigString(globalObject); algorithm = PasswordObject.Algorithm.label.getWithEql(algorithm_string, JSC.ZigString.eqlComptime) orelse { - globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "algorithm", unknown_password_algorithm_message); + } return JSC.JSValue.undefined; }; } var password = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[0]) orelse { - globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "password", "string or TypedArray"); + } return JSC.JSValue.undefined; }; var hash_ = JSC.Node.StringOrBuffer.fromJS(globalObject, bun.default_allocator, arguments[1]) orelse { password.deinit(); - globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + if (!globalObject.hasException()) { + globalObject.throwInvalidArgumentType("verify", "hash", "string or TypedArray"); + } return JSC.JSValue.undefined; }; @@ -2782,7 +2793,7 @@ pub const Crypto = struct { } const encoding = arguments.ptr[1]; const buffer = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValue(globalThis, globalThis.bunVM().allocator, input, encoding) orelse { - globalThis.throwInvalidArguments("expected blob, string or buffer", .{}); + if (!globalThis.hasException()) globalThis.throwInvalidArguments("expected blob, string or buffer", .{}); return JSC.JSValue.zero; }; defer buffer.deinit(); diff --git a/src/bun.js/api/bun/h2_frame_parser.zig b/src/bun.js/api/bun/h2_frame_parser.zig index 7aff206554..13c5d04d89 100644 --- a/src/bun.js/api/bun/h2_frame_parser.zig +++ b/src/bun.js/api/bun/h2_frame_parser.zig @@ -1994,13 +1994,13 @@ pub const H2FrameParser = struct { const name = name_slice.slice(); if (header_name.charAt(0) == ':') { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_PSEUDOHEADER", "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_PSEUDOHEADER, "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } var js_value = headers_arg.getTruthy(globalObject, name) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2010,20 +2010,20 @@ pub const H2FrameParser = struct { var value_iter = js_value.arrayIterator(globalObject); if (SingleValueHeaders.has(name) and value_iter.len > 1) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", "Header field \"{s}\" must only have a single value", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER, "Header field \"{s}\" must only have a single value", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } while (value_iter.next()) |item| { if (item.isEmptyOrUndefinedOrNull()) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } const value_str = item.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2043,7 +2043,7 @@ pub const H2FrameParser = struct { } } else { const value_str = js_value.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2121,7 +2121,8 @@ pub const H2FrameParser = struct { const payload = zig_str.slice(); this.sendData(stream_id, payload, close and !stream.waitForTrailers); } else { - globalObject.throw("Expected data to be an ArrayBuffer or a string", .{}); + if (!globalObject.hasException()) + globalObject.throw("Expected data to be an ArrayBuffer or a string", .{}); return .zero; } @@ -2199,7 +2200,7 @@ pub const H2FrameParser = struct { if (ignore_pseudo_headers == 1) continue; if (!ValidRequestPseudoHeaders.has(name)) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_PSEUDOHEADER", "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_PSEUDOHEADER, "\"{s}\" is an invalid pseudoheader or is used incorrectly", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } @@ -2208,7 +2209,7 @@ pub const H2FrameParser = struct { } var js_value = headers_arg.getTruthy(globalObject, name) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2219,20 +2220,20 @@ pub const H2FrameParser = struct { var value_iter = js_value.arrayIterator(globalObject); if (SingleValueHeaders.has(name) and value_iter.len > 1) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", "Header field \"{s}\" must only have a single value", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Header field \"{s}\" must only have a single value", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } while (value_iter.next()) |item| { if (item.isEmptyOrUndefinedOrNull()) { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; } const value_str = item.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; @@ -2256,7 +2257,7 @@ pub const H2FrameParser = struct { } else { log("single header {s}", .{name}); const value_str = js_value.toStringOrNull(globalObject) orelse { - const exception = JSC.toTypeErrorWithCode("ERR_HTTP2_INVALID_HEADER_VALUE", "Invalid value for header \"{s}\"", .{name}, globalObject); + const exception = JSC.toTypeError(.ERR_HTTP2_INVALID_HEADER_VALUE, "Invalid value for header \"{s}\"", .{name}, globalObject); globalObject.throwValue(exception); return .zero; }; diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 2fd9e07d35..70fe82f3e5 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1978,7 +1978,8 @@ fn NewSocket(comptime ssl: bool) type { }, }; } else { - globalObject.throw("Expected ArrayBufferView, a string, or a Blob", .{}); + if (!globalObject.hasException()) + globalObject.throw("Expected ArrayBufferView, a string, or a Blob", .{}); return .{ .fail = {} }; } } diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 963ea23db8..9839ef3ad7 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -326,7 +326,7 @@ pub const FFI = struct { break :brk std.DynLib.open(backup_name) catch { // Then, if that fails, report an error. const system_error = JSC.SystemError{ - .code = bun.String.createUTF8(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), + .code = bun.String.createUTF8(@tagName(.ERR_DLOPEN_FAILED)), .message = bun.String.createUTF8("Failed to open library. This is usually caused by a missing library or an invalid library path."), .syscall = bun.String.createUTF8("dlopen"), }; @@ -568,7 +568,7 @@ pub const FFI = struct { defer type_name.deinit(); abi_types.appendAssumeCapacity(ABIType.label.get(type_name.slice()) orelse { abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global); }); } } @@ -600,7 +600,7 @@ pub const FFI = struct { defer ret_slice.deinit(); return_type = ABIType.label.get(ret_slice.slice()) orelse { abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global); }; } @@ -648,7 +648,7 @@ pub const FFI = struct { const value = symbols_iter.value; if (value.isEmptyOrUndefinedOrNull()) { - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{any}\"", .{prop}, global); + return JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{any}\"", .{prop}, global); } var function: Function = .{}; diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig index 3dd5256230..ea59070a26 100644 --- a/src/bun.js/api/html_rewriter.zig +++ b/src/bun.js/api/html_rewriter.zig @@ -465,14 +465,14 @@ pub const HTMLRewriter = struct { return switch (buffering_error) { error.StreamAlreadyUsed => { var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_ALREADY_FINISHED))), + .code = bun.String.static("ERR_STREAM_ALREADY_FINISHED"), .message = bun.String.static("Stream already used, please create a new one"), }; return err.toErrorInstance(sink.global); }, else => { var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), + .code = bun.String.static("ERR_STREAM_CANNOT_PIPE"), .message = bun.String.static("Failed to pipe stream"), }; return err.toErrorInstance(sink.global); diff --git a/src/bun.js/api/js_brotli.zig b/src/bun.js/api/js_brotli.zig index 92eeb8a496..1464cee9f7 100644 --- a/src/bun.js/api/js_brotli.zig +++ b/src/bun.js/api/js_brotli.zig @@ -91,7 +91,7 @@ pub const BrotliEncoder = struct { if (!idx.isNumber()) break; const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); if (!was_set) { - globalThis.throwValue(globalThis.createErrorInstanceWithCode(.ERR_ZLIB_INITIALIZATION_FAILED, "Initialization failed", .{})); + globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw(); this.deinit(); return .zero; } @@ -409,7 +409,7 @@ pub const BrotliDecoder = struct { if (!idx.isNumber()) break; const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32()); if (!was_set) { - globalThis.throwValue(globalThis.createErrorInstanceWithCode(.ERR_ZLIB_INITIALIZATION_FAILED, "Initialization failed", .{})); + globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw(); this.deinit(); return .zero; } diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index c804f4ce53..46a6b2f704 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -1455,7 +1455,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn drainMicrotasks(this: *const RequestContext) void { if (this.isAsync()) return; - if(this.server) |server| server.vm.drainMicrotasks(); + if (this.server) |server| server.vm.drainMicrotasks(); } pub fn setAbortHandler(this: *RequestContext) void { @@ -1484,7 +1484,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp var class_name = value.getClassInfoName() orelse bun.String.empty; defer class_name.deref(); - if(ctx.server) |server| { + if (ctx.server) |server| { const globalThis: *JSC.JSGlobalObject = server.globalThis; Output.enableBuffering(); @@ -1516,7 +1516,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return; } - if(ctx.server == null) { + if (ctx.server == null) { ctx.renderMissingInvalidResponse(value); return; } @@ -1588,7 +1588,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.request_body = null; } - if(this.server) |server| { + if (this.server) |server| { this.server = null; server.request_pool_allocator.put(this); server.onRequestComplete(); @@ -1784,7 +1784,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } this.detachResponse(); this.endRequestStreamingAndDrain(); - this.deref(); + this.deref(); } /// Drain a partial response buffer @@ -1911,9 +1911,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.signal = null; defer signal.unref(); if (!signal.aborted()) { - const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalThis); - reason.ensureStillAlive(); - _ = signal.signal(reason); + signal.signal(globalThis, .ConnectionClosed); any_js_calls = true; } } @@ -1928,7 +1926,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (this.isDeadRequest()) { this.finalizeWithoutDeinit(); } else { - if(this.endRequestStreaming()) { + if (this.endRequestStreaming()) { any_js_calls = true; } @@ -1973,13 +1971,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.signal = null; defer signal.unref(); if (this.flags.aborted and !signal.aborted()) { - const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalThis); - reason.ensureStillAlive(); - _ = signal.signal(reason); + signal.signal(globalThis, .ConnectionClosed); } } - // Case 1: // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object // but we received nothing or the connection was aborted @@ -1989,7 +1984,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // Case 3: // Stream was not consumed and the connection was aborted or ended _ = this.endRequestStreaming(); - + if (this.byte_stream) |stream| { ctxLog("finalizeWithoutDeinit: stream != null", .{}); @@ -2306,9 +2301,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (comptime can_sendfile) { return this.renderSendFile(blob); } - if(this.server) |server| { - this.ref(); - this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, server.globalThis); + if (this.server) |server| { + this.ref(); + this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, server.globalThis); } } @@ -2320,7 +2315,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (result == .err) { - if(this.server) |server| { + if (this.server) |server| { this.runErrorHandler(result.err.toErrorInstance(server.globalThis)); } return; @@ -2475,7 +2470,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp streamLog("returned a promise", .{}); this.drainMicrotasks(); - switch (promise.status(globalThis.vm())) { .Pending => { streamLog("promise still Pending", .{}); @@ -2582,7 +2576,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp request_object.request_context.setRequest(req); assert(ctx.server != null); - request_object.ensureURL() catch { request_object.url = bun.String.empty; }; @@ -2613,15 +2606,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn endRequestStreamingAndDrain(this: *RequestContext) void { assert(this.server != null); - if(this.endRequestStreaming()) { + if (this.endRequestStreaming()) { this.server.?.vm.drainMicrotasks(); } } fn endRequestStreaming(this: *RequestContext) bool { assert(this.server != null); - // if we cannot, we have to reject pending promises - // first, we reject the request body promise - if (this.request_body) |body| { + // if we cannot, we have to reject pending promises + // first, we reject the request body promise + if (this.request_body) |body| { // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object // but we received nothing or the connection was aborted if (body.value == .Locked) { @@ -2634,7 +2627,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp fn detachResponse(this: *RequestContext) void { if (this.resp) |resp| { this.resp = null; - + if (this.flags.is_waiting_for_request_body) { this.flags.is_waiting_for_request_body = false; resp.clearOnData(); @@ -2812,8 +2805,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp resp.body.value = .{ .Used = {} }; } } - - if(req.isAbortedOrEnded()) { + + if (req.isAbortedOrEnded()) { return; } @@ -2878,7 +2871,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (comptime debug_mode) { - if(req.server) |server| { + if (req.server) |server| { if (!err.isEmptyOrUndefinedOrNull()) { var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator); defer exception_list.deinit(); @@ -2929,7 +2922,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (stream.isLocked(globalThis)) { streamLog("was locked but it shouldn't be", .{}); var err = JSC.SystemError{ - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), + .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE)), .message = bun.String.static("Stream already used, please create a new one"), }; stream.value.unprotect(); @@ -3144,7 +3137,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } fn finishRunningErrorHandler(this: *RequestContext, value: JSC.JSValue, status: u16) void { - if(this.server == null) return this.renderProductionError(status); + if (this.server == null) return this.renderProductionError(status); var vm: *JSC.VirtualMachine = this.server.?.vm; const globalThis = this.server.?.globalThis; if (comptime debug_mode) { @@ -3178,7 +3171,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp status: u16, ) void { JSC.markBinding(@src()); - if(this.server) |server| { + if (this.server) |server| { if (!server.config.onError.isEmpty() and !this.flags.has_called_error_handler) { this.flags.has_called_error_handler = true; const result = server.config.onError.call( @@ -3561,7 +3554,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } const max_request_body_preallocate_length = 1024 * 256; pub fn onStartBuffering(this: *RequestContext) void { - if(this.server) |server| { + if (this.server) |server| { ctxLog("onStartBuffering", .{}); // TODO: check if is someone calling onStartBuffering other than onStartBufferingCallback // if is not, this should be removed and only keep protect + setAbortHandler @@ -5316,7 +5309,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return if (request.request_context.getRemoteSocketInfo()) |info| JSSocketAddress__create( this.globalThis, - bun.String.static(info.ip).toJS(this.globalThis), + bun.String.init(info.ip).toJS(this.globalThis), info.port, info.is_ipv6, ) @@ -5507,7 +5500,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp // obviously invalid pointer marks it as used upgrader.upgrade_context = @as(*uws.uws_socket_context_s, @ptrFromInt(std.math.maxInt(usize))); // set the abort handler so we can receive onAbort to deref the context - upgrader.setAbortHandler(); + upgrader.setAbortHandler(); // after upgrading we should not use the response anymore upgrader.resp = null; request.upgrader = null; diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 9c3e789712..0a47be5325 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -187,17 +187,7 @@ pub fn createError( } } -pub fn throwTypeError( - code: JSC.Node.ErrorCode, - comptime fmt: string, - args: anytype, - ctx: js.JSContextRef, - exception: ExceptionValueRef, -) void { - exception.* = toTypeError(code, fmt, args, ctx).asObjectRef(); -} - -pub fn toTypeErrorWithCode( +fn toTypeErrorWithCode( code: []const u8, comptime fmt: string, args: anytype, @@ -219,31 +209,31 @@ pub fn toTypeErrorWithCode( } pub fn toTypeError( - code: JSC.Node.ErrorCode, - comptime fmt: string, + code: JSC.Error, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, ) JSC.JSValue { - return toTypeErrorWithCode(@tagName(code), fmt, args, ctx); + return code.fmt(ctx, fmt, args); } pub fn throwInvalidArguments( - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, exception: ExceptionValueRef, ) void { @setCold(true); - return throwTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, ctx, exception); + exception.* = JSC.Error.ERR_INVALID_ARG_TYPE.fmt(ctx, fmt, args).asObjectRef(); } pub fn toInvalidArguments( - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ctx: js.JSContextRef, ) JSC.JSValue { @setCold(true); - return toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, ctx); + return JSC.Error.ERR_INVALID_ARG_TYPE.fmt(ctx, fmt, args); } pub fn getAllocator(_: js.JSContextRef) std.mem.Allocator { diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 6d1b807b88..f011929334 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -291,6 +291,15 @@ extern "C" BunString BunString__fromBytes(const char* bytes, size_t length) return BunString__fromUTF8(bytes, length); } +extern "C" BunString BunString__createStaticExternal(const char* bytes, size_t length, bool isLatin1) +{ + Ref impl = isLatin1 ? WTF::ExternalStringImpl::createStatic({ reinterpret_cast(bytes), length }) : + + WTF::ExternalStringImpl::createStatic({ reinterpret_cast(bytes), length }); + + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + extern "C" BunString BunString__createExternal(const char* bytes, size_t length, bool isLatin1, void* ctx, void (*callback)(void* arg0, void* arg1, size_t arg2)) { Ref impl = isLatin1 ? WTF::ExternalStringImpl::create({ reinterpret_cast(bytes), length }, ctx, callback) : diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp new file mode 100644 index 0000000000..ecb2131d56 --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -0,0 +1,344 @@ + +#include "root.h" + +#include "DOMException.h" +#include "JavaScriptCore/Error.h" +#include "JavaScriptCore/ErrorType.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/WriteBarrier.h" +#include "root.h" +#include "headers-handwritten.h" +#include "BunClientData.h" +#include "helpers.h" +#include "JavaScriptCore/JSCJSValue.h" +#include "JavaScriptCore/ErrorInstance.h" +#include "JavaScriptCore/JSString.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/Symbol.h" +#include "wtf/text/ASCIILiteral.h" +#include "wtf/text/MakeString.h" +#include "wtf/text/WTFString.h" +#include "AbortSignal.h" +#include "JavaScriptCore/ErrorInstanceInlines.h" +#include "JavaScriptCore/JSInternalFieldObjectImplInlines.h" +#include "JSDOMException.h" + +#include "ErrorCode.h" + +extern "C" Zig::GlobalObject* Bun__getDefaultGlobalObject(); + +static JSC::JSObject* createErrorPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code, bool isDOMExceptionPrototype = false) +{ + JSC::JSObject* prototype; + + // Inherit from DOMException + // But preserve the error.stack property. + if (isDOMExceptionPrototype) { + auto* domGlobalObject = JSC::jsDynamicCast(globalObject); + if (UNLIKELY(!domGlobalObject)) { + domGlobalObject = Bun__getDefaultGlobalObject(); + } + // TODO: node:vm? + prototype = JSC::constructEmptyObject(globalObject, WebCore::JSDOMException::prototype(vm, *domGlobalObject)); + } else { + switch (type) { + case JSC::ErrorType::TypeError: + prototype = JSC::constructEmptyObject(globalObject, globalObject->m_typeErrorStructure.prototype(globalObject)); + break; + case JSC::ErrorType::RangeError: + prototype = JSC::constructEmptyObject(globalObject, globalObject->m_rangeErrorStructure.prototype(globalObject)); + break; + case JSC::ErrorType::Error: + prototype = JSC::constructEmptyObject(globalObject, globalObject->errorPrototype()); + break; + default: { + RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("TODO: Add support for more error types"); + break; + } + } + } + + prototype->putDirect(vm, vm.propertyNames->name, jsString(vm, String(name)), 0); + prototype->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), jsString(vm, String(code)), 0); + + return prototype; +} + +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value); +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3); +extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject); + +// clang-format on + +namespace Bun { + +using namespace JSC; + +#include "ErrorCode+Data.h" + +const ClassInfo ErrorCodeCache::s_info = { "ErrorCodeCache"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ErrorCodeCache) }; + +ErrorCodeCache::ErrorCodeCache(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +template +void ErrorCodeCache::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +DEFINE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE, ErrorCodeCache); + +Structure* ErrorCodeCache::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + return Structure::create(vm, globalObject, jsNull(), TypeInfo(InternalFieldTupleType, StructureFlags), info(), 0, 0); +} + +ErrorCodeCache* ErrorCodeCache::create(VM& vm, Structure* structure) +{ + ErrorCodeCache* object = new (NotNull, allocateCell(vm)) ErrorCodeCache(vm, structure); + object->finishCreation(vm); + return object; +} + +void ErrorCodeCache::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + + for (unsigned i = 0; i < NODE_ERROR_COUNT; i++) { + this->internalField(i).clear(); + } +} + +static ErrorCodeCache* errorCache(Zig::GlobalObject* globalObject) +{ + return static_cast(globalObject->nodeErrorCache()); +} + +// clang-format on +static Structure* createErrorStructure(JSC::VM& vm, JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code, bool isDOMExceptionPrototype = false) +{ + auto* prototype = createErrorPrototype(vm, globalObject, type, name, code, isDOMExceptionPrototype); + return ErrorInstance::createStructure(vm, globalObject, prototype); +} + +JSObject* ErrorCodeCache::createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options) +{ + auto* cache = errorCache(globalObject); + const auto& data = errors[static_cast(code)]; + if (!cache->internalField(static_cast(code))) { + auto* structure = createErrorStructure(vm, globalObject, data.type, data.name, data.code, code == ErrorCode::ABORT_ERR); + cache->internalField(static_cast(code)).set(vm, cache, structure); + } + + auto* structure = jsCast(cache->internalField(static_cast(code)).get()); + return JSC::ErrorInstance::create(globalObject, structure, message, options, nullptr, JSC::RuntimeType::TypeNothing, data.type, true); +} + +JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, const String& message) +{ + return errorCache(globalObject)->createError(vm, globalObject, code, jsString(vm, message), jsUndefined()); +} + +JSObject* createError(VM& vm, JSC::JSGlobalObject* globalObject, ErrorCode code, JSValue message) +{ + if (auto* zigGlobalObject = jsDynamicCast(globalObject)) + return createError(vm, zigGlobalObject, code, message, jsUndefined()); + + auto* structure = createErrorStructure(vm, globalObject, errors[static_cast(code)].type, errors[static_cast(code)].name, errors[static_cast(code)].code); + return JSC::ErrorInstance::create(globalObject, structure, message, jsUndefined(), nullptr, JSC::RuntimeType::TypeNothing, errors[static_cast(code)].type, true); +} + +JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options) +{ + return errorCache(globalObject)->createError(vm, globalObject, code, message, options); +} + +JSObject* createError(JSC::JSGlobalObject* globalObject, ErrorCode code, const String& message) +{ + auto& vm = globalObject->vm(); + return createError(vm, globalObject, code, jsString(vm, message)); +} + +JSObject* createError(Zig::JSGlobalObject* globalObject, ErrorCode code, JSC::JSValue message) +{ + auto& vm = globalObject->vm(); + return createError(vm, globalObject, code, message); +} + +WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) +{ + ASSERT(!arg.isEmpty()); + if (!arg.isCell()) + return arg.toString(globalObject)->getString(globalObject); + + auto cell = arg.asCell(); + auto jstype = cell->type(); + + if (jstype == JSC::JSType::StringType) { + return cell->toStringInline(globalObject)->getString(globalObject); + } + if (jstype == JSC::JSType::SymbolType) { + auto symbol = jsCast(cell); + auto result = symbol->tryGetDescriptiveString(); + if (result.has_value()) + return result.value(); + } + return arg.toString(globalObject)->getString(globalObject); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto argCount = callFrame->argumentCount(); + if (argCount < 3) { + JSC::throwTypeError(globalObject, scope, "requires 3 arguments"_s); + return {}; + } + auto arg_name = callFrame->argument(0); + auto expected_type = callFrame->argument(1); + auto actual_value = callFrame->argument(2); + return Bun__ERR_INVALID_ARG_TYPE(globalObject, JSValue::encode(arg_name), JSValue::encode(expected_type), JSValue::encode(actual_value)); +} +extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto arg_name = JSValue::decode(val_arg_name).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto expected_type = JSValue::decode(val_expected_type).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto actual_value = JSValueToStringSafe(globalObject, JSValue::decode(val_actual_value)); + RETURN_IF_EXCEPTION(scope, {}); + + auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); + return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto argCount = callFrame->argumentCount(); + if (argCount < 3) { + JSC::throwTypeError(globalObject, scope, "requires 3 arguments"_s); + return {}; + } + + auto arg_name = callFrame->argument(0).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto range = callFrame->argument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto input = callFrame->argument(2).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto message = makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received "_s, input); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_OUT_OF_RANGE, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_DISCONNECTED, "IPC channel is already disconnected"_s)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_SERVER_NOT_RUNNING, "Server is not running."_s)); +} + +extern "C" JSC::EncodedJSValue Bun__createErrorWithCode(JSC::JSGlobalObject* globalObject, ErrorCode code, BunString* message) +{ + return JSValue::encode(createError(globalObject, code, message->toWTFString(BunString::ZeroCopy))); +} + +extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (arg1 == 0) { + JSC::throwTypeError(globalObject, scope, "requires at least 1 argument"_s); + return {}; + } + + auto name1 = JSValue::decode(arg1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (arg2 == 0) { + // 1 arg name passed + auto message = makeString("The \""_s, name1, "\" argument must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name2 = JSValue::decode(arg2).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (arg3 == 0) { + // 2 arg names passed + auto message = makeString("The \""_s, name1, "\" and \""_s, name2, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); + } + + auto name3 = JSValue::decode(arg3).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + // 3 arg names passed + auto message = makeString("The \""_s, name1, "\", \""_s, name2, "\", and \""_s, name3, "\" arguments must be specified"_s); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_MISSING_ARGS, message)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return Bun__ERR_IPC_CHANNEL_CLOSED(globalObject); +} +extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_IPC_CHANNEL_CLOSED, "Channel closed."_s)); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) +{ + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_SOCKET_BAD_TYPE, "Bad socket type specified. Valid types are: udp4, udp6"_s)); +} + +} // namespace Bun + +JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CommonAbortReason abortReason) +{ + switch (abortReason) { + case CommonAbortReason::Timeout: { + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The operation timed out"_s); + } + case CommonAbortReason::UserAbort: { + // This message is a standardized error message. We cannot change it. + // https://webidl.spec.whatwg.org/#idl-DOMException:~:text=The%20operation%20was%20aborted. + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The operation was aborted."_s); + } + case CommonAbortReason::ConnectionClosed: { + return createError(globalObject, Bun::ErrorCode::ABORT_ERR, "The connection was closed"_s); + } + default: { + break; + } + } + + RELEASE_ASSERT_NOT_REACHED(); +} + +extern "C" JSC::EncodedJSValue WebCore__CommonAbortReason__toJS(JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason abortReason) +{ + return JSC::JSValue::encode(WebCore::toJS(globalObject, abortReason)); +} diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h new file mode 100644 index 0000000000..07c65ead77 --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.h @@ -0,0 +1,62 @@ +#pragma once + +#include "ZigGlobalObject.h" +#include "root.h" +#include +#include +#include "BunClientData.h" +#include "ErrorCode+List.h" + +namespace Bun { + +class ErrorCodeCache : public JSC::JSInternalFieldObjectImpl { +public: + using Base = JSInternalFieldObjectImpl; + using Field = ErrorCode; + + DECLARE_EXPORT_INFO; + + static size_t allocationSize(Checked inlineCapacity) + { + ASSERT_UNUSED(inlineCapacity, inlineCapacity == 0U); + return sizeof(ErrorCodeCache); + } + + template + static GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForErrorCodeCache.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForErrorCodeCache = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceForErrorCodeCache.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForErrorCodeCache = std::forward(space); }); + } + + static ErrorCodeCache* create(VM& vm, Structure* structure); + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject); + + JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options); + +private: + JS_EXPORT_PRIVATE ErrorCodeCache(VM&, Structure*); + DECLARE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE); + void finishCreation(VM&); +}; + +JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, const WTF::String& message); +JSC::JSObject* createError(JSC::JSGlobalObject* globalObject, ErrorCode code, const WTF::String& message); +JSC::JSObject* createError(Zig::GlobalObject* globalObject, ErrorCode code, JSC::JSValue message); +JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, ErrorCode code, JSValue message, JSValue options = jsUndefined()); +JSC::JSValue toJS(JSC::JSGlobalObject*, ErrorCode); + +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED); +JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE); + +} diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts new file mode 100644 index 0000000000..d4fdde8c30 --- /dev/null +++ b/src/bun.js/bindings/ErrorCode.ts @@ -0,0 +1,40 @@ +type ErrorCodeMapping = Array< + [ + /** error.code */ + string, + /** Constructor **/ + typeof TypeError | typeof RangeError | typeof Error | typeof SyntaxError, + /** error.name */ + string, + ] +>; + +export default [ + ["ABORT_ERR", Error, "AbortError"], + ["ERR_CRYPTO_INVALID_DIGEST", TypeError, "TypeError"], + ["ERR_ENCODING_INVALID_ENCODED_DATA", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_HEADER_VALUE", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_PSEUDOHEADER", TypeError, "TypeError"], + ["ERR_HTTP2_INVALID_SINGLE_VALUE_HEADER", TypeError, "TypeError"], + ["ERR_INVALID_ARG_TYPE", TypeError, "TypeError"], + ["ERR_INVALID_ARG_VALUE", TypeError, "TypeError"], + ["ERR_INVALID_THIS", TypeError, "TypeError"], + ["ERR_IPC_CHANNEL_CLOSED", Error, "Error"], + ["ERR_IPC_DISCONNECTED", Error, "Error"], + ["ERR_MISSING_ARGS", TypeError, "TypeError"], + ["ERR_OUT_OF_RANGE", RangeError, "RangeError"], + ["ERR_PARSE_ARGS_INVALID_OPTION_VALUE", TypeError, "TypeError"], + ["ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL", TypeError, "TypeError"], + ["ERR_PARSE_ARGS_UNKNOWN_OPTION", TypeError, "TypeError"], + ["ERR_SERVER_NOT_RUNNING", Error, "Error"], + ["ERR_SOCKET_BAD_TYPE", TypeError, "TypeError"], + ["ERR_STREAM_ALREADY_FINISHED", TypeError, "TypeError"], + ["ERR_STREAM_CANNOT_PIPE", TypeError, "TypeError"], + ["ERR_STREAM_DESTROYED", TypeError, "TypeError"], + ["ERR_STREAM_NULL_VALUES", TypeError, "TypeError"], + ["ERR_STREAM_WRITE_AFTER_END", TypeError, "TypeError"], + ["ERR_ZLIB_INITIALIZATION_FAILED", TypeError, "TypeError"], + + // Bun-specific + ["ERR_FORMDATA_PARSE_ERROR", TypeError, "TypeError"], +] as ErrorCodeMapping; diff --git a/src/bun.js/bindings/NodeError.cpp b/src/bun.js/bindings/NodeError.cpp deleted file mode 100644 index 66e46f8ab1..0000000000 --- a/src/bun.js/bindings/NodeError.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include "root.h" -#include "headers-handwritten.h" -#include "BunClientData.h" -#include "helpers.h" -#include "JavaScriptCore/JSCJSValue.h" -#include "JavaScriptCore/ErrorInstance.h" -#include "JavaScriptCore/ExceptionScope.h" -#include "JavaScriptCore/JSString.h" -#include "JavaScriptCore/JSType.h" -#include "JavaScriptCore/Symbol.h" -#include "wtf/text/ASCIILiteral.h" -#include "wtf/text/MakeString.h" -#include "wtf/text/WTFString.h" -#include - -JSC::EncodedJSValue JSC__JSValue__createTypeError(const ZigString* message, const ZigString* arg1, JSC::JSGlobalObject* globalObject); -JSC::EncodedJSValue JSC__JSValue__createRangeError(const ZigString* message, const ZigString* arg1, JSC::JSGlobalObject* globalObject); - -extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value); -extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3); -extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject); - -namespace Bun { - -using namespace JSC; - -WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) -{ - ASSERT(!arg.isEmpty()); - if (!arg.isCell()) - return arg.toString(globalObject)->getString(globalObject); - - auto cell = arg.asCell(); - auto jstype = cell->type(); - - if (jstype == JSC::JSType::StringType) { - return cell->toStringInline(globalObject)->getString(globalObject); - } - if (jstype == JSC::JSType::SymbolType) { - auto symbol = jsCast(cell); - auto result = symbol->tryGetDescriptiveString(); - if (result.has_value()) - return result.value(); - } - return arg.toString(globalObject)->getString(globalObject); -} - -JSC::JSValue createErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code) -{ - JSC::VM& vm = globalObject->vm(); - - JSC::JSObject* result = JSC::createError(globalObject, message); - JSC::EnsureStillAliveScope ensureAlive(result); - auto typeError = JSC::JSValue(result).asCell()->getObject(); - - auto clientData = WebCore::clientData(vm); - typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0); - - return typeError; -} - -JSC::JSValue createTypeErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code) -{ - JSC::VM& vm = globalObject->vm(); - - JSC::JSObject* result = JSC::createTypeError(globalObject, message); - JSC::EnsureStillAliveScope ensureAlive(result); - auto typeError = JSC::JSValue(result).asCell()->getObject(); - - auto clientData = WebCore::clientData(vm); - typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0); - - return typeError; -} - -JSC::JSValue createRangeErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code) -{ - JSC::VM& vm = globalObject->vm(); - - JSC::JSObject* result = JSC::createRangeError(globalObject, message); - JSC::EnsureStillAliveScope ensureAlive(result); - auto typeError = JSC::JSValue(result).asCell()->getObject(); - - auto clientData = WebCore::clientData(vm); - typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0); - - return typeError; -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto argCount = callFrame->argumentCount(); - if (argCount < 3) { - JSC::throwTypeError(globalObject, scope, "requires 3 arguments"_s); - return {}; - } - auto arg_name = callFrame->argument(0); - auto expected_type = callFrame->argument(1); - auto actual_value = callFrame->argument(2); - return Bun__ERR_INVALID_ARG_TYPE(globalObject, JSValue::encode(arg_name), JSValue::encode(expected_type), JSValue::encode(actual_value)); -} -extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto arg_name = JSValue::decode(val_arg_name).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto expected_type = JSValue::decode(val_expected_type).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto actual_value = JSValueToStringSafe(globalObject, JSValue::decode(val_actual_value)); - RETURN_IF_EXCEPTION(scope, {}); - - auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); - return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_INVALID_ARG_TYPE"_s)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - auto argCount = callFrame->argumentCount(); - if (argCount < 3) { - JSC::throwTypeError(globalObject, scope, "requires 3 arguments"_s); - return {}; - } - - auto arg_name = callFrame->argument(0).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto range = callFrame->argument(1).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto input = callFrame->argument(2).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto message = makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received "_s, input); - return JSC::JSValue::encode(createRangeErrorWithCode(globalObject, message, "ERR_OUT_OF_RANGE"_s)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) -{ - return JSC::JSValue::encode(createErrorWithCode(globalObject, "IPC channel is already disconnected"_s, "ERR_IPC_DISCONNECTED"_s)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) -{ - return JSC::JSValue::encode(createErrorWithCode(globalObject, "Server is not running."_s, "ERR_SERVER_NOT_RUNNING"_s)); -} - -extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3) -{ - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - - if (arg1 == 0) { - JSC::throwTypeError(globalObject, scope, "requires at least 1 argument"_s); - return {}; - } - - auto name1 = JSValue::decode(arg1).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - if (arg2 == 0) { - // 1 arg name passed - auto message = makeString("The \""_s, name1, "\" argument must be specified"_s); - return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s)); - } - - auto name2 = JSValue::decode(arg2).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - if (arg3 == 0) { - // 2 arg names passed - auto message = makeString("The \""_s, name1, "\" and \""_s, name2, "\" arguments must be specified"_s); - return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s)); - } - - auto name3 = JSValue::decode(arg3).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - // 3 arg names passed - auto message = makeString("The \""_s, name1, "\", \""_s, name2, "\", and \""_s, name3, "\" arguments must be specified"_s); - return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) -{ - return Bun__ERR_IPC_CHANNEL_CLOSED(globalObject); -} -extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject) -{ - return JSC::JSValue::encode(createErrorWithCode(globalObject, "Channel closed."_s, "ERR_IPC_CHANNEL_CLOSED"_s)); -} - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)) -{ - return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, "Bad socket type specified. Valid types are: udp4, udp6"_s, "ERR_SOCKET_BAD_TYPE"_s)); -} - -} diff --git a/src/bun.js/bindings/NodeError.h b/src/bun.js/bindings/NodeError.h deleted file mode 100644 index d1e72be077..0000000000 --- a/src/bun.js/bindings/NodeError.h +++ /dev/null @@ -1,12 +0,0 @@ -#include "root.h" - -namespace Bun { - -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)); -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)); -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)); -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*)); - -} diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 990dc1d3d7..99e79bf339 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -146,6 +146,7 @@ #include "UtilInspect.h" #include "Base64Helpers.h" #include "wtf/text/OrdinalNumber.h" +#include "ErrorCode.h" #if ENABLE(REMOTE_INSPECTOR) #include "JavaScriptCore/RemoteInspectorServer.h" @@ -2723,6 +2724,15 @@ void GlobalObject::finishCreation(VM& vm) init.set(Bun::createUtilInspectOptionsStructure(init.vm, init.owner)); }); + m_nodeErrorCache.initLater( + [](const Initializer& init) { + auto* structure = ErrorCodeCache::createStructure( + init.vm, + init.owner); + + init.set(ErrorCodeCache::create(init.vm, structure)); + }); + m_utilInspectStylizeColorFunction.initLater( [](const Initializer& init) { JSC::MarkedArgumentBuffer args; @@ -3581,6 +3591,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->mockModule.mockWithImplementationCleanupDataStructure.visit(visitor); thisObject->mockModule.withImplementationCleanupFunction.visit(visitor); + thisObject->m_nodeErrorCache.visit(visitor); + for (auto& barrier : thisObject->m_thenables) { visitor.append(barrier); } diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index a1bb74a492..8ce91abd3b 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -70,6 +70,8 @@ using DOMGuardedObjectSet = HashSet; class GlobalObject : public JSC::JSGlobalObject { using Base = JSC::JSGlobalObject; + // Move this to the front for better cache locality. + void* m_bunVM; public: static const JSC::ClassInfo s_info; @@ -402,6 +404,9 @@ public: // LazyProperty m_errorConstructorPrepareStackTraceInternalValue; + LazyProperty m_nodeErrorCache; + JSObject* nodeErrorCache() const { return m_nodeErrorCache.getInitializedOnMainThread(this); } + Structure* memoryFootprintStructure() { return m_memoryFootprintStructure.getInitializedOnMainThread(this); @@ -564,8 +569,6 @@ public: private: DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); - void* m_bunVM; - WebCore::SubtleCrypto* m_subtleCrypto = nullptr; WTF::Vector> m_aboutToBeNotifiedRejectedPromises; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 1edbb810be..6e40b48669 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1,4 +1,6 @@ + #include "root.h" + #include "JavaScriptCore/JSCast.h" #include "JavaScriptCore/JSType.h" #include "JavaScriptCore/NumberObject.h" @@ -4936,12 +4938,14 @@ void JSC__VM__notifyNeedDebuggerBreak(JSC__VM* arg0) { (*arg0).notifyNeedDebugge void JSC__VM__notifyNeedShellTimeoutCheck(JSC__VM* arg0) { (*arg0).notifyNeedShellTimeoutCheck(); } void JSC__VM__notifyNeedWatchdogCheck(JSC__VM* arg0) { (*arg0).notifyNeedWatchdogCheck(); } -void JSC__VM__throwError(JSC__VM* vm_, JSC__JSGlobalObject* arg1, JSC__JSValue value) +void JSC__VM__throwError(JSC__VM* vm_, JSC__JSGlobalObject* arg1, JSC__JSValue encodedValue) { JSC::VM& vm = *reinterpret_cast(vm_); - auto scope = DECLARE_THROW_SCOPE(vm); - JSC::JSObject* error = JSC::JSValue::decode(value).getObject(); + JSValue value = JSValue::decode(encodedValue); + scope.assertNoException(); // can't throw an exception when there's already one. + ASSERT(!value.isEmpty()); // can't throw an empty value. + JSC::JSObject* error = value.getObject(); JSC::Exception* exception = JSC::Exception::create(vm, error); scope.throwException(arg1, exception); } @@ -5484,11 +5488,14 @@ extern "C" JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, J return JSValue::encode(toJS>(*globalObject, *jsCast(globalObject), *abortSignal)); } -extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1) +extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC::JSGlobalObject* globalObject, uint8_t reason) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); - abortSignal->signalAbort(JSC::JSValue::decode(JSValue1)); + abortSignal->signalAbort( + globalObject, + static_cast(reason)); + ; return arg0; } @@ -5547,49 +5554,6 @@ extern "C" WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue value return reinterpret_cast(&object->wrapped()); } -static auto ABORT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("AbortError"); -extern "C" JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* message, const ZigString* arg1, - JSC__JSGlobalObject* globalObject) -{ - JSC::VM& vm = globalObject->vm(); - ZigString code = *arg1; - JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject(); - - error->putDirect( - vm, vm.propertyNames->name, - JSC::JSValue(JSC::jsOwnedString(vm, ABORT_ERROR_NAME)), - 0); - - if (code.len > 0) { - auto clientData = WebCore::clientData(vm); - JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject); - error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0); - } - - return JSC::JSValue::encode(error); -} - -static auto TIMEOUT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("TimeoutError"); -extern "C" JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* message, const ZigString* arg1, - JSC__JSGlobalObject* globalObject) -{ - JSC::VM& vm = globalObject->vm(); - ZigString code = *arg1; - JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject(); - - error->putDirect( - vm, vm.propertyNames->name, - JSC::JSValue(JSC::jsOwnedString(vm, TIMEOUT_ERROR_NAME)), - 0); - - if (code.len > 0) { - auto clientData = WebCore::clientData(vm); - JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject); - error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0); - } - - return JSC::JSValue::encode(error); -} CPP_DECL double JSC__JSValue__getUnixTimestamp(JSC__JSValue timeValue) { @@ -5738,7 +5702,7 @@ CPP_DECL void JSC__VM__setControlFlowProfiler(JSC__VM* vm, bool isEnabled) extern "C" EncodedJSValue JSC__createError(JSC::JSGlobalObject* globalObject, const BunString* str) { - return JSValue::encode(JSC::createError(globalObject, str->toWTFString())); + return JSValue::encode(JSC::createError(globalObject, str->toWTFString(BunString::ZeroCopy))); } extern "C" EncodedJSValue ExpectMatcherUtils__getSingleton(JSC::JSGlobalObject* globalObject_) diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index d164c0c390..3090f51f90 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -35,14 +35,10 @@ pub const JSObject = extern struct { }); } - const InitializeCallback = *const fn (ctx: ?*anyopaque, obj: [*c]JSObject, global: [*c]JSGlobalObject) callconv(.C) void; + const InitializeCallback = *const fn (ctx: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void; + extern fn JSC__JSObject__create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue; pub fn create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue { - return cppFn("create", .{ - global_object, - length, - ctx, - initializer, - }); + return JSC__JSObject__create(global_object, length, ctx, initializer); } extern fn JSC__createStructure(*JSC.JSGlobalObject, *JSC.JSCell, u32, names: [*]bun.String) JSC.JSValue; @@ -66,8 +62,8 @@ pub const JSObject = extern struct { pub fn Initializer(comptime Ctx: type, comptime func: fn (*Ctx, obj: *JSObject, global: *JSGlobalObject) void) type { return struct { - pub fn call(this: ?*anyopaque, obj: [*c]JSObject, global: [*c]JSGlobalObject) callconv(.C) void { - @call(bun.callmod_inline, func, .{ @as(*Ctx, @ptrCast(@alignCast(this.?))), obj.?, global.? }); + pub fn call(this: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void { + @call(bun.callmod_inline, func, .{ @as(*Ctx, @ptrCast(@alignCast(this))), obj, global }); } }; } @@ -114,7 +110,6 @@ pub const JSObject = extern struct { pub const Extern = [_][]const u8{ "putRecord", - "create", "getArrayLength", "getIndex", "putAtIndex", @@ -622,9 +617,10 @@ pub const ZigString = extern struct { return out; } - pub fn static(comptime slice_: []const u8) *const ZigString { + pub fn static(comptime slice_: [:0]const u8) *const ZigString { const Holder = struct { - pub const value = &ZigString{ ._unsafe_ptr_do_not_use = slice_.ptr, .len = slice_.len }; + const null_terminated_ascii_literal = slice_; + pub const value = &ZigString{ ._unsafe_ptr_do_not_use = null_terminated_ascii_literal.ptr, .len = null_terminated_ascii_literal.len }; }; return Holder.value; @@ -2003,6 +1999,18 @@ pub fn PromiseCallback(comptime Type: type, comptime CallbackFunction: fn (*Type }.callback; } +pub const CommonAbortReason = enum(u8) { + Timeout = 1, + UserAbort = 2, + ConnectionClosed = 3, + + pub fn toJS(this: CommonAbortReason, global: *JSGlobalObject) JSValue { + return WebCore__CommonAbortReason__toJS(global, this); + } + + extern fn WebCore__CommonAbortReason__toJS(*JSGlobalObject, CommonAbortReason) JSValue; +}; + pub const AbortSignal = extern opaque { pub const shim = Shimmer("WebCore", "AbortSignal", @This()); const cppFn = shim.cppFn; @@ -2042,12 +2050,15 @@ pub const AbortSignal = extern opaque { return cppFn("cleanNativeBindings", .{ this, ctx }); } + extern fn WebCore__AbortSignal__signal(*AbortSignal, *JSC.JSGlobalObject, CommonAbortReason) void; + pub fn signal( this: *AbortSignal, - reason: JSValue, - ) *AbortSignal { + globalObject: *JSC.JSGlobalObject, + reason: CommonAbortReason, + ) void { bun.Analytics.Features.abort_signal += 1; - return cppFn("signal", .{ this, reason }); + return WebCore__AbortSignal__signal(this, globalObject, reason); } /// This function is not threadsafe. aborted is a boolean, not an atomic! @@ -2095,15 +2106,7 @@ pub const AbortSignal = extern opaque { return WebCore__AbortSignal__new(global); } - pub fn createAbortError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue { - return cppFn("createAbortError", .{ message, code, global }); - } - - pub fn createTimeoutError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue { - return cppFn("createTimeoutError", .{ message, code, global }); - } - - pub const Extern = [_][]const u8{ "createAbortError", "createTimeoutError", "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" }; + pub const Extern = [_][]const u8{ "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" }; }; pub const JSPromise = extern struct { @@ -2756,14 +2759,7 @@ pub const JSFunction = extern struct { }; }; -pub const JSGlobalObject = extern struct { - pub const shim = Shimmer("JSC", "JSGlobalObject", @This()); - bytes: shim.Bytes, - - pub const include = "JavaScriptCore/JSGlobalObject.h"; - pub const name = "JSC::JSGlobalObject"; - pub const namespace = "JSC"; - +pub const JSGlobalObject = opaque { pub fn allocator(this: *JSGlobalObject) std.mem.Allocator { return this.bunVM().allocator; } @@ -2794,7 +2790,7 @@ pub const JSGlobalObject = extern struct { pub fn throwInvalidArguments( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const err = JSC.toInvalidArguments(fmt, args, this); @@ -2807,13 +2803,10 @@ pub const JSGlobalObject = extern struct { comptime field: []const u8, comptime typename: []const u8, ) JSC.JSValue { - return JSC.JSValue.createTypeError( - ZigString.static( - comptime std.fmt.comptimePrint("Expected {s} to be a {s} for '{s}'.", .{ field, typename, name_ }), - ), - ZigString.static("ERR_INVALID_ARG_TYPE"), - this, - ); + return this.ERR_INVALID_ARG_TYPE( + comptime std.fmt.comptimePrint("Expected {s} to be a {s} for '{s}'.", .{ field, typename, name_ }), + .{}, + ).toJS(); } pub fn toJS(this: *JSC.JSGlobalObject, value: anytype, comptime lifetime: JSC.Lifetime) JSC.JSValue { @@ -2837,7 +2830,8 @@ pub const JSGlobalObject = extern struct { ) JSValue { const ty_str = value.jsTypeString(this).toSlice(this, bun.default_allocator); defer ty_str.deinit(); - return this.throwValueRet(this.createTypeErrorInstanceWithCode(.ERR_INVALID_ARG_TYPE, "The \"{s}\" argument must be of type {s}. Received {s}", .{ field, typename, ty_str.slice() })); + this.ERR_INVALID_ARG_TYPE("The \"{s}\" argument must be of type {s}. Received {s}", .{ field, typename, ty_str.slice() }).throw(); + return .zero; } pub fn createNotEnoughArguments( @@ -2846,8 +2840,8 @@ pub const JSGlobalObject = extern struct { comptime expected: usize, got: usize, ) JSC.JSValue { - return JSC.toTypeErrorWithCode( - @tagName(JSC.Node.ErrorCode.ERR_MISSING_ARGS), + return JSC.toTypeError( + .ERR_MISSING_ARGS, "Not enough arguments to '" ++ name_ ++ "'. Expected {d}, got {d}.", .{ expected, got }, this, @@ -2863,11 +2857,12 @@ pub const JSGlobalObject = extern struct { this.throwValue(this.createNotEnoughArguments(name_, expected, got)); } + extern fn JSC__JSGlobalObject__reload(JSC__JSGlobalObject__ptr: *JSGlobalObject) void; pub fn reload(this: *JSC.JSGlobalObject) void { this.vm().drainMicrotasks(); this.vm().collectAsync(); - return cppFn("reload", .{this}); + JSC__JSGlobalObject__reload(this); } pub const BunPluginTarget = enum(u8) { @@ -2899,25 +2894,7 @@ pub const JSGlobalObject = extern struct { return result; } - pub fn createSyntheticModule_(this: *JSGlobalObject, export_names: [*]const ZigString, export_len: usize, value_ptrs: [*]const JSValue, values_len: usize) void { - shim.cppFn("createSyntheticModule_", .{ this, export_names, export_len, value_ptrs, values_len }); - } - - pub fn createSyntheticModule(this: *JSGlobalObject, comptime module: anytype) void { - const names = comptime std.meta.fieldNames(@TypeOf(module)); - var export_names: [names.len]ZigString = undefined; - var export_values: [names.len]JSValue = undefined; - inline for (comptime names, 0..) |export_name, i| { - export_names[i] = ZigString.init(export_name); - const function = @field(module, export_name).@"0"; - const len = @field(module, export_name).@"1"; - export_values[i] = JSC.NewFunction(this, &export_names[i], len, function, true); - } - - createSyntheticModule_(this, &export_names, names.len, &export_values, names.len); - } - - pub fn createErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2926,24 +2903,27 @@ pub const JSGlobalObject = extern struct { writer.print(fmt, args) catch // if an exception occurs in the middle of formatting the error message, it's better to just return the formatting string than an error about an error return ZigString.static(fmt).toErrorInstance(this); - var str = ZigString.fromUTF8(buf.toOwnedSliceLeaky()); + + // Ensure we clone it. + var str = ZigString.initUTF8(buf.toOwnedSliceLeaky()); + return str.toErrorInstance(this); } else { if (comptime strings.isAllASCII(fmt)) { - return ZigString.static(fmt).toErrorInstance(this); + return String.static(fmt).toErrorInstance(this); } else { return ZigString.initUTF8(fmt).toErrorInstance(this); } } } - pub fn createErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { + pub fn createErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: [:0]const u8, args: anytype) JSValue { var err = this.createErrorInstance(fmt, args); err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toJS(this)); return err; } - pub fn createTypeErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + fn createTypeErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2957,13 +2937,13 @@ pub const JSGlobalObject = extern struct { } } - pub fn createTypeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { + fn createTypeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: [:0]const u8, args: anytype) JSValue { var err = this.createTypeErrorInstance(fmt, args); err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toJS(this)); return err; } - pub fn createSyntaxErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createSyntaxErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2977,7 +2957,7 @@ pub const JSGlobalObject = extern struct { } } - pub fn createRangeErrorInstance(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createRangeErrorInstance(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { if (comptime std.meta.fieldNames(@TypeOf(args)).len > 0) { var stack_fallback = std.heap.stackFallback(1024 * 4, this.allocator()); var buf = bun.MutableString.init2048(stack_fallback.get()) catch unreachable; @@ -2991,22 +2971,20 @@ pub const JSGlobalObject = extern struct { } } - pub fn createRangeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: string, args: anytype) JSValue { + pub fn createRangeErrorInstanceWithCode(this: *JSGlobalObject, code: JSC.Node.ErrorCode, comptime fmt: [:0]const u8, args: anytype) JSValue { var err = this.createRangeErrorInstance(fmt, args); err.put(this, ZigString.static("code"), ZigString.init(@tagName(code)).toJS(this)); return err; } - pub fn createRangeError(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { + pub fn createRangeError(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { const err = createErrorInstance(this, fmt, args); err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_OUT_OF_RANGE)).toJS(this)); return err; } - pub fn createInvalidArgs(this: *JSGlobalObject, comptime fmt: string, args: anytype) JSValue { - const err = createErrorInstance(this, fmt, args); - err.put(this, ZigString.static("code"), ZigString.static(@tagName(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE)).toJS(this)); - return err; + pub fn createInvalidArgs(this: *JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) JSValue { + return JSC.Error.ERR_INVALID_ARG_TYPE.fmt(this, fmt, args); } pub fn createError( @@ -3024,7 +3002,7 @@ pub const JSGlobalObject = extern struct { pub fn throw( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const instance = this.createErrorInstance(fmt, args); @@ -3034,7 +3012,7 @@ pub const JSGlobalObject = extern struct { pub fn throwPretty( this: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) void { const instance = switch (Output.enable_ansi_colors) { @@ -3074,18 +3052,14 @@ pub const JSGlobalObject = extern struct { ); } + extern fn JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject__ptr: *JSGlobalObject, JSValue, JSValue, JSValue) void; pub fn queueMicrotaskJob( this: *JSGlobalObject, function: JSValue, first: JSValue, second: JSValue, ) void { - shim.cppFn("queueMicrotaskJob", .{ - this, - function, - first, - second, - }); + JSC__JSGlobalObject__queueMicrotaskJob(this, function, first, second); } pub fn throwValue( @@ -3107,7 +3081,7 @@ pub const JSGlobalObject = extern struct { pub fn throwError( this: *JSGlobalObject, err: anyerror, - comptime fmt: string, + comptime fmt: [:0]const u8, ) void { var str = ZigString.init(std.fmt.allocPrint(this.bunVM().allocator, "{s} " ++ fmt, .{@errorName(err)}) catch return); str.markUTF8(); @@ -3119,28 +3093,12 @@ pub const JSGlobalObject = extern struct { pub fn handleError( this: *JSGlobalObject, err: anyerror, - comptime fmt: string, + comptime fmt: [:0]const u8, ) JSValue { this.throwError(err, fmt); return JSValue.jsUndefined(); } - // pub fn createError(globalObject: *JSGlobalObject, error_type: ErrorType, message: *String) *JSObject { - // return cppFn("createError", .{ globalObject, error_type, message }); - // } - - // pub fn throwError( - // globalObject: *JSGlobalObject, - // err: *JSObject, - // ) *JSObject { - // return cppFn("throwError", .{ - // globalObject, - // err, - // }); - // } - - const cppFn = shim.cppFn; - pub fn ref(this: *JSGlobalObject) C_API.JSContextRef { return @as(C_API.JSContextRef, @ptrCast(this)); } @@ -3150,20 +3108,14 @@ pub const JSGlobalObject = extern struct { return this; } + extern fn JSC__JSGlobalObject__createAggregateError(*JSGlobalObject, [*]*anyopaque, u16, *const ZigString) JSValue; pub fn createAggregateError(globalObject: *JSGlobalObject, errors: [*]*anyopaque, errors_len: u16, message: *const ZigString) JSValue { - return cppFn("createAggregateError", .{ globalObject, errors, errors_len, message }); + return JSC__JSGlobalObject__createAggregateError(globalObject, errors, errors_len, message); } + extern fn JSC__JSGlobalObject__generateHeapSnapshot(*JSGlobalObject) JSValue; pub fn generateHeapSnapshot(this: *JSGlobalObject) JSValue { - return cppFn("generateHeapSnapshot", .{this}); - } - - pub fn putCachedObject(this: *JSGlobalObject, key: *const ZigString, value: JSValue) JSValue { - return cppFn("putCachedObject", .{ this, key, value }); - } - - pub fn getCachedObject(this: *JSGlobalObject, key: *const ZigString) JSValue { - return cppFn("getCachedObject", .{ this, key }); + return JSC__JSGlobalObject__generateHeapSnapshot(this); } extern fn JSGlobalObject__hasException(*JSGlobalObject) bool; @@ -3171,16 +3123,19 @@ pub const JSGlobalObject = extern struct { return JSGlobalObject__hasException(this); } + extern fn JSC__JSGlobalObject__vm(*JSGlobalObject) *VM; pub fn vm(this: *JSGlobalObject) *VM { - return cppFn("vm", .{this}); + return JSC__JSGlobalObject__vm(this); } + extern fn JSC__JSGlobalObject__deleteModuleRegistryEntry(*JSGlobalObject, *const ZigString) void; pub fn deleteModuleRegistryEntry(this: *JSGlobalObject, name_: *ZigString) void { - return cppFn("deleteModuleRegistryEntry", .{ this, name_ }); + return JSC__JSGlobalObject__deleteModuleRegistryEntry(this, name_); } + extern fn JSC__JSGlobalObject__bunVM(*JSGlobalObject) *anyopaque; fn bunVMUnsafe(this: *JSGlobalObject) *anyopaque { - return cppFn("bunVM", .{this}); + return JSC__JSGlobalObject__bunVM(this); } pub fn bunVM(this: *JSGlobalObject) *JSC.VirtualMachine { @@ -3201,12 +3156,9 @@ pub const JSGlobalObject = extern struct { return @as(*JSC.VirtualMachine, @ptrCast(@alignCast(this.bunVMUnsafe()))); } + extern fn JSC__JSGlobalObject__handleRejectedPromises(*JSGlobalObject) void; pub fn handleRejectedPromises(this: *JSGlobalObject) void { - return cppFn("handleRejectedPromises", .{this}); - } - - pub fn startRemoteInspector(this: *JSGlobalObject, host: [:0]const u8, port: u16) bool { - return cppFn("startRemoteInspector", .{ this, host, port }); + return JSC__JSGlobalObject__handleRejectedPromises(this); } extern fn ZigGlobalObject__readableStreamToArrayBuffer(*JSGlobalObject, JSValue) JSValue; @@ -3217,32 +3169,26 @@ pub const JSGlobalObject = extern struct { extern fn ZigGlobalObject__readableStreamToBlob(*JSGlobalObject, JSValue) JSValue; pub fn readableStreamToArrayBuffer(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToArrayBuffer(this, value); } pub fn readableStreamToBytes(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToBytes(this, value); } pub fn readableStreamToText(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToText(this, value); } pub fn readableStreamToJSON(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToJSON(this, value); } pub fn readableStreamToBlob(this: *JSGlobalObject, value: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToBlob(this, value); } pub fn readableStreamToFormData(this: *JSGlobalObject, value: JSValue, content_type: JSValue) JSValue { - if (comptime is_bindgen) unreachable; return ZigGlobalObject__readableStreamToFormData(this, value, content_type); } @@ -3250,24 +3196,7 @@ pub const JSGlobalObject = extern struct { if (bun.Environment.allow_assert) this.bunVM().assertOnJSThread(); } - pub const Extern = [_][]const u8{ - "reload", - "bunVM", - "putCachedObject", - "getCachedObject", - "createAggregateError", - - "deleteModuleRegistryEntry", - - "vm", - "generateHeapSnapshot", - "startRemoteInspector", - "handleRejectedPromises", - "createSyntheticModule_", - "queueMicrotaskJob", - // "createError", - // "throwError", - }; + pub usingnamespace @import("ErrorCode").JSGlobalObjectExtensions; }; pub const JSNativeFn = JSHostFunctionPtr; diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 75e74262d1..27d1e91655 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -1021,7 +1021,10 @@ pub export fn NodeModuleModule__findPath( const found = if (paths_maybe) |paths| found: { var iter = paths.iterator(global); while (iter.next()) |path| { - const cur_path = bun.String.tryFromJS(path, global) orelse continue; + const cur_path = bun.String.tryFromJS(path, global) orelse { + if (global.hasException()) return .zero; + continue; + }; defer cur_path.deref(); if (findPathInner(request_bun_str, cur_path, global)) |found| { diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 9484fb327d..c9d6940e95 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -231,11 +231,9 @@ CPP_DECL JSC__JSValue WebCore__AbortSignal__abortReason(WebCore__AbortSignal* ar CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__addListener(WebCore__AbortSignal* arg0, void* arg1, void(* ArgFn2)(void* arg0, JSC__JSValue JSValue1)); CPP_DECL void WebCore__AbortSignal__cleanNativeBindings(WebCore__AbortSignal* arg0, void* arg1); CPP_DECL JSC__JSValue WebCore__AbortSignal__create(JSC__JSGlobalObject* arg0); -CPP_DECL JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); -CPP_DECL JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue JSValue0); CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__ref(WebCore__AbortSignal* arg0); -CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1); +CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSGlobalObject*, uint8_t abortReason); CPP_DECL JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, JSC__JSGlobalObject* arg1); CPP_DECL void WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index 542f52b673..3593830bee 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -143,8 +143,6 @@ pub extern fn WebCore__AbortSignal__abortReason(arg0: ?*bindings.AbortSignal) JS pub extern fn WebCore__AbortSignal__addListener(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque, ArgFn2: ?*const fn (?*anyopaque, JSC__JSValue) callconv(.C) void) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__cleanNativeBindings(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque) void; pub extern fn WebCore__AbortSignal__create(arg0: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn WebCore__AbortSignal__createAbortError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue; -pub extern fn WebCore__AbortSignal__createTimeoutError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue; pub extern fn WebCore__AbortSignal__fromJS(JSValue0: JSC__JSValue) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__ref(arg0: ?*bindings.AbortSignal) ?*bindings.AbortSignal; pub extern fn WebCore__AbortSignal__signal(arg0: ?*bindings.AbortSignal, JSValue1: JSC__JSValue) ?*bindings.AbortSignal; diff --git a/src/bun.js/bindings/helpers.h b/src/bun.js/bindings/helpers.h index d403b7453a..95ea588532 100644 --- a/src/bun.js/bindings/helpers.h +++ b/src/bun.js/bindings/helpers.h @@ -1,6 +1,7 @@ #pragma once #include "root.h" +#include "wtf/text/ASCIILiteral.h" #include #include @@ -84,13 +85,13 @@ static const WTF::String toString(ZigString str) return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::ExternalStringImpl::create({ untag(str.ptr), str.len }, untagVoid(str.ptr), free_global_string)) : WTF::String(WTF::ExternalStringImpl::create( - { reinterpret_cast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)); + { reinterpret_cast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)); } return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::createWithoutCopying({ untag(str.ptr), str.len })) : WTF::String(WTF::StringImpl::createWithoutCopying( - { reinterpret_cast(untag(str.ptr)), str.len })); + { reinterpret_cast(untag(str.ptr)), str.len })); } static WTF::AtomString toAtomString(ZigString str) @@ -115,7 +116,7 @@ static const WTF::String toString(ZigString str, StringPointer ptr) return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::createWithoutCopying({ &untag(str.ptr)[ptr.off], ptr.len })) : WTF::String(WTF::StringImpl::createWithoutCopying( - { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); + { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); } static const WTF::String toStringCopy(ZigString str, StringPointer ptr) @@ -130,7 +131,7 @@ static const WTF::String toStringCopy(ZigString str, StringPointer ptr) return !isTaggedUTF16Ptr(str.ptr) ? WTF::String(WTF::StringImpl::create(std::span { &untag(str.ptr)[ptr.off], ptr.len })) : WTF::String(WTF::StringImpl::create( - std::span { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); + std::span { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); } static const WTF::String toStringCopy(ZigString str) @@ -284,13 +285,15 @@ static const WTF::String toStringStatic(ZigString str) return WTF::String(AtomStringImpl::add(std::span { reinterpret_cast(untag(str.ptr)), str.len })); } - return WTF::String(AtomStringImpl::add( - std::span { reinterpret_cast(untag(str.ptr)), str.len })); + auto* untagged = untag(str.ptr); + ASSERT(untagged[str.len] == 0); + ASCIILiteral ascii = ASCIILiteral::fromLiteralUnsafe(reinterpret_cast(untagged)); + return WTF::String(ascii); } static JSC::JSValue getErrorInstance(const ZigString* str, JSC__JSGlobalObject* globalObject) { - WTF::String message = toStringCopy(*str); + WTF::String message = toString(*str); if (UNLIKELY(message.isNull() && str->len > 0)) { // pending exception while creating an error. return JSC::JSValue(); diff --git a/src/bun.js/bindings/webcore/AbortSignal.cpp b/src/bun.js/bindings/webcore/AbortSignal.cpp index b0baaef44f..d26816c01e 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.cpp +++ b/src/bun.js/bindings/webcore/AbortSignal.cpp @@ -32,6 +32,7 @@ #include "Event.h" #include "EventNames.h" #include "JSDOMException.h" +#include "JavaScriptCore/JSCJSValue.h" #include "ScriptExecutionContext.h" #include "WebCoreOpaqueRoot.h" #include "wtf/DebugHeap.h" @@ -112,6 +113,20 @@ AbortSignal::AbortSignal(ScriptExecutionContext* context, Aborted aborted, JSC:: AbortSignal::~AbortSignal() = default; +JSValue AbortSignal::jsReason(JSC::JSGlobalObject& globalObject) +{ + JSValue existingValue = m_reason.getValue(jsUndefined()); + if (existingValue.isUndefined()) { + if (m_commonReason != CommonAbortReason::None) { + existingValue = toJS(&globalObject, m_commonReason); + m_commonReason = CommonAbortReason::None; + m_reason.setWeakly(existingValue); + } + } + + return existingValue; +} + void AbortSignal::addSourceSignal(AbortSignal& signal) { if (signal.isDependent()) { @@ -164,6 +179,16 @@ void AbortSignal::signalAbort(JSC::JSValue reason) dependentSignal->signalAbort(reason); } +void AbortSignal::signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason) +{ + // 1. If signal's aborted flag is set, then return. + if (m_aborted) + return; + + m_commonReason = reason; + signalAbort(toJS(globalObject, reason)); +} + void AbortSignal::cleanNativeBindings(void* ref) { auto callbacks = std::exchange(m_native_callbacks, {}); @@ -183,7 +208,7 @@ void AbortSignal::signalFollow(AbortSignal& signal) return; if (signal.aborted()) { - signalAbort(signal.reason().getValue()); + signalAbort(signal.jsReason(*scriptExecutionContext()->jsGlobalObject())); return; } @@ -203,7 +228,8 @@ void AbortSignal::eventListenersDidChange() uint32_t AbortSignal::addAbortAlgorithmToSignal(AbortSignal& signal, Ref&& algorithm) { if (signal.aborted()) { - algorithm->handleEvent(signal.m_reason.getValue()); + // TODO: Null check. + algorithm->handleEvent(signal.jsReason(*signal.scriptExecutionContext()->jsGlobalObject())); return 0; } return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable { diff --git a/src/bun.js/bindings/webcore/AbortSignal.h b/src/bun.js/bindings/webcore/AbortSignal.h index 17b8bd225e..37105bbbea 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.h +++ b/src/bun.js/bindings/webcore/AbortSignal.h @@ -30,6 +30,8 @@ #include "ContextDestructionObserver.h" #include "EventTarget.h" #include "JSValueInWrappedObject.h" +#include "JavaScriptCore/JSGlobalObject.h" +#include "ZigGlobalObject.h" #include "wtf/DebugHeap.h" #include "wtf/FastMalloc.h" #include @@ -46,6 +48,15 @@ class WebCoreOpaqueRoot; DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AbortSignal); +enum class CommonAbortReason : uint8_t { + None, + Timeout, + UserAbort, + ConnectionClosed, +}; + +JSC::JSValue toJS(JSC::JSGlobalObject*, CommonAbortReason); + class AbortSignal final : public RefCounted, public EventTargetWithInlineData, private ContextDestructionObserver { WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AbortSignal); @@ -61,11 +72,13 @@ public: static uint32_t addAbortAlgorithmToSignal(AbortSignal&, Ref&&); static void removeAbortAlgorithmFromSignal(AbortSignal&, uint32_t algorithmIdentifier); + void signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason); void signalAbort(JSC::JSValue reason); void signalFollow(AbortSignal&); bool aborted() const { return m_aborted; } const JSValueInWrappedObject& reason() const { return m_reason; } + JSValue jsReason(JSC::JSGlobalObject& globalObject); void cleanNativeBindings(void* ref); void addNativeCallback(NativeCallbackTuple callback) { m_native_callbacks.append(callback); } @@ -89,8 +102,10 @@ public: AbortSignalSet& sourceSignals() { return m_sourceSignals; } private: - enum class Aborted : bool { No, - Yes }; + enum class Aborted : bool { + No, + Yes + }; explicit AbortSignal(ScriptExecutionContext*, Aborted = Aborted::No, JSC::JSValue reason = JSC::jsUndefined()); void setHasActiveTimeoutTimer(bool hasActiveTimeoutTimer) { m_hasActiveTimeoutTimer = hasActiveTimeoutTimer; } @@ -112,6 +127,7 @@ private: AbortSignalSet m_sourceSignals; AbortSignalSet m_dependentSignals; JSValueInWrappedObject m_reason; + CommonAbortReason m_commonReason { CommonAbortReason::None }; Vector m_native_callbacks; uint32_t m_algorithmIdentifier { 0 }; bool m_aborted { false }; diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index d4dc018135..f6708c75b7 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -44,6 +44,7 @@ public: std::unique_ptr m_clientSubspaceForMockWithImplementationCleanupData; std::unique_ptr m_clientSubspaceForProcessObject; std::unique_ptr m_clientSubspaceForInternalModuleRegistry; + std::unique_ptr m_clientSubspaceForErrorCodeCache; std::unique_ptr m_clientSubspaceForBunInspectorConnection; std::unique_ptr m_clientSubspaceForJSNextTickQueue; std::unique_ptr m_clientSubspaceForNAPIFunction; diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index f2a25be521..e6450bb154 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -44,6 +44,7 @@ public: std::unique_ptr m_subspaceForMockWithImplementationCleanupData; std::unique_ptr m_subspaceForProcessObject; std::unique_ptr m_subspaceForInternalModuleRegistry; + std::unique_ptr m_subspaceForErrorCodeCache; std::unique_ptr m_subspaceForBunInspectorConnection; std::unique_ptr m_subspaceForJSNextTickQueue; std::unique_ptr m_subspaceForNAPIFunction; diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.cpp b/src/bun.js/bindings/webcore/JSAbortSignal.cpp index 5369d9b920..e2cd1d383d 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignal.cpp +++ b/src/bun.js/bindings/webcore/JSAbortSignal.cpp @@ -225,7 +225,7 @@ static inline JSValue jsAbortSignal_reasonGetter(JSGlobalObject& lexicalGlobalOb auto& vm = JSC::getVM(&lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); auto& impl = thisObject.wrapped(); - RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.reason()))); + RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.jsReason(lexicalGlobalObject)))); } JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) diff --git a/src/bun.js/ipc.zig b/src/bun.js/ipc.zig index d19004bcdc..7f0f7f04eb 100644 --- a/src/bun.js/ipc.zig +++ b/src/bun.js/ipc.zig @@ -83,7 +83,7 @@ const advanced = struct { log("Received IPC message type {d} ({s}) len {d}", .{ @intFromEnum(message_type), - std.enums.tagName(IPCMessageType, message_type) orelse "unknown", + bun.tagName(IPCMessageType, message_type) orelse "unknown", message_len, }); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 2d0638c5bf..a5fa1f6479 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -2844,13 +2844,13 @@ pub const VirtualMachine = struct { writer: Writer, current_exception_list: ?*ExceptionList = null, - pub fn iteratorWithColor(_vm: [*c]VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn iteratorWithColor(_vm: [*c]VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { iterator(_vm, globalObject, nextValue, ctx.?, true); } - pub fn iteratorWithOutColor(_vm: [*c]VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn iteratorWithOutColor(_vm: [*c]VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { iterator(_vm, globalObject, nextValue, ctx.?, false); } - inline fn iterator(_: [*c]VM, _: [*c]JSGlobalObject, nextValue: JSValue, ctx: ?*anyopaque, comptime color: bool) void { + inline fn iterator(_: [*c]VM, _: *JSGlobalObject, nextValue: JSValue, ctx: ?*anyopaque, comptime color: bool) void { const this_ = @as(*@This(), @ptrFromInt(@intFromPtr(ctx))); VirtualMachine.get().printErrorlikeObject(nextValue, null, this_.current_exception_list, Writer, this_.writer, color, allow_side_effects); } diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 2c667f3c8d..b8cd068110 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -1779,7 +1779,7 @@ pub const ModuleLoader = struct { .allocator = null, .source_code = switch (comptime flags) { .print_source_and_clone => bun.String.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), - .print_source => bun.String.static(parse_result.source.contents), + .print_source => bun.String.init(parse_result.source.contents), else => @compileError("unreachable"), }, .specifier = input_specifier, @@ -2533,7 +2533,7 @@ pub const ModuleLoader = struct { } else if (jsc_vm.standalone_module_graph) |graph| { const specifier_utf8 = specifier.toUTF8(bun.default_allocator); defer specifier_utf8.deinit(); - if (graph.files.get(specifier_utf8.slice())) |file| { + if (graph.files.getPtr(specifier_utf8.slice())) |file| { if (file.loader == .sqlite or file.loader == .sqlite_embedded) { const code = \\/* Generated code */ @@ -2546,7 +2546,7 @@ pub const ModuleLoader = struct { ; return ResolvedSource{ .allocator = null, - .source_code = bun.String.init(code), + .source_code = bun.String.static(code), .specifier = specifier, .source_url = specifier.dupeRef(), .hash = 0, @@ -2556,7 +2556,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = bun.String.static(file.contents), + .source_code = file.toWTFString(), .specifier = specifier, .source_url = specifier.dupeRef(), .hash = 0, diff --git a/src/bun.js/node/node_fs_watcher.zig b/src/bun.js/node/node_fs_watcher.zig index 9c5a296bee..79cc5b8a49 100644 --- a/src/bun.js/node/node_fs_watcher.zig +++ b/src/bun.js/node/node_fs_watcher.zig @@ -535,7 +535,7 @@ pub const FSWatcher = struct { listener.ensureStillAlive(); var args = [_]JSC.JSValue{ EventType.@"error".toJS(this.globalThis), - if (err.isEmptyOrUndefinedOrNull()) JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.globalThis) else err, + JSC.CommonAbortReason.UserAbort.toJS(this.globalThis), }; _ = listener.callWithGlobalThis( this.globalThis, diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index b55d061ec4..0c4dd970e0 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -62,7 +62,7 @@ pub const OS = struct { } catch { const err = JSC.SystemError{ .message = bun.String.static("Failed to get cpu information"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR)), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -313,13 +313,10 @@ pub const OS = struct { const arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; if (arguments.len > 0 and !arguments[0].isNumber()) { - const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + globalThis.ERR_INVALID_ARG_TYPE( "getPriority() expects a number", .{}, - globalThis, - ); - globalThis.vm().throwError(globalThis, err); + ).throw(); return .undefined; } @@ -335,7 +332,7 @@ pub const OS = struct { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), //.info = info, .errno = -3, .syscall = bun.String.static("uv_os_getpriority"), @@ -419,7 +416,7 @@ pub const OS = struct { if (rc != 0) { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: getifaddrs returned an error"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), .errno = @intFromEnum(std.posix.errno(rc)), .syscall = bun.String.static("getifaddrs"), }; @@ -602,7 +599,7 @@ pub const OS = struct { if (err != 0) { const sys_err = JSC.SystemError{ .message = bun.String.static("uv_interface_addresses failed"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), //.info = info, .errno = err, .syscall = bun.String.static("uv_interface_addresses"), @@ -730,7 +727,7 @@ pub const OS = struct { if (arguments.len == 0) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + .ERR_INVALID_ARG_TYPE, "The \"priority\" argument must be of type number. Received undefined", .{}, globalThis, @@ -744,7 +741,7 @@ pub const OS = struct { if (priority < -20 or priority > 19) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_OUT_OF_RANGE, + .ERR_OUT_OF_RANGE, "The value of \"priority\" is out of range. It must be >= -20 && <= 19", .{}, globalThis, @@ -758,7 +755,7 @@ pub const OS = struct { .SRCH => { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_setpriority returned ESRCH (no such process)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), //.info = info, .errno = -3, .syscall = bun.String.static("uv_os_setpriority"), @@ -770,7 +767,7 @@ pub const OS = struct { .ACCES => { const err = JSC.SystemError{ .message = bun.String.static("A system error occurred: uv_os_setpriority returned EACCESS (permission denied)"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static(@tagName(.ERR_SYSTEM_ERROR)), //.info = info, .errno = -13, .syscall = bun.String.static("uv_os_setpriority"), @@ -811,7 +808,7 @@ pub const OS = struct { if (err != 0) { const sys_err = JSC.SystemError{ .message = bun.String.static("failed to get system uptime"), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .code = bun.String.static("ERR_SYSTEM_ERROR"), .errno = err, .syscall = bun.String.static("uv_uptime"), }; @@ -853,7 +850,7 @@ pub const OS = struct { return JSC.ZigString.init(C.getVersion(&name_buffer)).withEncoding().toJS(globalThis); } - inline fn getMachineName() []const u8 { + inline fn getMachineName() [:0]const u8 { return switch (@import("builtin").target.cpu.arch) { .arm => "arm", .aarch64 => "arm64", diff --git a/src/bun.js/node/util/parse_args.zig b/src/bun.js/node/util/parse_args.zig index 78f1bbd9e6..c047fddd25 100644 --- a/src/bun.js/node/util/parse_args.zig +++ b/src/bun.js/node/util/parse_args.zig @@ -193,7 +193,7 @@ fn checkOptionLikeValue(globalThis: *JSGlobalObject, token: OptionToken) ParseAr var err: JSValue = undefined; if (token.raw.asBunString(globalThis).hasPrefixComptime("--")) { err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{}' argument is ambiguous.\nDid you forget to specify the option argument for '{}'?\nTo specify an option argument starting with a dash use '{}=-XYZ'.", .{ raw_name, raw_name, raw_name }, globalThis, @@ -201,7 +201,7 @@ fn checkOptionLikeValue(globalThis: *JSGlobalObject, token: OptionToken) ParseAr } else { const token_name = token.name.asBunString(globalThis); err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{}' argument is ambiguous.\nDid you forget to specify the option argument for '{}'?\nTo specify an option argument starting with a dash use '--{}=-XYZ' or '{}-XYZ'.", .{ raw_name, raw_name, token_name, raw_name }, globalThis, @@ -219,7 +219,7 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti switch (option.type) { .string => if (token.value == .jsvalue and !token.value.jsvalue.isString()) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{s}{s}{s}--{s} ' argument missing", .{ if (!option.short_name.isEmpty()) "-" else "", @@ -234,7 +234,7 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti }, .boolean => if (token.value != .jsvalue or !token.value.jsvalue.isUndefined()) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_INVALID_OPTION_VALUE, + .ERR_PARSE_ARGS_INVALID_OPTION_VALUE, "Option '{s}{s}{s}--{s}' does not take an argument", .{ if (!option.short_name.isEmpty()) "-" else "", @@ -252,12 +252,12 @@ fn checkOptionUsage(globalThis: *JSGlobalObject, options: []const OptionDefiniti const raw_name = OptionToken.RawNameFormatter{ .token = token, .globalThis = globalThis }; const err = if (allow_positionals) (JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNKNOWN_OPTION, + .ERR_PARSE_ARGS_UNKNOWN_OPTION, "Unknown option '{}'. To specify a positional argument starting with a '-', place it at the end of the command after '--', as in '-- \"{}\"", .{ raw_name, raw_name }, globalThis, )) else (JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNKNOWN_OPTION, + .ERR_PARSE_ARGS_UNKNOWN_OPTION, "Unknown option '{}'", .{raw_name}, globalThis, @@ -328,7 +328,7 @@ fn parseOptionDefinitions(globalThis: *JSGlobalObject, options_obj: JSValue, opt try validateString(globalThis, short_option, "options.{s}.short", .{option.long_name}); var short_option_str = short_option.toBunString(globalThis); if (short_option_str.length() != 1) { - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "options.{s}.short must be a single character", .{option.long_name}, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "options.{s}.short must be a single character", .{option.long_name}, globalThis); globalThis.vm().throwError(globalThis, err); return error.ParseError; } @@ -592,7 +592,7 @@ const ParseArgsState = struct { .positional => |token| { if (!this.allow_positionals) { const err = JSC.toTypeError( - JSC.Node.ErrorCode.ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL, + .ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL, "Unexpected argument '{s}'. This command does not take positional arguments", .{token.value.asBunString(globalThis)}, globalThis, @@ -618,7 +618,7 @@ const ParseArgsState = struct { // reuse JSValue for the kind names: "positional", "option", "option-terminator" const kind_idx = @intFromEnum(token_generic); const kind_jsvalue = this.kinds_jsvalues[kind_idx] orelse kindval: { - const val = String.static(@as(string, @tagName(token_generic))).toJS(globalThis); + const val = String.static(@tagName(token_generic)).toJS(globalThis); this.kinds_jsvalues[kind_idx] = val; break :kindval val; }; diff --git a/src/bun.js/node/util/validators.zig b/src/bun.js/node/util/validators.zig index 6ea243b67c..f13de796c2 100644 --- a/src/bun.js/node/util/validators.zig +++ b/src/bun.js/node/util/validators.zig @@ -16,23 +16,22 @@ pub fn getTypeName(globalObject: *JSGlobalObject, value: JSValue) ZigString { pub fn throwErrInvalidArgValue( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, fmt, args, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fmt, args, globalThis); globalThis.vm().throwError(globalThis, err); return error.InvalidArgument; } pub fn throwErrInvalidArgTypeWithMessage( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, fmt, args, globalThis); - globalThis.vm().throwError(globalThis, err); + globalThis.ERR_INVALID_ARG_TYPE(fmt, args).throw(); return error.InvalidArgument; } @@ -50,7 +49,7 @@ pub fn throwErrInvalidArgType( pub fn throwRangeError( globalThis: *JSGlobalObject, - comptime fmt: string, + comptime fmt: [:0]const u8, args: anytype, ) !void { @setCold(true); diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 5047e27039..954f3db565 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -129,7 +129,7 @@ pub const Expect = struct { } }; - pub fn getSignature(comptime matcher_name: string, comptime args: string, comptime not: bool) string { + pub fn getSignature(comptime matcher_name: string, comptime args: string, comptime not: bool) [:0]const u8 { const received = "expect(received)."; comptime if (not) { return received ++ "not." ++ matcher_name ++ "(" ++ args ++ ")"; @@ -394,11 +394,11 @@ pub const Expect = struct { return expect_js_value; } - pub fn throw(this: *Expect, globalThis: *JSGlobalObject, comptime signature: string, comptime fmt: string, args: anytype) void { + pub fn throw(this: *Expect, globalThis: *JSGlobalObject, comptime signature: [:0]const u8, comptime fmt: [:0]const u8, args: anytype) void { if (this.custom_label.isEmpty()) { - globalThis.throwPretty(comptime signature ++ fmt, args); + globalThis.throwPretty(signature ++ fmt, args); } else { - globalThis.throwPretty(comptime "{}" ++ fmt, .{this.custom_label} ++ args); + globalThis.throwPretty("{}" ++ fmt, .{this.custom_label} ++ args); } } @@ -851,8 +851,7 @@ pub const Expect = struct { const not = this.flags.not; if (!value.isObject()) { - const err = globalThis.createTypeErrorInstance("Expected value must be an object\nReceived: {}", .{value.toFmt(&formatter)}); - globalThis.throwValue(err); + globalThis.throwInvalidArguments("Expected value must be an object\nReceived: {}", .{value.toFmt(&formatter)}); return .zero; } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index a04806e597..0a2a770df9 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -1739,7 +1739,7 @@ inline fn createScope( inline fn createIfScope( globalThis: *JSGlobalObject, callframe: *CallFrame, - comptime property: string, + comptime property: [:0]const u8, comptime signature: string, comptime Scope: type, comptime tag: Tag, @@ -2032,7 +2032,7 @@ fn eachBind( inline fn createEach( globalThis: *JSGlobalObject, callframe: *CallFrame, - comptime property: string, + comptime property: [:0]const u8, comptime signature: string, comptime is_test: bool, ) JSValue { diff --git a/src/bun.js/test/pretty_format.zig b/src/bun.js/test/pretty_format.zig index c571ee533f..4f66dc796e 100644 --- a/src/bun.js/test/pretty_format.zig +++ b/src/bun.js/test/pretty_format.zig @@ -677,7 +677,7 @@ pub const JestPrettyFormat = struct { return struct { formatter: *JestPrettyFormat.Formatter, writer: Writer, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); const key = JSC.JSObject.getIndex(nextValue, globalObject, 0); const value = JSC.JSObject.getIndex(nextValue, globalObject, 1); @@ -712,7 +712,7 @@ pub const JestPrettyFormat = struct { return struct { formatter: *JestPrettyFormat.Formatter, writer: Writer, - pub fn forEach(_: [*c]JSC.VM, globalObject: [*c]JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { + pub fn forEach(_: [*c]JSC.VM, globalObject: *JSGlobalObject, ctx: ?*anyopaque, nextValue: JSValue) callconv(.C) void { var this: *@This() = bun.cast(*@This(), ctx orelse return); this.formatter.writeIndent(Writer, this.writer) catch {}; const key_tag = Tag.get(nextValue, globalObject); diff --git a/src/bun.js/webcore.zig b/src/bun.js/webcore.zig index 48109d21c5..8a869afff6 100644 --- a/src/bun.js/webcore.zig +++ b/src/bun.js/webcore.zig @@ -547,7 +547,7 @@ pub const Crypto = struct { return .zero; } - fn throwInvalidParams(globalThis: *JSC.JSGlobalObject, comptime error_type: @Type(.EnumLiteral), comptime message: string, fmt: anytype) JSC.JSValue { + fn throwInvalidParams(globalThis: *JSC.JSGlobalObject, comptime error_type: @Type(.EnumLiteral), comptime message: [:0]const u8, fmt: anytype) JSC.JSValue { const err = switch (error_type) { .RangeError => globalThis.createRangeErrorInstanceWithCode( .ERR_CRYPTO_INVALID_SCRYPT_PARAMS, diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 6c10647225..9dff428406 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -1393,17 +1393,21 @@ pub const Blob = struct { } { const name_value_str = bun.String.tryFromJS(args[1], globalThis) orelse { - globalThis.throwInvalidArguments("new File(bits, name) expects string as the second argument", .{}); + if (!globalThis.hasException()) { + globalThis.throwInvalidArguments("new File(bits, name) expects string as the second argument", .{}); + } return null; }; defer name_value_str.deref(); blob = get(globalThis, args[0], false, true) catch |err| { - if (err == error.InvalidArguments) { - globalThis.throwInvalidArguments("new File(bits, name) expects iterable as the first argument", .{}); - return null; + if (!globalThis.hasException()) { + if (err == error.InvalidArguments) { + globalThis.throwInvalidArguments("new File(bits, name) expects iterable as the first argument", .{}); + return null; + } + globalThis.throwOutOfMemory(); } - globalThis.throwOutOfMemory(); return null; }; diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 43282d5ec0..46ce1d849d 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -321,10 +321,24 @@ pub const Body = struct { Message: bun.String, JSValue: JSC.Strong, + pub fn toStreamError(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.WebCore.StreamResult.StreamError { + return switch (this.*) { + .Aborted => .{ + .AbortReason = JSC.CommonAbortReason.UserAbort, + }, + .Timeout => .{ + .AbortReason = JSC.CommonAbortReason.Timeout, + }, + else => .{ + .JSValue = this.toJS(globalObject), + }, + }; + } + pub fn toJS(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue { const js_value = switch (this.*) { - .Timeout => JSC.WebCore.AbortSignal.createTimeoutError(JSC.ZigString.static("The operation timed out"), &JSC.ZigString.Empty, globalObject), - .Aborted => JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalObject), + .Timeout => JSC.CommonAbortReason.Timeout.toJS(globalObject), + .Aborted => JSC.CommonAbortReason.UserAbort.toJS(globalObject), .SystemError => |system_error| system_error.toErrorInstance(globalObject), .Message => |message| message.toErrorInstance(globalObject), // do a early return in this case we don't need to create a new Strong @@ -648,12 +662,15 @@ pub const Body = struct { return Body.Value{ .Blob = Blob.get(globalThis, value, true, false) catch |err| { - if (err == error.InvalidArguments) { - globalThis.throwInvalidArguments("Expected an Array", .{}); - return null; + if (!globalThis.hasException()) { + if (err == error.InvalidArguments) { + globalThis.throwInvalidArguments("Expected an Array", .{}); + return null; + } + + globalThis.throwInvalidArguments("Invalid Body object", .{}); } - globalThis.throwInvalidArguments("Invalid Body object", .{}); return null; }, }; @@ -927,12 +944,12 @@ pub const Body = struct { } } + // The Promise version goes before the ReadableStream version incase the Promise version is used too. + // Avoid creating unnecessary duplicate JSValue. if (strong_readable.get()) |readable| { if (readable.ptr == .Bytes) { readable.ptr.Bytes.onData( - .{ - .err = .{ .JSValue = this.Error.toJS(global) }, - }, + .{ .err = this.Error.toStreamError(global) }, bun.default_allocator, ); } else { @@ -1186,11 +1203,7 @@ pub fn BodyMixin(comptime Type: type) type { var encoder = this.getFormDataEncoding() orelse { // TODO: catch specific errors from getFormDataEncoding - const err = globalObject.createTypeErrorInstance("Can't decode form data from body because of incorrect MIME type/boundary", .{}); - return JSC.JSPromise.rejectedPromiseValue( - globalObject, - err, - ); + return globalObject.ERR_FORMDATA_PARSE_ERROR("Can't decode form data from body because of incorrect MIME type/boundary", .{}).reject(); }; if (value.* == .Locked) { @@ -1206,15 +1219,12 @@ pub fn BodyMixin(comptime Type: type) type { blob.slice(), encoder.encoding, ) catch |err| { - return JSC.JSPromise.rejectedPromiseValue( - globalObject, - globalObject.createTypeErrorInstance( - "FormData parse error {s}", - .{ - @errorName(err), - }, - ), - ); + return globalObject.ERR_FORMDATA_PARSE_ERROR( + "FormData parse error {s}", + .{ + @errorName(err), + }, + ).reject(); }; return JSC.JSPromise.wrap( diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index fa2cca8d98..667a4991a0 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -649,8 +649,7 @@ pub const TextDecoder = struct { } else |err| { switch (err) { error.InvalidByteSequence => { - const type_error = globalThis.createErrorInstanceWithCode(.ERR_ENCODING_INVALID_ENCODED_DATA, "Invalid byte sequence", .{}); - globalThis.throwValue(type_error); + globalThis.ERR_ENCODING_INVALID_ENCODED_DATA("Invalid byte sequence", .{}).throw(); return .zero; }, error.OutOfMemory => { diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 150602df4d..3eb2dcb2c8 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -495,8 +495,7 @@ pub const Response = struct { bun.assert(!arguments[1].isEmptyOrUndefinedOrNull()); - const err = globalThis.createTypeErrorInstance("Expected options to be one of: null, undefined, or object", .{}); - globalThis.throwValue(err); + globalThis.throwInvalidArguments("Expected options to be one of: null, undefined, or object", .{}); break :brk null; }, } @@ -1262,7 +1261,7 @@ pub const Fetch = struct { if (signal.aborted()) { this.abort_reason = signal.abortReason(); if (this.abort_reason.isEmptyOrUndefinedOrNull()) { - return JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.global_this); + return JSC.CommonAbortReason.UserAbort.toJS(this.global_this); } this.abort_reason.protect(); return this.abort_reason; @@ -1864,12 +1863,12 @@ pub const Fetch = struct { } if (url_str.tag == .Dead) { - globalObject.throwValue(JSC.toTypeError(.ERR_INVALID_ARG_TYPE, "Invalid URL", .{}, globalObject)); + globalObject.ERR_INVALID_ARG_TYPE("Invalid URL", .{}).throw(); return .zero; } if (url_str.isEmpty()) { - globalObject.throwValue(JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, globalObject)); + globalObject.ERR_INVALID_ARG_TYPE(fetch_error_blank_url, .{}).throw(); return .zero; } @@ -1881,7 +1880,7 @@ pub const Fetch = struct { } if (url.hostname.len == 0) { - globalObject.throwValue(JSC.toTypeError(.ERR_INVALID_ARG_VALUE, fetch_error_blank_url, .{}, globalObject)); + globalObject.ERR_INVALID_ARG_TYPE(fetch_error_blank_url, .{}).throw(); bun.default_allocator.free(url.href); return .zero; } @@ -2349,8 +2348,9 @@ pub const Fetch = struct { bun.default_allocator.free(hn); hostname = null; } - const err = JSC.toTypeError(.ERR_INVALID_ARG_VALUE, "fetch() URL is invalid", .{}, ctx); - return JSPromise.rejectedPromiseValue(globalThis, err); + return globalThis + .ERR_INVALID_ARG_VALUE("fetch() URL is invalid", .{}) + .reject(); }; url_proxy_buffer = url.href; if (url.isFile()) { @@ -2526,9 +2526,12 @@ pub const Fetch = struct { } } } else { - const fetch_error = fetch_type_error_strings.get(js.JSValueGetType(ctx, first_arg.asRef())); - const err = JSC.toTypeError(.ERR_INVALID_ARG_TYPE, "{s}", .{fetch_error}, ctx); - exception.* = err.asObjectRef(); + if (!globalThis.hasException()) { + const fetch_error = fetch_type_error_strings.get(js.JSValueGetType(ctx, first_arg.asRef())); + const err = JSC.toTypeError(.ERR_INVALID_ARG_TYPE, "{s}", .{fetch_error}, ctx); + exception.* = err.asObjectRef(); + } + return .zero; } diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index b24fdb3e5f..f00258029e 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -660,7 +660,7 @@ pub const DrainResult = union(enum) { pub const StreamResult = union(Tag) { pending: *Pending, - err: union(Err) { Error: Syscall.Error, JSValue: JSC.JSValue }, + err: StreamError, done: void, owned: bun.ByteList, owned_and_done: bun.ByteList, @@ -682,9 +682,32 @@ pub const StreamResult = union(Tag) { } } - pub const Err = enum { - Error, - JSValue, + pub const StreamError = union(enum) { + Error: Syscall.Error, + AbortReason: JSC.CommonAbortReason, + + // TODO: use an explicit JSC.Strong here. + JSValue: JSC.JSValue, + WeakJSValue: JSC.JSValue, + + const WasStrong = enum { + Strong, + Weak, + }; + + pub fn toJSWeak(this: *const @This(), globalObject: *JSC.JSGlobalObject) struct { JSC.JSValue, WasStrong } { + return switch (this.*) { + .Error => |err| { + return .{ err.toJSC(globalObject), WasStrong.Weak }; + }, + .JSValue => .{ this.JSValue, WasStrong.Strong }, + .WeakJSValue => .{ this.WeakJSValue, WasStrong.Weak }, + .AbortReason => |reason| { + const value = reason.toJS(globalObject); + return .{ value, WasStrong.Weak }; + }, + }; + } }; pub const Tag = enum { @@ -943,13 +966,12 @@ pub const StreamResult = union(Tag) { defer loop.exit(); switch (result.*) { - .err => |err| { + .err => |*err| { const value = brk: { - if (err == .Error) break :brk err.Error.toJSC(globalThis); - - const js_err = err.JSValue; + const js_err, const was_strong = err.toJSWeak(globalThis); js_err.ensureStillAlive(); - js_err.unprotect(); + if (was_strong == .Strong) + js_err.unprotect(); break :brk js_err; }; @@ -1010,12 +1032,11 @@ pub const StreamResult = union(Tag) { }, .err => |err| { - if (err == .Error) { - return JSC.JSPromise.rejectedPromise(globalThis, JSValue.c(err.Error.toJS(globalThis))).asValue(globalThis); + const js_err, const was_strong = err.toJSWeak(globalThis); + if (was_strong == .Strong) { + js_err.unprotect(); } - const js_err = err.JSValue; js_err.ensureStillAlive(); - js_err.unprotect(); return JSC.JSPromise.rejectedPromise(globalThis, js_err).asValue(globalThis); }, @@ -1637,7 +1658,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { }; const err = JSC.SystemError{ .message = bun.String.static(Static.message), - .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_ILLEGAL_CONSTRUCTOR))), + .code = bun.String.static(@tagName(.ERR_ILLEGAL_CONSTRUCTOR)), }; globalThis.throwValue(err.toErrorInstance(globalThis)); return .undefined; @@ -1690,7 +1711,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { } fn invalidThis(globalThis: *JSGlobalObject) JSValue { - const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); + const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected Sink", .{}, globalThis); globalThis.vm().throwError(globalThis, err); return .undefined; } @@ -1716,7 +1737,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (args.len == 0) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_MISSING_ARGS, + .ERR_MISSING_ARGS, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, @@ -1730,7 +1751,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (arg.isEmptyOrUndefinedOrNull()) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_STREAM_NULL_VALUES, + .ERR_STREAM_NULL_VALUES, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, @@ -1749,7 +1770,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { if (!arg.isString()) { globalThis.vm().throwError(globalThis, JSC.toTypeError( - JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + .ERR_INVALID_ARG_TYPE, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{}, globalThis, @@ -1785,7 +1806,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { const args = args_list.ptr[0..args_list.len]; if (args.len == 0 or !args[0].isString()) { const err = JSC.toTypeError( - if (args.len == 0) JSC.Node.ErrorCode.ERR_MISSING_ARGS else JSC.Node.ErrorCode.ERR_INVALID_ARG_TYPE, + if (args.len == 0) .ERR_MISSING_ARGS else .ERR_INVALID_ARG_TYPE, "writeUTF8() expects a string", .{}, globalThis, @@ -1974,7 +1995,7 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { // JSC.markBinding(@src()); // var this = @ptrCast(*ThisSocket, @alignCast( fromJS(globalThis, callframe.this()) orelse { -// const err = JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_THIS, "Expected Socket", .{}, globalThis); +// const err = JSC.toTypeError(.ERR_INVALID_THIS, "Expected Socket", .{}, globalThis); // globalThis.vm().throwError(globalThis, err); // return .undefined; // })); @@ -4332,13 +4353,9 @@ pub const ByteStream = struct { if (to_copy.len == 0) { if (stream == .err) { - if (stream.err == .Error) { - this.pending.result = .{ .err = .{ .Error = stream.err.Error } }; - } - const js_err = stream.err.JSValue; - js_err.ensureStillAlive(); - js_err.protect(); - this.pending.result = .{ .err = .{ .JSValue = js_err } }; + this.pending.result = .{ + .err = stream.err, + }; } else { this.pending.result = .{ .done = {}, diff --git a/src/bun.zig b/src/bun.zig index 2aeb572bea..0b95eb26d8 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1261,11 +1261,11 @@ pub const JSON = @import("./json_parser.zig"); pub const JSAst = @import("./js_ast.zig"); pub const bit_set = @import("./bit_set.zig"); -pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { +pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) [:0]const u8) { const Map = struct { const vargs = args; const labels = brk: { - var vabels_ = std.enums.EnumArray(T, []const u8).initFill(""); + var vabels_ = std.enums.EnumArray(T, [:0]const u8).initFill(""); @setEvalBranchQuota(99999); for (vargs) |field| { vabels_.set(field.@"0", field.@"1"); @@ -1273,7 +1273,7 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { break :brk vabels_; }; - pub fn get(input: T) []const u8 { + pub fn get(input: T) [:0]const u8 { return labels.get(input); } }; @@ -1282,7 +1282,7 @@ pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) { } pub fn ComptimeEnumMap(comptime T: type) type { - var entries: [std.enums.values(T).len]struct { string, T } = undefined; + var entries: [std.enums.values(T).len]struct { [:0]const u8, T } = undefined; for (std.enums.values(T), &entries) |value, *entry| { entry.* = .{ .@"0" = @tagName(value), .@"1" = value }; } @@ -3638,3 +3638,10 @@ pub fn memmove(output: []u8, input: []const u8) void { pub const hmac = @import("./hmac.zig"); pub const libdeflate = @import("./deps/libdeflate.zig"); + +/// like std.enums.tagName, except it doesn't lose the sentinel value. +pub fn tagName(comptime Enum: type, value: Enum) ?[:0]const u8 { + return inline for (@typeInfo(Enum).Enum.fields) |f| { + if (@intFromEnum(value) == f.value) break f.name; + } else null; +} diff --git a/src/codegen/generate-node-errors.ts b/src/codegen/generate-node-errors.ts new file mode 100644 index 0000000000..838fc32f90 --- /dev/null +++ b/src/codegen/generate-node-errors.ts @@ -0,0 +1,128 @@ +import NodeErrors from "../bun.js/bindings/ErrorCode.ts"; +const outputDir = process.argv[2]; +import path from "node:path"; + +if (!outputDir) { + throw new Error("Missing output directory"); +} + +let enumHeader = ``; +let listHeader = ``; +let zig = ``; + +enumHeader = ` +// clang-format off +// Generated by: src/codegen/generate-node-errors.ts +#pragma once + +namespace Bun { + static constexpr size_t NODE_ERROR_COUNT = ${NodeErrors.length}; + enum class ErrorCode : uint8_t { +`; + +listHeader = ` +// clang-format off +// Generated by: src/codegen/generate-node-errors.ts +#pragma once + +struct ErrorCodeData { + JSC::ErrorType type; + WTF::ASCIILiteral name; + WTF::ASCIILiteral code; +}; +static constexpr ErrorCodeData errors[${NodeErrors.length}] = { +`; + +zig = ` +// Generated by: src/codegen/generate-node-errors.ts +const std = @import("std"); +const bun = @import("root").bun; +const JSC = bun.JSC; + +fn ErrorBuilder(comptime code_: Error, comptime fmt_: [:0]const u8, Args: type) type { + return struct { + const code = code_; + const fmt = fmt_; + globalThis: *JSC.JSGlobalObject, + args: Args, + + // Throw this error as a JS exception + pub inline fn throw(this: @This()) void { + code.throw(this.globalThis, fmt, this.args); + } + + /// Turn this into a JSValue + pub inline fn toJS(this: @This()) JSC.JSValue { + return code.fmt(this.globalThis, fmt, this.args); + } + + /// Turn this into a JSPromise that is already rejected. + pub inline fn reject(this: @This()) JSC.JSValue { + return JSC.JSPromise.rejectedPromiseValue(this.globalThis, code.fmt(this.globalThis, fmt, this.args)); + } + + }; +} + +pub const Error = enum(u8) { + +`; + +let i = 0; +let listForUsingNamespace = ""; +for (const [code, constructor, name] of NodeErrors) { + enumHeader += ` ${code} = ${i},\n`; + listHeader += ` { JSC::ErrorType::${constructor.name}, "${name}"_s, "${code}"_s },\n`; + zig += ` ${code} = ${i},\n`; + listForUsingNamespace += ` /// ${name}: ${code} (instanceof ${constructor.name})\n`; + listForUsingNamespace += ` pub inline fn ${code}(globalThis: *JSC.JSGlobalObject, comptime fmt: [:0]const u8, args: anytype) ErrorBuilder(Error.${code}, fmt, @TypeOf(args)) {\n`; + listForUsingNamespace += ` return .{ .globalThis = globalThis, .args = args };\n`; + listForUsingNamespace += ` }\n`; + i++; +} + +enumHeader += ` +}; +} // namespace Bun +`; + +listHeader += ` +}; +`; + +zig += ` + + + extern fn Bun__createErrorWithCode(globalThis: *JSC.JSGlobalObject, code: Error, message: *bun.String) JSC.JSValue; + + /// Creates an Error object with the given error code. + /// Derefs the message string. + pub fn toJS(this: Error, globalThis: *JSC.JSGlobalObject, message: *bun.String) JSC.JSValue { + defer message.deref(); + return Bun__createErrorWithCode(globalThis, this, message); + } + + pub fn fmt(this: Error, globalThis: *JSC.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) JSC.JSValue { + if (comptime std.meta.fieldNames(@TypeOf(args)).len == 0) { + var message = bun.String.static(fmt_str); + return toJS(this, globalThis, &message); + } + + var message = bun.String.createFormat(fmt_str, args) catch bun.outOfMemory(); + return toJS(this, globalThis, &message); + } + + pub fn throw(this: Error, globalThis: *JSC.JSGlobalObject, comptime fmt_str: [:0]const u8, args: anytype) void { + globalThis.throwValue(fmt(this, globalThis, fmt_str, args)); + } + +}; + +pub const JSGlobalObjectExtensions = struct { +${listForUsingNamespace} +}; +`; + +await Bun.write(path.join(outputDir, "ErrorCode+List.h"), enumHeader); +await Bun.write(path.join(outputDir, "ErrorCode+Data.h"), listHeader); +await Bun.write(path.join(outputDir, "ErrorCode.zig"), zig); diff --git a/src/darwin_c.zig b/src/darwin_c.zig index a5b3a8721a..bdf1c97674 100644 --- a/src/darwin_c.zig +++ b/src/darwin_c.zig @@ -298,11 +298,11 @@ pub const SystemErrno = enum(u8) { return @enumFromInt(code); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.EnumMap(SystemErrno, []const u8); + const LabelMap = std.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); map.put(.E2BIG, "Argument list too long"); diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index e009a2ab54..a6a9ccfef7 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -1604,7 +1604,8 @@ pub export fn Bun__canonicalizeIP( const size = bun.len(bun.cast([*:0]u8, &ip_addr)); return JSC.ZigString.init(ip_addr[0..size]).toJS(globalThis); } else { - globalThis.throwInvalidArguments("address must be a string", .{}); + if (!globalThis.hasException()) + globalThis.throwInvalidArguments("address must be a string", .{}); return .zero; } } diff --git a/src/install/resolution.zig b/src/install/resolution.zig index 39e3decda1..2fecc21aff 100644 --- a/src/install/resolution.zig +++ b/src/install/resolution.zig @@ -247,7 +247,7 @@ pub const Resolution = extern struct { pub fn format(formatter: DebugFormatter, comptime layout: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void { try writer.writeAll("Resolution{ ."); - try writer.writeAll(std.enums.tagName(Tag, formatter.resolution.tag) orelse "invalid"); + try writer.writeAll(bun.tagName(Tag, formatter.resolution.tag) orelse "invalid"); try writer.writeAll(" = "); switch (formatter.resolution.tag) { .npm => try formatter.resolution.value.npm.version.fmt(formatter.buf).format(layout, opts, writer), diff --git a/src/js/internal/errors.ts b/src/js/internal/errors.ts index e6f6051235..4281a68059 100644 --- a/src/js/internal/errors.ts +++ b/src/js/internal/errors.ts @@ -1,8 +1,8 @@ export default { - ERR_INVALID_ARG_TYPE: $newCppFunction("NodeError.cpp", "jsFunction_ERR_INVALID_ARG_TYPE", 3), - ERR_OUT_OF_RANGE: $newCppFunction("NodeError.cpp", "jsFunction_ERR_OUT_OF_RANGE", 3), - ERR_IPC_DISCONNECTED: $newCppFunction("NodeError.cpp", "jsFunction_ERR_IPC_DISCONNECTED", 0), - ERR_SERVER_NOT_RUNNING: $newCppFunction("NodeError.cpp", "jsFunction_ERR_SERVER_NOT_RUNNING", 0), - ERR_IPC_CHANNEL_CLOSED: $newCppFunction("NodeError.cpp", "jsFunction_ERR_IPC_CHANNEL_CLOSED", 0), - ERR_SOCKET_BAD_TYPE: $newCppFunction("NodeError.cpp", "jsFunction_ERR_SOCKET_BAD_TYPE", 0), + ERR_INVALID_ARG_TYPE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_INVALID_ARG_TYPE", 3), + ERR_OUT_OF_RANGE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_OUT_OF_RANGE", 3), + ERR_IPC_DISCONNECTED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_IPC_DISCONNECTED", 0), + ERR_SERVER_NOT_RUNNING: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_SERVER_NOT_RUNNING", 0), + ERR_IPC_CHANNEL_CLOSED: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_IPC_CHANNEL_CLOSED", 0), + ERR_SOCKET_BAD_TYPE: $newCppFunction("ErrorCode.cpp", "jsFunction_ERR_SOCKET_BAD_TYPE", 0), }; diff --git a/src/jsc.zig b/src/jsc.zig index f2a77d910b..c10ef6f96b 100644 --- a/src/jsc.zig +++ b/src/jsc.zig @@ -112,3 +112,5 @@ pub const conv = if (bun.Environment.isWindows and bun.Environment.isX64) std.builtin.CallingConvention.SysV else std.builtin.CallingConvention.C; + +pub const Error = @import("ErrorCode").Error; diff --git a/src/linux_c.zig b/src/linux_c.zig index 46e1b080a0..64b46ea178 100644 --- a/src/linux_c.zig +++ b/src/linux_c.zig @@ -152,11 +152,11 @@ pub const SystemErrno = enum(u8) { return @enumFromInt(code); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.EnumMap(SystemErrno, []const u8); + const LabelMap = std.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); diff --git a/src/output.zig b/src/output.zig index 61c6f3cabb..ef89f456c5 100644 --- a/src/output.zig +++ b/src/output.zig @@ -700,7 +700,7 @@ pub const color_map = ComptimeStringMap(string, .{ &.{ "yellow", ED ++ "33m" }, }); const RESET: string = "\x1b[0m"; -pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) string { +pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) [:0]const u8 { if (comptime bun.fast_debug_build_mode) return fmt; @@ -778,8 +778,7 @@ pub fn prettyFmt(comptime fmt: string, comptime is_enabled: bool) string { } }; - const fmt_data = comptime new_fmt[0..new_fmt_i].*; - return &fmt_data; + return comptime (new_fmt[0..new_fmt_i].* ++ .{0})[0..new_fmt_i :0]; } pub noinline fn prettyWithPrinter(comptime fmt: string, args: anytype, comptime printer: anytype, comptime l: Destination) void { diff --git a/src/sql/postgres.zig b/src/sql/postgres.zig index d88db48535..35195afecd 100644 --- a/src/sql/postgres.zig +++ b/src/sql/postgres.zig @@ -2085,7 +2085,7 @@ pub const PostgresSQLQuery = struct { return; } - const instance = globalObject.createTypeErrorInstance("Failed to bind query: {s}", .{@errorName(err)}); + const instance = globalObject.createErrorInstance("Failed to bind query: {s}", .{@errorName(err)}); // TODO: error handling const vm = JSC.VirtualMachine.get(); diff --git a/src/string.zig b/src/string.zig index 31df35dcdd..087f57ac6b 100644 --- a/src/string.zig +++ b/src/string.zig @@ -314,6 +314,10 @@ pub const String = extern struct { extern fn BunString__fromLatin1Unitialized(len: usize) String; extern fn BunString__fromUTF16Unitialized(len: usize) String; + pub fn ascii(bytes: []const u8) String { + return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.init(bytes) } }; + } + pub fn isGlobal(this: String) bool { return this.tag == Tag.ZigString and this.value.ZigString.isGloballyAllocated(); } @@ -430,7 +434,11 @@ pub const String = extern struct { return BunString__fromUTF16(bytes.ptr, bytes.len); } - pub fn createFormat(comptime fmt: []const u8, args: anytype) !String { + pub fn createFormat(comptime fmt: [:0]const u8, args: anytype) !String { + if (comptime std.meta.fieldNames(@TypeOf(args)).len == 0) { + return String.static(fmt); + } + var sba = std.heap.stackFallback(16384, bun.default_allocator); const alloc = sba.get(); const buf = try std.fmt.allocPrint(alloc, fmt, args); @@ -566,7 +574,7 @@ pub const String = extern struct { }; } - pub fn static(input: []const u8) String { + pub fn static(input: [:0]const u8) String { return .{ .tag = .StaticZigString, .value = .{ .StaticZigString = ZigString.init(input) }, @@ -585,6 +593,11 @@ pub const String = extern struct { ptr: ?*anyopaque, callback: ?*const fn (*anyopaque, *anyopaque, u32) callconv(.C) void, ) String; + extern fn BunString__createStaticExternal( + bytes: [*]const u8, + len: usize, + isLatin1: bool, + ) String; /// ctx is the pointer passed into `createExternal` /// buffer is the pointer to the buffer, either [*]u8 or [*]u16 @@ -597,6 +610,16 @@ pub const String = extern struct { return BunString__createExternal(bytes.ptr, bytes.len, isLatin1, ctx, callback); } + /// This should rarely be used. The WTF::StringImpl* will never be freed. + /// + /// So this really only makes sense when you need to dynamically allocate a + /// string that will never be freed. + pub fn createStaticExternal(bytes: []const u8, isLatin1: bool) String { + JSC.markBinding(@src()); + bun.assert(bytes.len > 0); + return BunString__createStaticExternal(bytes.ptr, bytes.len, isLatin1); + } + extern fn BunString__createExternalGloballyAllocatedLatin1( bytes: [*]u8, len: usize, diff --git a/src/string_builder.zig b/src/string_builder.zig index 60b46dbd32..64bbc005c6 100644 --- a/src/string_builder.zig +++ b/src/string_builder.zig @@ -134,6 +134,25 @@ pub fn appendCount(this: *StringBuilder, slice: string) bun.StringPointer { return bun.StringPointer{ .offset = @as(u32, @truncate(start)), .length = @as(u32, @truncate(slice.len)) }; } +pub fn appendCountZ(this: *StringBuilder, slice: string) bun.StringPointer { + if (comptime Environment.allow_assert) { + assert(this.len <= this.cap); // didn't count everything + assert(this.ptr != null); // must call allocate first + } + + const start = this.len; + bun.copy(u8, this.ptr.?[this.len..this.cap], slice); + this.ptr.?[this.len + slice.len] = 0; + const result = this.ptr.?[this.len..this.cap][0..slice.len]; + _ = result; + this.len += slice.len; + this.len += 1; + + if (comptime Environment.allow_assert) assert(this.len <= this.cap); + + return bun.StringPointer{ .offset = @as(u32, @truncate(start)), .length = @as(u32, @truncate(slice.len)) }; +} + pub fn fmt(this: *StringBuilder, comptime str: string, args: anytype) string { if (comptime Environment.allow_assert) { assert(this.len <= this.cap); // didn't count everything @@ -168,6 +187,26 @@ pub fn fmtAppendCount(this: *StringBuilder, comptime str: string, args: anytype) }; } +pub fn fmtAppendCountZ(this: *StringBuilder, comptime str: string, args: anytype) bun.StringPointer { + if (comptime Environment.allow_assert) { + assert(this.len <= this.cap); // didn't count everything + assert(this.ptr != null); // must call allocate first + } + + const buf = this.ptr.?[this.len..this.cap]; + const out = std.fmt.bufPrintZ(buf, str, args) catch unreachable; + const off = this.len; + this.len += out.len; + this.len += 1; + + if (comptime Environment.allow_assert) assert(this.len <= this.cap); + + return bun.StringPointer{ + .offset = @as(u32, @truncate(off)), + .length = @as(u32, @truncate(out.len)), + }; +} + pub fn fmtCount(this: *StringBuilder, comptime str: string, args: anytype) void { this.cap += std.fmt.count(str, args); } diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 5552cae6a7..1b4342865b 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -155,8 +155,8 @@ pub inline fn containsComptime(self: string, comptime str: string) bool { } pub const includes = contains; -pub fn inMapCaseInsensitive(self: string, comptime ComptimeStringMap: anytype) ?ComptimeStringMap.Value { - return bun.String.static(self).inMapCaseInsensitive(ComptimeStringMap); +pub fn inMapCaseInsensitive(self: []const u8, comptime ComptimeStringMap: anytype) ?ComptimeStringMap.Value { + return bun.String.ascii(self).inMapCaseInsensitive(ComptimeStringMap); } pub inline fn containsAny(in: anytype, target: string) bool { diff --git a/src/sys.zig b/src/sys.zig index eee32ce408..3013a79e9b 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -366,12 +366,12 @@ pub const Error = struct { break :brk @as(C.SystemErrno, @enumFromInt(this.errno)); }; - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { return errname; } } else if (this.errno > 0 and this.errno < C.SystemErrno.max) { const system_errno = @as(C.SystemErrno, @enumFromInt(this.errno)); - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { return errname; } } @@ -408,7 +408,7 @@ pub const Error = struct { break :brk @as(C.SystemErrno, @enumFromInt(this.errno)); }; - if (std.enums.tagName(bun.C.SystemErrno, system_errno)) |errname| { + if (bun.tagName(bun.C.SystemErrno, system_errno)) |errname| { err.code = bun.String.static(errname); if (C.SystemErrno.labels.get(system_errno)) |label| { err.message = bun.String.static(label); diff --git a/src/windows_c.zig b/src/windows_c.zig index db6a06b8a3..abb19b54dc 100644 --- a/src/windows_c.zig +++ b/src/windows_c.zig @@ -814,11 +814,11 @@ pub const SystemErrno = enum(u16) { return @as(SystemErrno, @enumFromInt(code)); } - pub fn label(this: SystemErrno) ?[]const u8 { + pub fn label(this: SystemErrno) ?[:0]const u8 { return labels.get(this) orelse null; } - const LabelMap = std.enums.EnumMap(SystemErrno, []const u8); + const LabelMap = std.enums.EnumMap(SystemErrno, [:0]const u8); pub const labels: LabelMap = brk: { var map: LabelMap = LabelMap.initFull(""); diff --git a/test/js/third_party/body-parser/express-compile-fixture.ts b/test/js/third_party/body-parser/express-compile-fixture.ts index c74234b2a9..073203eca1 100644 --- a/test/js/third_party/body-parser/express-compile-fixture.ts +++ b/test/js/third_party/body-parser/express-compile-fixture.ts @@ -1,6 +1,9 @@ const express = require("express"); const app = express(); const port = 0; +// https://github.com/oven-sh/bun/issues/11739 +import json from "./package.json"; +import textFile from "./text.txt"; app.get("/", (req, res) => { res.send("Hello World!"); @@ -13,6 +16,27 @@ const server = app.listen(port, () => { console.error("Expected 'Hello World!', got", text); process.exit(1); } + + // https://github.com/oven-sh/bun/issues/11739 + if (textFile !== "hello hello\ncopyright symbols: ©\nMy UTF-16 string is 😀") { + console.log("Expected 'hello hello\ncopyright symbols: ©\nMy UTF-16 string is 😀', got", textFile); + process.exit(1); + } + + // https://github.com/oven-sh/bun/issues/11739 + if (json[String.fromCharCode(169)] !== "©") { + console.log("Copyright", json[String.fromCharCode(169)]); + console.log("json has an encoding issue.", json); + process.exit(1); + } + + // https://github.com/oven-sh/bun/issues/11739 + if (json[String.fromCodePoint(128512)] !== "😀") { + console.log("Smiley", json[String.fromCodePoint(128512)]); + console.log("json has an encoding issue.", json); + process.exit(1); + } + console.log("OK"); process.exit(0); }); diff --git a/test/js/third_party/body-parser/package.json b/test/js/third_party/body-parser/package.json index 0dfa98c593..3efb3d84ad 100644 --- a/test/js/third_party/body-parser/package.json +++ b/test/js/third_party/body-parser/package.json @@ -1,6 +1,11 @@ { "name": "body-parser-test", "version": "1.0.0", + "© test latin1 copyright symbols": "©", + "©": "©", + "test utf16 smiley": "😀", + "smile 😀": "😀", + "😀": "😀", "dependencies": { "express": "4.18.2", "body-parser": "1.20.1", diff --git a/test/js/third_party/body-parser/text.txt b/test/js/third_party/body-parser/text.txt new file mode 100644 index 0000000000..dc35d2e18e --- /dev/null +++ b/test/js/third_party/body-parser/text.txt @@ -0,0 +1,3 @@ +hello hello +copyright symbols: © +My UTF-16 string is 😀 \ No newline at end of file