From b785884cd8677556146e3382fcca450ceb04aad8 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 8 Aug 2025 17:26:39 +0200 Subject: [PATCH 1/2] runtime: fix returning an empty array crash --- src/runtime/js.zig | 15 ++++++++++----- src/runtime/test_primitive_types.zig | 5 +++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/runtime/js.zig b/src/runtime/js.zig index a67a4c57..aae49029 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -3469,11 +3469,16 @@ fn simpleZigValueToJs(isolate: v8.Isolate, value: anytype, comptime fail: bool) else => @compileError("Invalid TypeArray type: " ++ @typeName(value_type)), }; - const buffer_len = len * bits / 8; - const backing_store = v8.BackingStore.init(isolate, buffer_len); - const data: [*]u8 = @alignCast(@ptrCast(backing_store.getData())); - @memcpy(data[0..buffer_len], @as([]const u8, @ptrCast(values))[0..buffer_len]); - const array_buffer = v8.ArrayBuffer.initWithBackingStore(isolate, &backing_store.toSharedPtr()); + var array_buffer: v8.ArrayBuffer = undefined; + if (len == 0) { + array_buffer = v8.ArrayBuffer.init(isolate, 0); + } else { + const buffer_len = len * bits / 8; + const backing_store = v8.BackingStore.init(isolate, buffer_len); + const data: [*]u8 = @alignCast(@ptrCast(backing_store.getData())); + @memcpy(data[0..buffer_len], @as([]const u8, @ptrCast(values))[0..buffer_len]); + array_buffer = v8.ArrayBuffer.initWithBackingStore(isolate, &backing_store.toSharedPtr()); + } switch (@typeInfo(value_type)) { .int => |n| switch (n.signedness) { diff --git a/src/runtime/test_primitive_types.zig b/src/runtime/test_primitive_types.zig index 49db501c..f9989e3f 100644 --- a/src/runtime/test_primitive_types.zig +++ b/src/runtime/test_primitive_types.zig @@ -118,6 +118,10 @@ const Primitives = struct { } } + pub fn _returnEmptyUint8(_: *const Primitives) Env.TypedArray(u8) { + return .{ .values = &.{} }; + } + pub fn _returnUint8(_: *const Primitives) Env.TypedArray(u8) { return .{ .values = &.{ 10, 20, 250 } }; } @@ -325,6 +329,7 @@ test "JS: primitive types" { .{ "try { p.intu64(arr_i64) } catch(e) { e instanceof TypeError; }", "true" }, .{ "try { p.intu64(arr_u32) } catch(e) { e instanceof TypeError; }", "true" }, + .{ "p.returnEmptyUint8()", "" }, .{ "p.returnUint8()", "10,20,250" }, .{ "p.returnInt8()", "10,-20,-120" }, .{ "p.returnUint16()", "10,200,2050" }, From f463cb16da66fa226ba9d7c2674ae9af1f39d3b1 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 8 Aug 2025 17:50:18 +0200 Subject: [PATCH 2/2] runtime: handle empty array parameter --- src/runtime/js.zig | 8 ++++++++ src/runtime/test_primitive_types.zig | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/src/runtime/js.zig b/src/runtime/js.zig index aae49029..ca93ece6 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -976,6 +976,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { // need this sentinel check to keep the compiler happy if (ptr.sentinel() == null) { if (force_u8 or js_value.isUint8Array() or js_value.isUint8ClampedArray()) { + if (byte_len == 0) return &[_]u8{}; const arr_ptr = @as([*]u8, @alignCast(@ptrCast(data))); return arr_ptr[0..byte_len]; } @@ -983,42 +984,49 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { }, i8 => { if (js_value.isInt8Array()) { + if (byte_len == 0) return &[_]i8{}; const arr_ptr = @as([*]i8, @alignCast(@ptrCast(data))); return arr_ptr[0..byte_len]; } }, u16 => { if (js_value.isUint16Array()) { + if (byte_len == 0) return &[_]u16{}; const arr_ptr = @as([*]u16, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 2]; } }, i16 => { if (js_value.isInt16Array()) { + if (byte_len == 0) return &[_]i16{}; const arr_ptr = @as([*]i16, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 2]; } }, u32 => { if (js_value.isUint32Array()) { + if (byte_len == 0) return &[_]u32{}; const arr_ptr = @as([*]u32, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 4]; } }, i32 => { if (js_value.isInt32Array()) { + if (byte_len == 0) return &[_]i32{}; const arr_ptr = @as([*]i32, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 4]; } }, u64 => { if (js_value.isBigUint64Array()) { + if (byte_len == 0) return &[_]u64{}; const arr_ptr = @as([*]u64, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 8]; } }, i64 => { if (js_value.isBigInt64Array()) { + if (byte_len == 0) return &[_]i64{}; const arr_ptr = @as([*]i64, @alignCast(@ptrCast(data))); return arr_ptr[0 .. byte_len / 8]; } diff --git a/src/runtime/test_primitive_types.zig b/src/runtime/test_primitive_types.zig index f9989e3f..ec3fab73 100644 --- a/src/runtime/test_primitive_types.zig +++ b/src/runtime/test_primitive_types.zig @@ -281,6 +281,10 @@ test "JS: primitive types" { // typed arrays try runner.testCases(&.{ + .{ "let empty_arr = new Int8Array([]);", "undefined" }, + .{ "p.int8(empty_arr)", "undefined" }, + .{ "empty_arr;", "" }, + .{ "let arr_i8 = new Int8Array([-10, -20, -30]);", "undefined" }, .{ "p.int8(arr_i8)", "undefined" }, .{ "arr_i8;", "-13,-23,-33" },