Ability to return typed arrays

This commit is contained in:
Karl Seguin
2025-05-12 17:47:05 +08:00
parent b5eea2136b
commit 212d7f1865
4 changed files with 125 additions and 6 deletions

View File

@@ -13,8 +13,8 @@
.hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd",
},
.v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/f0c7eaaffe39f2f1a224fbe97e550daca0ca1801.tar.gz",
.hash = "v8-0.0.0-xddH62T4IADchAHFgo4nx79w1VedNDhIVErtSNgup-Tk",
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/fc764e7d29bc1514924e8df09255a057e03d453a.tar.gz",
.hash = "v8-0.0.0-xddH6zUZIQBJf109L94sC-mWH1NJXWCnOJGJttKtfasI",
},
//.v8 = .{ .path = "../zig-v8-fork" },
//.tigerbeetle_io = .{ .path = "../tigerbeetle-io" },

View File

@@ -1012,6 +1012,22 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
}
};
// If a function returns a []i32, should that map to a plain-old
// JavaScript array, or a Int32Array? It's ambiguous. By default, we'll
// map arrays/slices to the JavaScript arrays. If you want a TypedArray
// wrap it in this.
// Also, this type has nothing to do with the Env. But we place it here
// for consistency. Want a callback? Env.Callback. Want a JsObject?
// Env.JsObject. Want a TypedArray? Env.TypedArray.
pub fn TypedArray(comptime T: type) type {
return struct {
// See Callback._CALLBACK_ID_KLUDGE
const _TYPED_ARRAY_ID_KLUDGE = true;
values: []const T,
};
}
pub const Inspector = struct {
isolate: v8.Isolate,
inner: *v8.Inspector,
@@ -2632,6 +2648,53 @@ fn simpleZigValueToJs(isolate: v8.Isolate, value: anytype, comptime fail: bool)
}
return v8.initNull(isolate).toValue();
},
.@"struct" => {
const T = @TypeOf(value);
if (@hasDecl(T, "_TYPED_ARRAY_ID_KLUDGE")) {
const values = value.values;
const value_type = @typeInfo(@TypeOf(values)).pointer.child;
const len = values.len;
const bits = switch (@typeInfo(value_type)) {
.int => |n| n.bits,
.float => |f| f.bits,
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());
switch (@typeInfo(value_type)) {
.int => |n| switch (n.signedness) {
.unsigned => switch (n.bits) {
8 => return v8.Uint8Array.init(array_buffer, 0, len).toValue(),
16 => return v8.Uint16Array.init(array_buffer, 0, len).toValue(),
32 => return v8.Uint32Array.init(array_buffer, 0, len).toValue(),
64 => return v8.BigUint64Array.init(array_buffer, 0, len).toValue(),
else => {},
},
.signed => switch (n.bits) {
8 => return v8.Int8Array.init(array_buffer, 0, len).toValue(),
16 => return v8.Int16Array.init(array_buffer, 0, len).toValue(),
32 => return v8.Int32Array.init(array_buffer, 0, len).toValue(),
64 => return v8.BigInt64Array.init(array_buffer, 0, len).toValue(),
else => {},
},
},
.float => |f| switch (f.bits) {
32 => return v8.Float32Array.init(array_buffer, 0, len).toValue(),
64 => return v8.Float64Array.init(array_buffer, 0, len).toValue(),
else => {},
},
else => {},
}
// We normally don't fail in this function unless fail == true
// but this can never be valid.
@compileError("Invalid TypeArray type: " ++ @typeName(value_type));
}
},
.@"union" => return simpleZigValueToJs(isolate, std.meta.activeTag(value), fail),
else => {},
}

View File

@@ -17,6 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// TODO: use functions instead of "fake" struct once we handle function API generation
const Runner = testing.Runner(void, void, .{Primitives});
const Env = Runner.Env;
const Primitives = struct {
pub fn constructor() Primitives {
return .{};
@@ -114,6 +118,46 @@ const Primitives = struct {
}
}
pub fn _returnUint8(_: *const Primitives) Env.TypedArray(u8) {
return .{ .values = &.{ 10, 20, 250 } };
}
pub fn _returnInt8(_: *const Primitives) Env.TypedArray(i8) {
return .{ .values = &.{ 10, -20, -120 } };
}
pub fn _returnUint16(_: *const Primitives) Env.TypedArray(u16) {
return .{ .values = &.{ 10, 200, 2050 } };
}
pub fn _returnInt16(_: *const Primitives) Env.TypedArray(i16) {
return .{ .values = &.{ 10, -420, 0 } };
}
pub fn _returnUint32(_: *const Primitives) Env.TypedArray(u32) {
return .{ .values = &.{ 10, 2444343, 43432432 } };
}
pub fn _returnInt32(_: *const Primitives) Env.TypedArray(i32) {
return .{ .values = &.{ 10, -20, -495929123 } };
}
pub fn _returnUint64(_: *const Primitives) Env.TypedArray(u64) {
return .{ .values = &.{ 10, 495812375924, 0 } };
}
pub fn _returnInt64(_: *const Primitives) Env.TypedArray(i64) {
return .{ .values = &.{ 10, -49283838122, -2 } };
}
pub fn _returnFloat32(_: *const Primitives) Env.TypedArray(f32) {
return .{ .values = &.{ 1.1, -200.035, 0.0003 } };
}
pub fn _returnFloat64(_: *const Primitives) Env.TypedArray(f64) {
return .{ .values = &.{ 8881.22284, -4928.3838122, -0.00004 } };
}
pub fn _int16(_: *const Primitives, arr: []i16) void {
for (arr) |*a| {
a.* -= @intCast(arr.len);
@@ -153,7 +197,7 @@ const Primitives = struct {
const testing = @import("testing.zig");
test "JS: primitive types" {
var runner = try testing.Runner(void, void, .{Primitives}).init({}, {});
var runner = try Runner.init({}, {});
defer runner.deinit();
// constructor
@@ -280,5 +324,16 @@ test "JS: primitive types" {
.{ "try { p.int64(arr_u64) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu64(arr_i64) } catch(e) { e instanceof TypeError; }", "true" },
.{ "try { p.intu64(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
.{ "p.returnUint8()", "10,20,250" },
.{ "p.returnInt8()", "10,-20,-120" },
.{ "p.returnUint16()", "10,200,2050" },
.{ "p.returnInt16()", "10,-420,0" },
.{ "p.returnUint32()", "10,2444343,43432432" },
.{ "p.returnInt32()", "10,-20,-495929123" },
.{ "p.returnUint64()", "10,495812375924,0" },
.{ "p.returnInt64()", "10,-49283838122,-2" },
.{ "p.returnFloat32()", "1.100000023841858,-200.03500366210938,0.0003000000142492354" },
.{ "p.returnFloat64()", "8881.22284,-4928.3838122,-0.00004" },
}, .{});
}

View File

@@ -26,15 +26,16 @@ pub const allocator = std.testing.allocator;
// browser.Env or the browser.SessionState
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
const Env = js.Env(State, struct {
pub const Interfaces = AdjustedTypes;
});
return struct {
env: *Env,
scope: *Env.Scope,
executor: Env.Executor,
pub const Env = js.Env(State, struct {
pub const Interfaces = AdjustedTypes;
});
const Self = @This();
pub fn init(state: State, global: Global) !*Self {