From 51ce3bc269495afc6145c33bb67230cdd5342f6e Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 25 Sep 2025 18:03:22 -0800 Subject: [PATCH] [publish images] ci: ensure tests that require docker have it available (#22781) --- cmake/targets/BuildBun.cmake | 1 + scripts/bootstrap.sh | 13 +++++++-- scripts/utils.mjs | 6 ++++ src/bun.js/api/YAMLObject.zig | 2 +- src/bun.js/api/bun/udp_socket.zig | 2 +- src/bun.js/api/server.zig | 5 +--- src/bun.js/api/server/ServerWebSocket.zig | 22 ++++---------- src/bun.js/bindings/CatchScope.zig | 2 +- src/bun.js/bindings/JSValue.zig | 23 +++------------ src/bun.js/bindings/SQLClient.cpp | 26 +++++++++-------- src/bun.js/bindings/bindings.cpp | 9 ++---- src/bun.js/node/util/parse_args.zig | 6 ++-- src/bun.js/test/expect/toThrow.zig | 2 +- src/bun.js/webcore/Sink.zig | 5 +--- src/codegen/cppbind.ts | 34 +++++++++++++--------- src/codegen/shared-types.ts | 1 + src/dns.zig | 8 ++--- src/sql/mysql/js/JSMySQLConnection.zig | 4 +-- src/sql/mysql/protocol/ResultSet.zig | 4 +-- src/sql/postgres/DataCell.zig | 4 +-- src/sql/postgres/PostgresSQLConnection.zig | 2 +- src/sql/shared/SQLDataCell.zig | 26 +++++++++++++++++ src/valkey/js_valkey_functions.zig | 7 +---- src/valkey/valkey_protocol.zig | 2 +- test/cli/install/bun-install-proxy.test.ts | 4 +-- test/harness.ts | 6 ++++ test/internal/ban-limits.json | 4 +-- test/js/bun/s3/s3.test.ts | 6 ++-- test/js/bun/symbols.test.ts | 2 +- test/js/sql/local-sql.test.ts | 12 ++++---- test/js/valkey/test-utils.ts | 4 +-- 31 files changed, 135 insertions(+), 119 deletions(-) diff --git a/cmake/targets/BuildBun.cmake b/cmake/targets/BuildBun.cmake index ac6104c398..5e8795c1bd 100644 --- a/cmake/targets/BuildBun.cmake +++ b/cmake/targets/BuildBun.cmake @@ -1011,6 +1011,7 @@ if(LINUX) -Wl,--wrap=exp2 -Wl,--wrap=expf -Wl,--wrap=fcntl64 + -Wl,--wrap=gettid -Wl,--wrap=log -Wl,--wrap=log2 -Wl,--wrap=log2f diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index a72f614a28..3537285e05 100755 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -1,5 +1,5 @@ #!/bin/sh -# Version: 18 +# Version: 19 # A script that installs the dependencies needed to build and test Bun. # This should work on macOS and Linux with a POSIX shell. @@ -685,6 +685,8 @@ install_common_software() { apt-transport-https \ software-properties-common fi + install_packages \ + libc6-dbg ;; dnf) install_packages \ @@ -1193,7 +1195,7 @@ install_docker() { execute_sudo amazon-linux-extras install docker ;; amzn-* | alpine-*) - install_packages docker + install_packages docker docker-cli-compose ;; *) sh="$(require sh)" @@ -1208,10 +1210,17 @@ install_docker() { if [ -f "$systemctl" ]; then execute_sudo "$systemctl" enable docker fi + if [ "$os" = "linux" ] && [ "$distro" = "alpine" ]; then + execute doas rc-update add docker default + execute doas rc-service docker start + fi getent="$(which getent)" if [ -n "$("$getent" group docker)" ]; then usermod="$(which usermod)" + if [ -z "$usermod" ]; then + usermod="$(sudo which usermod)" + fi if [ -f "$usermod" ]; then execute_sudo "$usermod" -aG docker "$user" fi diff --git a/scripts/utils.mjs b/scripts/utils.mjs index cc7b205150..604227f9cd 100755 --- a/scripts/utils.mjs +++ b/scripts/utils.mjs @@ -2866,6 +2866,12 @@ export function printEnvironment() { spawnSync([shell, "-c", "free -m -w"], { stdio: "inherit" }); } }); + startGroup("Docker", () => { + const shell = which(["sh", "bash"]); + if (shell) { + spawnSync([shell, "-c", "docker ps"], { stdio: "inherit" }); + } + }); } if (isWindows) { startGroup("Disk (win)", () => { diff --git a/src/bun.js/api/YAMLObject.zig b/src/bun.js/api/YAMLObject.zig index ad5e5d8da9..3d18dfa4ec 100644 --- a/src/bun.js/api/YAMLObject.zig +++ b/src/bun.js/api/YAMLObject.zig @@ -1030,7 +1030,7 @@ const ParserCtx = struct { const key_str = try key.toBunString(ctx.global); defer key_str.deref(); - obj.putMayBeIndex(ctx.global, &key_str, value); + try obj.putMayBeIndex(ctx.global, &key_str, value); } return obj; diff --git a/src/bun.js/api/bun/udp_socket.zig b/src/bun.js/api/bun/udp_socket.zig index e34eadfa88..1d0a78a14a 100644 --- a/src/bun.js/api/bun/udp_socket.zig +++ b/src/bun.js/api/bun/udp_socket.zig @@ -625,7 +625,7 @@ pub const UDPSocket = struct { if (val.asArrayBuffer(globalThis)) |arrayBuffer| { break :brk arrayBuffer.slice(); } else if (val.isString()) { - break :brk val.toString(globalThis).toSlice(globalThis, alloc).slice(); + break :brk (try val.toJSString(globalThis)).toSlice(globalThis, alloc).slice(); } else { return globalThis.throwInvalidArguments("Expected ArrayBufferView or string as payload", .{}); } diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 0ea9ff488b..aa17c485e1 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -719,10 +719,7 @@ pub fn NewServer(protocol_enum: enum { http, https }, development_kind: enum { d } { - var js_string = message_value.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + var js_string = try message_value.toJSString(globalThis); const view = js_string.view(globalThis); const slice = view.toSlice(bun.default_allocator); defer slice.deinit(); diff --git a/src/bun.js/api/server/ServerWebSocket.zig b/src/bun.js/api/server/ServerWebSocket.zig index ffd5bec449..a244b2dc29 100644 --- a/src/bun.js/api/server/ServerWebSocket.zig +++ b/src/bun.js/api/server/ServerWebSocket.zig @@ -438,10 +438,7 @@ pub fn publish( } { - var js_string = message_value.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + var js_string = try message_value.toJSString(globalThis); const view = js_string.view(globalThis); const slice = view.toSlice(bun.default_allocator); defer slice.deinit(); @@ -505,10 +502,7 @@ pub fn publishText( return globalThis.throw("publishText requires a non-empty message", .{}); } - var js_string = message_value.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + var js_string = try message_value.toJSString(globalThis); const view = js_string.view(globalThis); const slice = view.toSlice(bun.default_allocator); defer slice.deinit(); @@ -756,10 +750,7 @@ pub fn send( } { - var js_string = message_value.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + var js_string = try message_value.toJSString(globalThis); const view = js_string.view(globalThis); const slice = view.toSlice(bun.default_allocator); defer slice.deinit(); @@ -814,10 +805,7 @@ pub fn sendText( return globalThis.throw("sendText expects a string", .{}); } - var js_string = message_value.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + var js_string = try message_value.toJSString(globalThis); const view = js_string.view(globalThis); const slice = view.toSlice(bun.default_allocator); defer slice.deinit(); @@ -997,7 +985,7 @@ inline fn sendPing( }, } } else if (value.isString()) { - var string_value = value.toString(globalThis).toSlice(globalThis, bun.default_allocator); + var string_value = (try value.toJSString(globalThis)).toSlice(globalThis, bun.default_allocator); defer string_value.deinit(); const buffer = string_value.slice(); diff --git a/src/bun.js/bindings/CatchScope.zig b/src/bun.js/bindings/CatchScope.zig index 22c6014159..d20f42cc28 100644 --- a/src/bun.js/bindings/CatchScope.zig +++ b/src/bun.js/bindings/CatchScope.zig @@ -77,7 +77,7 @@ pub const CatchScope = struct { /// Intended for use with `try`. Returns if there is already a pending exception or if traps cause /// an exception to be thrown (this is the same as how RETURN_IF_EXCEPTION behaves in C++) - pub fn returnIfException(self: *CatchScope) bun.JSError!void { + pub fn returnIfException(self: *CatchScope) !void { if (self.exceptionIncludingTraps() != null) return error.JSError; } diff --git a/src/bun.js/bindings/JSValue.zig b/src/bun.js/bindings/JSValue.zig index a6cc5029e8..f8a54edcd2 100644 --- a/src/bun.js/bindings/JSValue.zig +++ b/src/bun.js/bindings/JSValue.zig @@ -347,12 +347,11 @@ pub const JSValue = enum(i64) { @compileError("Unsupported key type in put(). Expected ZigString or bun.String, got " ++ @typeName(Key)); } } - extern fn JSC__JSValue__putMayBeIndex(target: JSValue, globalObject: *JSGlobalObject, key: *const String, value: jsc.JSValue) void; /// Note: key can't be numeric (if so, use putMayBeIndex instead) /// Same as `.put` but accepts both non-numeric and numeric keys. /// Prefer to use `.put` if the key is guaranteed to be non-numeric (e.g. known at comptime) - pub inline fn putMayBeIndex(this: JSValue, globalObject: *JSGlobalObject, key: *const String, value: JSValue) void { - JSC__JSValue__putMayBeIndex(this, globalObject, key, value); + pub fn putMayBeIndex(this: JSValue, globalObject: *JSGlobalObject, key: *const String, value: JSValue) bun.JSError!void { + return bun.cpp.JSC__JSValue__putMayBeIndex(this, globalObject, key, value); } extern fn JSC__JSValue__putToPropertyKey(target: JSValue, globalObject: *JSGlobalObject, key: jsc.JSValue, value: jsc.JSValue) void; @@ -1191,10 +1190,8 @@ pub const JSValue = enum(i64) { return getZigString(this, global).toSliceZ(allocator); } - extern fn JSC__JSValue__toString(this: JSValue, globalThis: *JSGlobalObject) *JSString; - /// On exception, this returns the empty string. - pub fn toString(this: JSValue, globalThis: *JSGlobalObject) *JSString { - return JSC__JSValue__toString(this, globalThis); + pub fn toJSString(this: JSValue, globalThis: *JSGlobalObject) bun.JSError!*JSString { + return bun.cpp.JSC__JSValue__toStringOrNull(this, globalThis); } extern fn JSC__JSValue__jsonStringify(this: JSValue, globalThis: *JSGlobalObject, indent: u32, out: *bun.String) void; @@ -1202,17 +1199,6 @@ pub const JSValue = enum(i64) { return bun.jsc.fromJSHostCallGeneric(globalThis, @src(), JSC__JSValue__jsonStringify, .{ this, globalThis, indent, out }); } - extern fn JSC__JSValue__toStringOrNull(this: JSValue, globalThis: *JSGlobalObject) ?*JSString; - // Calls JSValue::toStringOrNull. Returns error on exception. - pub fn toJSString(this: JSValue, globalThis: *JSGlobalObject) bun.JSError!*JSString { - var scope: ExceptionValidationScope = undefined; - scope.init(globalThis, @src()); - defer scope.deinit(); - const maybe_string = JSC__JSValue__toStringOrNull(this, globalThis); - scope.assertExceptionPresenceMatches(maybe_string == null); - return maybe_string orelse error.JSError; - } - /// Call `toString()` on the JSValue and clone the result. pub fn toSliceOrNull(this: JSValue, globalThis: *JSGlobalObject) bun.JSError!ZigString.Slice { const str = try bun.String.fromJS(this, globalThis); @@ -2424,7 +2410,6 @@ const ArrayBuffer = jsc.ArrayBuffer; const C_API = bun.jsc.C; const CatchScope = jsc.CatchScope; const DOMURL = jsc.DOMURL; -const ExceptionValidationScope = jsc.ExceptionValidationScope; const JSArrayIterator = jsc.JSArrayIterator; const JSCell = jsc.JSCell; const JSGlobalObject = jsc.JSGlobalObject; diff --git a/src/bun.js/bindings/SQLClient.cpp b/src/bun.js/bindings/SQLClient.cpp index c9c8f41313..00c08ab91d 100644 --- a/src/bun.js/bindings/SQLClient.cpp +++ b/src/bun.js/bindings/SQLClient.cpp @@ -161,10 +161,10 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel return jsNumber(cell.value.unsigned_integer); break; case DataCellTag::Bigint: - return JSC::JSBigInt::createFrom(globalObject, cell.value.bigint); + RELEASE_AND_RETURN(scope, JSC::JSBigInt::createFrom(globalObject, cell.value.bigint)); break; case DataCellTag::UnsignedBigint: - return JSC::JSBigInt::createFrom(globalObject, cell.value.unsigned_bigint); + RELEASE_AND_RETURN(scope, JSC::JSBigInt::createFrom(globalObject, cell.value.unsigned_bigint)); break; case DataCellTag::Boolean: return jsBoolean(cell.value.boolean); @@ -189,6 +189,7 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel if (cell.value.json) { auto str = WTF::String(cell.value.json); JSC::JSValue json = JSC::JSONParse(globalObject, str); + RETURN_IF_EXCEPTION(scope, {}); return json; } return jsNull(); @@ -198,14 +199,10 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel uint32_t length = cell.value.array.length; for (uint32_t i = 0; i < length; i++) { JSValue result = toJS(vm, globalObject, cell.value.array.cells[i]); - if (result.isEmpty()) [[unlikely]] { - return {}; - } - + RETURN_IF_EXCEPTION(scope, {}); args.append(result); } - - return JSC::constructArray(globalObject, static_cast(nullptr), args); + RELEASE_AND_RETURN(scope, JSC::constructArray(globalObject, static_cast(nullptr), args)); } case DataCellTag::TypedArray: { JSC::JSType type = static_cast(cell.value.typed_array.type); @@ -343,6 +340,7 @@ static JSC::JSValue toJS(JSC::Structure* structure, DataCell* cells, uint32_t co // -> { "8": 1, "2": 2, "3": 3 } // 8 > count object->putDirectIndex(globalObject, cell.index, value); + RETURN_IF_EXCEPTION(scope, {}); } } else { uint32_t structureOffsetIndex = 0; @@ -356,6 +354,7 @@ static JSC::JSValue toJS(JSC::Structure* structure, DataCell* cells, uint32_t co ASSERT(!cell.isNamedColumn()); ASSERT(!cell.isDuplicateColumn()); object->putDirectIndex(globalObject, cell.index, value); + RETURN_IF_EXCEPTION(scope, {}); } else if (cell.isNamedColumn()) { JSValue value = toJS(vm, globalObject, cell); RETURN_IF_EXCEPTION(scope, {}); @@ -387,6 +386,7 @@ static JSC::JSValue toJS(JSC::Structure* structure, DataCell* cells, uint32_t co JSValue value = toJS(vm, globalObject, cell); RETURN_IF_EXCEPTION(scope, {}); array->putDirectIndex(globalObject, i, value); + RETURN_IF_EXCEPTION(scope, {}); } return array; } @@ -399,20 +399,22 @@ static JSC::JSValue toJS(JSC::Structure* structure, DataCell* cells, uint32_t co } static JSC::JSValue toJS(JSC::JSArray* array, JSC::Structure* structure, DataCell* cells, uint32_t count, JSC::JSGlobalObject* globalObject, Bun::BunStructureFlags flags, BunResultMode result_mode, ExternColumnIdentifier* namesPtr, uint32_t namesCount) { + auto& vm = JSC::getVM(globalObject); + auto scope = DECLARE_THROW_SCOPE(vm); JSValue value = toJS(structure, cells, count, globalObject, flags, result_mode, namesPtr, namesCount); - if (value.isEmpty()) - return {}; + RETURN_IF_EXCEPTION(scope, {}); if (array) { array->push(globalObject, value); + RETURN_IF_EXCEPTION(scope, {}); return array; } auto* newArray = JSC::constructEmptyArray(globalObject, static_cast(nullptr), 1); - if (!newArray) - return {}; + RETURN_IF_EXCEPTION(scope, {}); newArray->putDirectIndex(globalObject, 0, value); + RETURN_IF_EXCEPTION(scope, {}); return newArray; } diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index fd10d44f99..496c5e14f8 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -3713,7 +3713,7 @@ void JSC__JSValue__putToPropertyKey(JSC::EncodedJSValue JSValue0, JSC::JSGlobalO object->putDirectMayBeIndex(arg1, pkey, value); } -extern "C" void JSC__JSValue__putMayBeIndex(JSC::EncodedJSValue target, JSC::JSGlobalObject* globalObject, const BunString* key, JSC::EncodedJSValue value) +extern "C" [[ZIG_EXPORT(check_slow)]] void JSC__JSValue__putMayBeIndex(JSC::EncodedJSValue target, JSC::JSGlobalObject* globalObject, const BunString* key, JSC::EncodedJSValue value) { auto& vm = JSC::getVM(globalObject); ThrowScope scope = DECLARE_THROW_SCOPE(vm); @@ -4346,12 +4346,7 @@ JSC::JSObject* JSC__JSValue__toObject(JSC::EncodedJSValue JSValue0, JSC::JSGloba return value.toObject(arg1); } -JSC::JSString* JSC__JSValue__toString(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) -{ - JSC::JSValue value = JSC::JSValue::decode(JSValue0); - return value.toString(arg1); -}; -JSC::JSString* JSC__JSValue__toStringOrNull(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) +[[ZIG_EXPORT(null_is_throw)]] JSC::JSString* JSC__JSValue__toStringOrNull(JSC::EncodedJSValue JSValue0, JSC::JSGlobalObject* arg1) { JSC::JSValue value = JSC::JSValue::decode(JSValue0); return value.toStringOrNull(arg1); diff --git a/src/bun.js/node/util/parse_args.zig b/src/bun.js/node/util/parse_args.zig index 37513e185e..5904dff0aa 100644 --- a/src/bun.js/node/util/parse_args.zig +++ b/src/bun.js/node/util/parse_args.zig @@ -272,10 +272,10 @@ fn storeOption(globalThis: *JSGlobalObject, option_name: ValueRef, option_value: } else { var value_list = try JSValue.createEmptyArray(globalThis, 1); try value_list.putIndex(globalThis, 0, new_value); - values.putMayBeIndex(globalThis, &key, value_list); + try values.putMayBeIndex(globalThis, &key, value_list); } } else { - values.putMayBeIndex(globalThis, &key, new_value); + try values.putMayBeIndex(globalThis, &key, new_value); } } @@ -723,7 +723,7 @@ pub fn parseArgs(globalThis: *JSGlobalObject, callframe: *jsc.CallFrame) bun.JSE if (!option.long_name.eqlComptime("__proto__")) { if (try state.values.getOwn(globalThis, option.long_name) == null) { log(" Setting \"{}\" to default value", .{option.long_name}); - state.values.putMayBeIndex(globalThis, &option.long_name, default_value); + try state.values.putMayBeIndex(globalThis, &option.long_name, default_value); } } } diff --git a/src/bun.js/test/expect/toThrow.zig b/src/bun.js/test/expect/toThrow.zig index fda78e44b7..0fd4e1f28c 100644 --- a/src/bun.js/test/expect/toThrow.zig +++ b/src/bun.js/test/expect/toThrow.zig @@ -23,7 +23,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame } } else if (value.isString()) { // `.toThrow("") behaves the same as `.toThrow()` - const s = value.toString(globalThis); + const s = try value.toJSString(globalThis); if (s.length() == 0) break :brk .zero; } break :brk value; diff --git a/src/bun.js/webcore/Sink.zig b/src/bun.js/webcore/Sink.zig index f26a613feb..6ad9e07c9a 100644 --- a/src/bun.js/webcore/Sink.zig +++ b/src/bun.js/webcore/Sink.zig @@ -403,10 +403,7 @@ pub fn JSSink(comptime SinkType: type, comptime abi_name: []const u8) type { return globalThis.throwValue(globalThis.toTypeError(.INVALID_ARG_TYPE, "write() expects a string, ArrayBufferView, or ArrayBuffer", .{})); } - const str = arg.toString(globalThis); - if (globalThis.hasException()) { - return .zero; - } + const str = try arg.toJSString(globalThis); const view = str.view(globalThis); diff --git a/src/codegen/cppbind.ts b/src/codegen/cppbind.ts index ba01d3bcad..c269256cbe 100644 --- a/src/codegen/cppbind.ts +++ b/src/codegen/cppbind.ts @@ -45,8 +45,8 @@ To run manually: - **[[ZIG_NONNULL]]** - Mark pointer parameters as non-nullable: ```cpp - [[ZIG_EXPORT(nothrow)]] void process([[ZIG_NONNULL]] JSGlobalObject* globalThis, - [[ZIG_NONNULL]] JSValue* values, + [[ZIG_EXPORT(nothrow)]] void process([[ZIG_NONNULL]] JSGlobalObject* globalThis, + [[ZIG_NONNULL]] JSValue* values, size_t count) { ... } ``` Generates: `pub extern fn process(globalThis: *jsc.JSGlobalObject, values: [*]const jsc.JSValue) void;` @@ -397,7 +397,7 @@ function processFunction(ctx: ParseContext, node: SyntaxNode, tag: ExportTag): C }; } -type ExportTag = "check_slow" | "zero_is_throw" | "false_is_throw" | "nothrow"; +type ExportTag = "check_slow" | "zero_is_throw" | "false_is_throw" | "null_is_throw" | "nothrow"; const sharedTypesText = await Bun.file("src/codegen/shared-types.ts").text(); const sharedTypesLines = sharedTypesText.split("\n"); @@ -570,7 +570,8 @@ async function processFile(parser: CppParser, file: string, allFunctions: CppFn[ tagStr === "nothrow" || tagStr === "zero_is_throw" || tagStr === "check_slow" || - tagStr === "false_is_throw" + tagStr === "false_is_throw" || + tagStr === "null_is_throw" ) { tag = tagStr; } else if (tagStr === "print") { @@ -580,7 +581,7 @@ async function processFile(parser: CppParser, file: string, allFunctions: CppFn[ } else { appendError( nodePosition(tagIdentifier, ctx), - "tag must be nothrow, zero_is_throw, check_slow, or false_is_throw: " + tagStr, + "tag must be nothrow, zero_is_throw, check_slow, false_is_throw, or null_is_throw: " + tagStr, ); tag = "nothrow"; } @@ -639,7 +640,7 @@ function generateZigFn( resultSourceLinks: string[], cfg: Cfg, ): void { - const returnType = generateZigType(fn.returnType, null); + let returnType = generateZigType(fn.returnType, null); if (resultBindings.length) resultBindings.push(""); resultBindings.push(generateZigSourceComment(cfg, resultSourceLinks, fn)); if (fn.tag === "nothrow") { @@ -667,7 +668,7 @@ function generateZigFn( ); } resultBindings.push( - `pub inline fn ${formatZigName(fn.name)}(${generateZigParameterList(fn.parameters, globalThisArg)}) bun.JSError!${returnType} {`, + `pub fn ${formatZigName(fn.name)}(${generateZigParameterList(fn.parameters, globalThisArg)}) error{JSError}!${returnType} {`, ` if (comptime Environment.ci_assert) {`, ` var scope: jsc.CatchScope = undefined;`, ` scope.init(${formatZigName(globalThisArg.name)}, @src());`, @@ -697,9 +698,16 @@ function generateZigFn( if (returnType !== "bool") { appendError(fn.position, "ZIG_EXPORT(false_is_throw) is only allowed for functions that return bool"); } + returnType = "void"; + } else if (fn.tag === "null_is_throw") { + equalsValue = "null"; + if (!returnType.startsWith("?*")) { + appendError(fn.position, "ZIG_EXPORT(null_is_throw) is only allowed for functions that return optional pointer"); + } + returnType = returnType.slice(1); } else assertNever(fn.tag); resultBindings.push( - `pub inline fn ${formatZigName(fn.name)}(${generateZigParameterList(fn.parameters, globalThisArg)}) bun.JSError!${fn.tag === "false_is_throw" ? "void" : returnType} {`, + `pub fn ${formatZigName(fn.name)}(${generateZigParameterList(fn.parameters, globalThisArg)}) error{JSError}!${returnType} {`, ` if (comptime Environment.ci_assert) {`, ` var scope: jsc.ExceptionValidationScope = undefined;`, ` scope.init(${formatZigName(globalThisArg.name)}, @src());`, @@ -707,11 +715,11 @@ function generateZigFn( ``, ` const value = raw.${formatZigName(fn.name)}(${fn.parameters.map(p => formatZigName(p.name)).join(", ")});`, ` scope.assertExceptionPresenceMatches(value == ${equalsValue});`, - ` return if (value == ${equalsValue}) error.JSError ${fn.tag === "false_is_throw" ? "" : "else value"};`, + ` return if (value == ${equalsValue}) error.JSError ${fn.tag === "false_is_throw" ? "" : "else value"}${fn.tag === "null_is_throw" ? ".?" : ""};`, ` } else {`, ` const value = raw.${formatZigName(fn.name)}(${fn.parameters.map(p => formatZigName(p.name)).join(", ")});`, ` if (value == ${equalsValue}) return error.JSError;`, - ...(fn.tag === "false_is_throw" ? [] : [` return value;`]), + ...(fn.tag === "false_is_throw" ? [] : [` return value${fn.tag === "null_is_throw" ? ".?" : ""};`]), ` }`, `}`, ); @@ -733,14 +741,14 @@ async function main() { if (!dstDir) { console.error( String.raw` - _ _ _ + _ _ _ | | (_) | | ___ _ __ _ __ | |__ _ _ __ __| | / __| '_ \| '_ \| '_ \| | '_ \ / _' | | (__| |_) | |_) | |_) | | | | | (_| | \___| .__/| .__/|_.__/|_|_| |_|\__,_| - | | | | - |_| |_| + | | | | + |_| |_| `.slice(1), ); console.error("Usage: bun src/codegen/cppbind src build/debug/codegen"); diff --git a/src/codegen/shared-types.ts b/src/codegen/shared-types.ts index ec2b2b6e24..9611a3983d 100644 --- a/src/codegen/shared-types.ts +++ b/src/codegen/shared-types.ts @@ -56,6 +56,7 @@ export const sharedTypes: Record = { "JSC::SourceProvider": "bun.jsc.SourceProvider", "JSC::CallFrame": "bun.jsc.CallFrame", "JSC::JSObject": "bun.jsc.JSObject", + "JSC::JSString": "bun.jsc.JSString", }; export const bannedTypes: Record = { diff --git a/src/dns.zig b/src/dns.zig index e3bc939830..0875024c4c 100644 --- a/src/dns.zig +++ b/src/dns.zig @@ -150,7 +150,7 @@ pub const GetAddrInfo = struct { if (value.isString()) { return try map.fromJS(globalObject, value) orelse { - if (value.toString(globalObject).length() == 0) { + if ((try value.toJSString(globalObject)).length() == 0) { return .unspecified; } @@ -211,7 +211,7 @@ pub const GetAddrInfo = struct { if (value.isString()) { return try map.fromJS(globalObject, value) orelse { - if (value.toString(globalObject).length() == 0) + if ((try value.toJSString(globalObject)).length() == 0) return .unspecified; return error.InvalidSocketType; @@ -251,7 +251,7 @@ pub const GetAddrInfo = struct { if (value.isString()) { return try map.fromJS(globalObject, value) orelse { - const str = value.toString(globalObject); + const str = try value.toJSString(globalObject); if (str.length() == 0) return .unspecified; @@ -301,7 +301,7 @@ pub const GetAddrInfo = struct { if (value.isString()) { return try label.fromJS(globalObject, value) orelse { - if (value.toString(globalObject).length() == 0) { + if ((try value.toJSString(globalObject)).length() == 0) { return default; } diff --git a/src/sql/mysql/js/JSMySQLConnection.zig b/src/sql/mysql/js/JSMySQLConnection.zig index b1b52c0608..92b31c1b5d 100644 --- a/src/sql/mysql/js/JSMySQLConnection.zig +++ b/src/sql/mysql/js/JSMySQLConnection.zig @@ -667,7 +667,7 @@ pub fn onConnectionEstabilished(this: *@This()) void { pub fn onQueryResult(this: *@This(), request: *JSMySQLQuery, result: MySQLQueryResult) void { request.resolve(this.getQueriesArray(), result); } -pub fn onResultRow(this: *@This(), request: *JSMySQLQuery, statement: *MySQLStatement, Context: type, reader: NewReader(Context)) error{ShortRead}!void { +pub fn onResultRow(this: *@This(), request: *JSMySQLQuery, statement: *MySQLStatement, Context: type, reader: NewReader(Context)) (error{ ShortRead, JSError })!void { const result_mode = request.getResultMode(); var stack_fallback = std.heap.stackFallback(4096, bun.default_allocator); const allocator = stack_fallback.get(); @@ -700,7 +700,7 @@ pub fn onResultRow(this: *@This(), request: *JSMySQLQuery, statement: *MySQLStat }; const pending_value = request.getPendingValue() orelse .js_undefined; // Process row data - const row_value = row.toJS( + const row_value = try row.toJS( this.#globalObject, pending_value, structure, diff --git a/src/sql/mysql/protocol/ResultSet.zig b/src/sql/mysql/protocol/ResultSet.zig index d5a06d117f..1d49650869 100644 --- a/src/sql/mysql/protocol/ResultSet.zig +++ b/src/sql/mysql/protocol/ResultSet.zig @@ -8,7 +8,7 @@ pub const Row = struct { bigint: bool = false, globalObject: *jsc.JSGlobalObject, - pub fn toJS(this: *Row, globalObject: *jsc.JSGlobalObject, array: JSValue, structure: JSValue, flags: SQLDataCell.Flags, result_mode: SQLQueryResultMode, cached_structure: ?CachedStructure) JSValue { + pub fn toJS(this: *Row, globalObject: *jsc.JSGlobalObject, array: JSValue, structure: JSValue, flags: SQLDataCell.Flags, result_mode: SQLQueryResultMode, cached_structure: ?CachedStructure) !JSValue { var names: ?[*]jsc.JSObject.ExternColumnIdentifier = null; var names_count: u32 = 0; if (cached_structure) |c| { @@ -18,7 +18,7 @@ pub const Row = struct { } } - return SQLDataCell.JSC__constructObjectFromDataCell( + return SQLDataCell.constructObjectFromDataCell( globalObject, array, structure, diff --git a/src/sql/postgres/DataCell.zig b/src/sql/postgres/DataCell.zig index 98a721a735..74f32034f3 100644 --- a/src/sql/postgres/DataCell.zig +++ b/src/sql/postgres/DataCell.zig @@ -906,7 +906,7 @@ pub const Putter = struct { count: usize = 0, globalObject: *jsc.JSGlobalObject, - pub fn toJS(this: *Putter, globalObject: *jsc.JSGlobalObject, array: JSValue, structure: JSValue, flags: SQLDataCell.Flags, result_mode: PostgresSQLQueryResultMode, cached_structure: ?PostgresCachedStructure) JSValue { + pub fn toJS(this: *Putter, globalObject: *jsc.JSGlobalObject, array: JSValue, structure: JSValue, flags: SQLDataCell.Flags, result_mode: PostgresSQLQueryResultMode, cached_structure: ?PostgresCachedStructure) !JSValue { var names: ?[*]jsc.JSObject.ExternColumnIdentifier = null; var names_count: u32 = 0; if (cached_structure) |c| { @@ -916,7 +916,7 @@ pub const Putter = struct { } } - return SQLDataCell.JSC__constructObjectFromDataCell( + return SQLDataCell.constructObjectFromDataCell( globalObject, array, structure, diff --git a/src/sql/postgres/PostgresSQLConnection.zig b/src/sql/postgres/PostgresSQLConnection.zig index 0d2445a5ff..e176281713 100644 --- a/src/sql/postgres/PostgresSQLConnection.zig +++ b/src/sql/postgres/PostgresSQLConnection.zig @@ -1429,7 +1429,7 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera }; const pending_value = PostgresSQLQuery.js.pendingValueGetCached(thisValue) orelse .zero; pending_value.ensureStillAlive(); - const result = putter.toJS( + const result = try putter.toJS( this.globalObject, pending_value, structure, diff --git a/src/sql/shared/SQLDataCell.zig b/src/sql/shared/SQLDataCell.zig index 1cf73d6edb..059ba6e71d 100644 --- a/src/sql/shared/SQLDataCell.zig +++ b/src/sql/shared/SQLDataCell.zig @@ -141,6 +141,32 @@ pub const SQLDataCell = extern struct { _: u29 = 0, }; + // TODO: cppbind isn't yet able to detect slice parameters when the next is uint32_t + pub fn constructObjectFromDataCell( + globalObject: *jsc.JSGlobalObject, + encodedArrayValue: jsc.JSValue, + encodedStructureValue: jsc.JSValue, + cells: [*]SQLDataCell, + count: u32, + flags: SQLDataCell.Flags, + result_mode: u8, + namesPtr: ?[*]bun.jsc.JSObject.ExternColumnIdentifier, + namesCount: u32, + ) !jsc.JSValue { + if (comptime bun.Environment.ci_assert) { + var scope: jsc.ExceptionValidationScope = undefined; + scope.init(globalObject, @src()); + defer scope.deinit(); + const value = JSC__constructObjectFromDataCell(globalObject, encodedArrayValue, encodedStructureValue, cells, count, flags, result_mode, namesPtr, namesCount); + scope.assertExceptionPresenceMatches(value == .zero); + return if (value == .zero) error.JSError else value; + } else { + const value = JSC__constructObjectFromDataCell(globalObject, encodedArrayValue, encodedStructureValue, cells, count, flags, result_mode, namesPtr, namesCount); + if (value == .zero) return error.JSError; + return value; + } + } + pub extern fn JSC__constructObjectFromDataCell( *jsc.JSGlobalObject, JSValue, diff --git a/src/valkey/js_valkey_functions.zig b/src/valkey/js_valkey_functions.zig index 79cb990e92..4b6d2daad6 100644 --- a/src/valkey/js_valkey_functions.zig +++ b/src/valkey/js_valkey_functions.zig @@ -833,12 +833,7 @@ fn fromJS(globalObject: *jsc.JSGlobalObject, value: JSValue) !?JSArgument { if (value.isNumber()) { // Allow numbers to be passed as strings. - const str = value.toString(globalObject); - if (globalObject.hasException()) { - @branchHint(.unlikely); - return error.JSError; - } - + const str = try value.toJSString(globalObject); return try JSArgument.fromJSMaybeFile(globalObject, bun.default_allocator, str.toJS(), true); } diff --git a/src/valkey/valkey_protocol.zig b/src/valkey/valkey_protocol.zig index 39e27dd2dc..a02514f7c6 100644 --- a/src/valkey/valkey_protocol.zig +++ b/src/valkey/valkey_protocol.zig @@ -288,7 +288,7 @@ pub const RESPValue = union(RESPType) { defer key_str.deref(); const js_value = try entry.value.toJSWithOptions(globalObject, options); - js_obj.putMayBeIndex(globalObject, &key_str, js_value); + try js_obj.putMayBeIndex(globalObject, &key_str, js_value); } return js_obj; }, diff --git a/test/cli/install/bun-install-proxy.test.ts b/test/cli/install/bun-install-proxy.test.ts index 85acc8711c..40672217c0 100644 --- a/test/cli/install/bun-install-proxy.test.ts +++ b/test/cli/install/bun-install-proxy.test.ts @@ -1,11 +1,11 @@ import { beforeAll, it } from "bun:test"; import { exec } from "child_process"; import { rm } from "fs/promises"; -import { bunEnv, bunExe, isDockerEnabled, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, dockerExe, isDockerEnabled, tempDirWithFiles } from "harness"; import { join } from "path"; import { promisify } from "util"; const execAsync = promisify(exec); -const dockerCLI = Bun.which("docker") as string; +const dockerCLI = dockerExe() as string; const SQUID_URL = "http://127.0.0.1:3128"; if (isDockerEnabled()) { beforeAll(async () => { diff --git a/test/harness.ts b/test/harness.ts index c0d2ac7b8f..9b46505c57 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -860,6 +860,9 @@ export function dockerExe(): string | null { export function isDockerEnabled(): boolean { const dockerCLI = dockerExe(); if (!dockerCLI) { + if (isCI && isLinux) { + throw new Error("A functional `docker` is required in CI for some tests."); + } return false; } @@ -872,6 +875,9 @@ export function isDockerEnabled(): boolean { const info = execSync(`"${dockerCLI}" info`, { stdio: ["ignore", "pipe", "inherit"] }); return info.toString().indexOf("Server Version:") !== -1; } catch { + if (isCI && isLinux) { + throw new Error("A functional `docker` is required in CI for some tests."); + } return false; } } diff --git a/test/internal/ban-limits.json b/test/internal/ban-limits.json index 6980821328..bee82aa703 100644 --- a/test/internal/ban-limits.json +++ b/test/internal/ban-limits.json @@ -22,8 +22,8 @@ "allocator.ptr !=": 1, "allocator.ptr ==": 0, "global.hasException": 28, - "globalObject.hasException": 48, - "globalThis.hasException": 133, + "globalObject.hasException": 47, + "globalThis.hasException": 127, "std.StringArrayHashMap(": 1, "std.StringArrayHashMapUnmanaged(": 11, "std.StringHashMap(": 0, diff --git a/test/js/bun/s3/s3.test.ts b/test/js/bun/s3/s3.test.ts index 6de01baacc..8c8034785a 100644 --- a/test/js/bun/s3/s3.test.ts +++ b/test/js/bun/s3/s3.test.ts @@ -1,9 +1,9 @@ import type { S3Options } from "bun"; -import { S3Client, s3 as defaultS3, file, randomUUIDv7, which } from "bun"; +import { S3Client, s3 as defaultS3, file, randomUUIDv7 } from "bun"; import { describe, expect, it } from "bun:test"; import child_process from "child_process"; import { randomUUID } from "crypto"; -import { bunEnv, bunExe, getSecret, isCI, isDockerEnabled, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, dockerExe, getSecret, isCI, isDockerEnabled, tempDirWithFiles } from "harness"; import path from "path"; const s3 = (...args) => defaultS3.file(...args); const S3 = (...args) => new S3Client(...args); @@ -11,7 +11,7 @@ const S3 = (...args) => new S3Client(...args); // Import docker-compose helper import * as dockerCompose from "../../../docker/index.ts"; -const dockerCLI = which("docker") as string; +const dockerCLI = dockerExe() as string; type S3Credentials = S3Options & { service: string; }; diff --git a/test/js/bun/symbols.test.ts b/test/js/bun/symbols.test.ts index 621871b6d6..ecc4ed441d 100644 --- a/test/js/bun/symbols.test.ts +++ b/test/js/bun/symbols.test.ts @@ -33,7 +33,7 @@ if (process.platform === "linux") { throw new Error(`Found glibc symbols > 2.26. This breaks Amazon Linux 2 and Vercel. ${Bun.inspect.table(errors, { colors: true })} -To fix this, add it to -Wl,-wrap=symbol in the linker flags and update workaround-missing-symbols.cpp.`); +To fix this, add it to -Wl,--wrap=symbol in the linker flags and update workaround-missing-symbols.cpp.`); } }); diff --git a/test/js/sql/local-sql.test.ts b/test/js/sql/local-sql.test.ts index 527bb57e8b..b9af87ae07 100644 --- a/test/js/sql/local-sql.test.ts +++ b/test/js/sql/local-sql.test.ts @@ -1,6 +1,6 @@ import { SQL } from "bun"; import { afterAll, expect, test } from "bun:test"; -import { bunEnv, bunExe, isDockerEnabled, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, dockerExe, isDockerEnabled, tempDirWithFiles } from "harness"; import path from "path"; const postgres = (...args) => new SQL(...args); @@ -9,7 +9,7 @@ import net from "net"; import { promisify } from "util"; const execAsync = promisify(exec); -const dockerCLI = Bun.which("docker") as string; +const dockerCLI = dockerExe() as string; async function findRandomPort() { return new Promise((resolve, reject) => { @@ -199,7 +199,7 @@ if (isDockerEnabled()) { const searchs = await db\` WITH cte AS ( - SELECT + SELECT post.id, post."content", post.created_at AS "createdAt", @@ -214,10 +214,10 @@ if (isDockerEnabled()) { \${fragment} ORDER BY post.created_at DESC ) - SELECT - * + SELECT + * FROM cte - -- LIMIT 5 + -- LIMIT 5 \`; return Response.json(searchs); } catch { diff --git a/test/js/valkey/test-utils.ts b/test/js/valkey/test-utils.ts index 18d8e3c728..34ce3347cc 100644 --- a/test/js/valkey/test-utils.ts +++ b/test/js/valkey/test-utils.ts @@ -1,6 +1,6 @@ import { RedisClient, type SpawnOptions } from "bun"; import { afterAll, beforeAll, expect } from "bun:test"; -import { bunEnv, isCI, randomPort, tempDirWithFiles } from "harness"; +import { bunEnv, dockerExe, isCI, randomPort, tempDirWithFiles } from "harness"; import path from "path"; import * as dockerCompose from "../../docker/index.ts"; @@ -8,7 +8,7 @@ import { UnixDomainSocketProxy } from "../../unix-domain-socket-proxy.ts"; import * as fs from "node:fs"; import * as os from "node:os"; -const dockerCLI = Bun.which("docker") as string; +const dockerCLI = dockerExe() as string; export const isEnabled = !!dockerCLI && (() => {