From 52418932b11e248bbac128d6a7b03dcc15327587 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Sun, 15 Feb 2026 02:03:11 +0300 Subject: [PATCH 1/3] `simpleZigValueToJs`: support `Uint8ClampedArray` Needed this to implement `ImageData#data` getter. This works differently than other typed arrays since returned object can be mutated from both Zig and JS ends. --- src/browser/js/js.zig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 49842fa7..cd569b09 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -77,6 +77,14 @@ pub const ArrayBuffer = struct { } }; +/// `mutable` indicates bytes are not copied by `simpleZigValueToJs`; +/// instead, `BackingStore` takes ownership of already allocated `values`. +pub fn Uint8ClampedArray(comptime state: enum(u1) { immutable, mutable }) type { + return struct { + values: if (state == .mutable) []u8 else []const u8, + }; +} + pub const Exception = struct { local: *const Local, handle: *const v8.Value, @@ -194,6 +202,24 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, // but this can never be valid. @compileError("Invalid TypeArray type: " ++ @typeName(value_type)); }, + Uint8ClampedArray(.mutable) => { + const values = value.values; + const len = values.len; + var array_buffer: *const v8.ArrayBuffer = undefined; + if (len == 0) { + array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; + } else { + // `deleter` cannot be null. + const empty_deleter = struct { + fn deleter(_: ?*anyopaque, _: usize, _: ?*anyopaque) callconv(.c) void {} + }.deleter; + const backing_store = v8.v8__ArrayBuffer__NewBackingStore2(values.ptr, len, empty_deleter, null); + const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); + // Attach store to array buffer. + array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; + } + return @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, len)); + }, inline String, BigInt, Integer, Number, Value, Object => return value.handle, else => {}, } From 119f3169e2a3d5cf16992b573724fbab7d666219 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Sun, 15 Feb 2026 12:11:44 +0300 Subject: [PATCH 2/3] `Uint8ClampedArray`: update comment + change enum literals I feel `ref` and `copy` is on-point on what's being actually happening. --- src/browser/js/js.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index cd569b09..d5ebca5a 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -77,11 +77,14 @@ pub const ArrayBuffer = struct { } }; -/// `mutable` indicates bytes are not copied by `simpleZigValueToJs`; -/// instead, `BackingStore` takes ownership of already allocated `values`. -pub fn Uint8ClampedArray(comptime state: enum(u1) { immutable, mutable }) type { +/// `ref` indicates bytes are not copied by `simpleZigValueToJs`; +/// instead, `values` references an already allocated memory. Note that +/// this variant assumes memory is (de)allocated by an arena allocator. +/// +/// `copy` behaves the same as `TypedArray(T)`. +pub fn Uint8ClampedArray(comptime state: enum(u1) { ref, copy }) type { return struct { - values: if (state == .mutable) []u8 else []const u8, + values: if (state == .ref) []u8 else []const u8, }; } @@ -202,7 +205,7 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, // but this can never be valid. @compileError("Invalid TypeArray type: " ++ @typeName(value_type)); }, - Uint8ClampedArray(.mutable) => { + Uint8ClampedArray(.ref) => { const values = value.values; const len = values.len; var array_buffer: *const v8.ArrayBuffer = undefined; From 26ce9b2d4a8eb8557287e7facd6c22b7aaaaf9a1 Mon Sep 17 00:00:00 2001 From: Halil Durak Date: Sun, 15 Feb 2026 12:12:45 +0300 Subject: [PATCH 3/3] `Uint8ClampedArray`: implement `.copy` variant --- src/browser/js/js.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index d5ebca5a..16f553a5 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -223,6 +223,22 @@ pub fn simpleZigValueToJs(isolate: Isolate, value: anytype, comptime fail: bool, } return @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, len)); }, + Uint8ClampedArray(.copy) => { + const values = value.values; + const len = values.len; + var array_buffer: *const v8.ArrayBuffer = undefined; + if (len == 0) { + array_buffer = v8.v8__ArrayBuffer__New(isolate.handle, 0).?; + } else { + const backing_store = v8.v8__ArrayBuffer__NewBackingStore(isolate.handle, len); + const data: [*]u8 = @ptrCast(@alignCast(v8.v8__BackingStore__Data(backing_store))); + @memcpy(data[0..len], @as([]const u8, @ptrCast(values))[0..len]); + const backing_store_ptr = v8.v8__BackingStore__TO_SHARED_PTR(backing_store); + // Attach store to array buffer. + array_buffer = v8.v8__ArrayBuffer__New2(isolate.handle, &backing_store_ptr).?; + } + return @ptrCast(v8.v8__Uint8ClampedArray__New(array_buffer, 0, len)); + }, inline String, BigInt, Integer, Number, Value, Object => return value.handle, else => {}, }