Skip to content

Commit b086337

Browse files
Merge pull request #629 from lightpanda-io/return_typed_arrays
Ability to return typed arrays
2 parents 884ec05 + 49562f5 commit b086337

File tree

6 files changed

+127
-8
lines changed

6 files changed

+127
-8
lines changed

.github/actions/install/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ inputs:
1717
zig-v8:
1818
description: 'zig v8 version to install'
1919
required: false
20-
default: 'v0.1.20'
20+
default: 'v0.1.21'
2121
v8:
2222
description: 'v8 version to install'
2323
required: false

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ARG ZIG=0.14.0
55
ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
66
ARG ARCH=x86_64
77
ARG V8=11.1.134
8-
ARG ZIG_V8=v0.1.20
8+
ARG ZIG_V8=v0.1.21
99

1010
RUN apt-get update -yq && \
1111
apt-get install -yq xz-utils \

build.zig.zon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
.hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd",
1414
},
1515
.v8 = .{
16-
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/f0c7eaaffe39f2f1a224fbe97e550daca0ca1801.tar.gz",
17-
.hash = "v8-0.0.0-xddH62T4IADchAHFgo4nx79w1VedNDhIVErtSNgup-Tk",
16+
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/fc764e7d29bc1514924e8df09255a057e03d453a.tar.gz",
17+
.hash = "v8-0.0.0-xddH6zUZIQBJf109L94sC-mWH1NJXWCnOJGJttKtfasI",
1818
},
1919
//.v8 = .{ .path = "../zig-v8-fork" },
2020
//.tigerbeetle_io = .{ .path = "../tigerbeetle-io" },

src/runtime/js.zig

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,22 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
10121012
}
10131013
};
10141014

1015+
// If a function returns a []i32, should that map to a plain-old
1016+
// JavaScript array, or a Int32Array? It's ambiguous. By default, we'll
1017+
// map arrays/slices to the JavaScript arrays. If you want a TypedArray
1018+
// wrap it in this.
1019+
// Also, this type has nothing to do with the Env. But we place it here
1020+
// for consistency. Want a callback? Env.Callback. Want a JsObject?
1021+
// Env.JsObject. Want a TypedArray? Env.TypedArray.
1022+
pub fn TypedArray(comptime T: type) type {
1023+
return struct {
1024+
// See Callback._CALLBACK_ID_KLUDGE
1025+
const _TYPED_ARRAY_ID_KLUDGE = true;
1026+
1027+
values: []const T,
1028+
};
1029+
}
1030+
10151031
pub const Inspector = struct {
10161032
isolate: v8.Isolate,
10171033
inner: *v8.Inspector,
@@ -2632,6 +2648,53 @@ fn simpleZigValueToJs(isolate: v8.Isolate, value: anytype, comptime fail: bool)
26322648
}
26332649
return v8.initNull(isolate).toValue();
26342650
},
2651+
.@"struct" => {
2652+
const T = @TypeOf(value);
2653+
if (@hasDecl(T, "_TYPED_ARRAY_ID_KLUDGE")) {
2654+
const values = value.values;
2655+
const value_type = @typeInfo(@TypeOf(values)).pointer.child;
2656+
const len = values.len;
2657+
const bits = switch (@typeInfo(value_type)) {
2658+
.int => |n| n.bits,
2659+
.float => |f| f.bits,
2660+
else => @compileError("Invalid TypeArray type: " ++ @typeName(value_type)),
2661+
};
2662+
2663+
const buffer_len = len * bits / 8;
2664+
const backing_store = v8.BackingStore.init(isolate, buffer_len);
2665+
const data: [*]u8 = @alignCast(@ptrCast(backing_store.getData()));
2666+
@memcpy(data[0..buffer_len], @as([]const u8, @ptrCast(values))[0..buffer_len]);
2667+
const array_buffer = v8.ArrayBuffer.initWithBackingStore(isolate, &backing_store.toSharedPtr());
2668+
2669+
switch (@typeInfo(value_type)) {
2670+
.int => |n| switch (n.signedness) {
2671+
.unsigned => switch (n.bits) {
2672+
8 => return v8.Uint8Array.init(array_buffer, 0, len).toValue(),
2673+
16 => return v8.Uint16Array.init(array_buffer, 0, len).toValue(),
2674+
32 => return v8.Uint32Array.init(array_buffer, 0, len).toValue(),
2675+
64 => return v8.BigUint64Array.init(array_buffer, 0, len).toValue(),
2676+
else => {},
2677+
},
2678+
.signed => switch (n.bits) {
2679+
8 => return v8.Int8Array.init(array_buffer, 0, len).toValue(),
2680+
16 => return v8.Int16Array.init(array_buffer, 0, len).toValue(),
2681+
32 => return v8.Int32Array.init(array_buffer, 0, len).toValue(),
2682+
64 => return v8.BigInt64Array.init(array_buffer, 0, len).toValue(),
2683+
else => {},
2684+
},
2685+
},
2686+
.float => |f| switch (f.bits) {
2687+
32 => return v8.Float32Array.init(array_buffer, 0, len).toValue(),
2688+
64 => return v8.Float64Array.init(array_buffer, 0, len).toValue(),
2689+
else => {},
2690+
},
2691+
else => {},
2692+
}
2693+
// We normally don't fail in this function unless fail == true
2694+
// but this can never be valid.
2695+
@compileError("Invalid TypeArray type: " ++ @typeName(value_type));
2696+
}
2697+
},
26352698
.@"union" => return simpleZigValueToJs(isolate, std.meta.activeTag(value), fail),
26362699
else => {},
26372700
}

src/runtime/test_primitive_types.zig

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

1919
// TODO: use functions instead of "fake" struct once we handle function API generation
20+
21+
const Runner = testing.Runner(void, void, .{Primitives});
22+
const Env = Runner.Env;
23+
2024
const Primitives = struct {
2125
pub fn constructor() Primitives {
2226
return .{};
@@ -114,6 +118,46 @@ const Primitives = struct {
114118
}
115119
}
116120

121+
pub fn _returnUint8(_: *const Primitives) Env.TypedArray(u8) {
122+
return .{ .values = &.{ 10, 20, 250 } };
123+
}
124+
125+
pub fn _returnInt8(_: *const Primitives) Env.TypedArray(i8) {
126+
return .{ .values = &.{ 10, -20, -120 } };
127+
}
128+
129+
pub fn _returnUint16(_: *const Primitives) Env.TypedArray(u16) {
130+
return .{ .values = &.{ 10, 200, 2050 } };
131+
}
132+
133+
pub fn _returnInt16(_: *const Primitives) Env.TypedArray(i16) {
134+
return .{ .values = &.{ 10, -420, 0 } };
135+
}
136+
137+
pub fn _returnUint32(_: *const Primitives) Env.TypedArray(u32) {
138+
return .{ .values = &.{ 10, 2444343, 43432432 } };
139+
}
140+
141+
pub fn _returnInt32(_: *const Primitives) Env.TypedArray(i32) {
142+
return .{ .values = &.{ 10, -20, -495929123 } };
143+
}
144+
145+
pub fn _returnUint64(_: *const Primitives) Env.TypedArray(u64) {
146+
return .{ .values = &.{ 10, 495812375924, 0 } };
147+
}
148+
149+
pub fn _returnInt64(_: *const Primitives) Env.TypedArray(i64) {
150+
return .{ .values = &.{ 10, -49283838122, -2 } };
151+
}
152+
153+
pub fn _returnFloat32(_: *const Primitives) Env.TypedArray(f32) {
154+
return .{ .values = &.{ 1.1, -200.035, 0.0003 } };
155+
}
156+
157+
pub fn _returnFloat64(_: *const Primitives) Env.TypedArray(f64) {
158+
return .{ .values = &.{ 8881.22284, -4928.3838122, -0.00004 } };
159+
}
160+
117161
pub fn _int16(_: *const Primitives, arr: []i16) void {
118162
for (arr) |*a| {
119163
a.* -= @intCast(arr.len);
@@ -153,7 +197,7 @@ const Primitives = struct {
153197

154198
const testing = @import("testing.zig");
155199
test "JS: primitive types" {
156-
var runner = try testing.Runner(void, void, .{Primitives}).init({}, {});
200+
var runner = try Runner.init({}, {});
157201
defer runner.deinit();
158202

159203
// constructor
@@ -280,5 +324,16 @@ test "JS: primitive types" {
280324
.{ "try { p.int64(arr_u64) } catch(e) { e instanceof TypeError; }", "true" },
281325
.{ "try { p.intu64(arr_i64) } catch(e) { e instanceof TypeError; }", "true" },
282326
.{ "try { p.intu64(arr_u32) } catch(e) { e instanceof TypeError; }", "true" },
327+
328+
.{ "p.returnUint8()", "10,20,250" },
329+
.{ "p.returnInt8()", "10,-20,-120" },
330+
.{ "p.returnUint16()", "10,200,2050" },
331+
.{ "p.returnInt16()", "10,-420,0" },
332+
.{ "p.returnUint32()", "10,2444343,43432432" },
333+
.{ "p.returnInt32()", "10,-20,-495929123" },
334+
.{ "p.returnUint64()", "10,495812375924,0" },
335+
.{ "p.returnInt64()", "10,-49283838122,-2" },
336+
.{ "p.returnFloat32()", "1.100000023841858,-200.03500366210938,0.0003000000142492354" },
337+
.{ "p.returnFloat64()", "8881.22284,-4928.3838122,-0.00004" },
283338
}, .{});
284339
}

src/runtime/testing.zig

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ pub const allocator = std.testing.allocator;
2626
// browser.Env or the browser.SessionState
2727
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
2828
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
29-
const Env = js.Env(State, struct {
30-
pub const Interfaces = AdjustedTypes;
31-
});
3229

3330
return struct {
3431
env: *Env,
3532
scope: *Env.Scope,
3633
executor: Env.Executor,
3734

35+
pub const Env = js.Env(State, struct {
36+
pub const Interfaces = AdjustedTypes;
37+
});
38+
3839
const Self = @This();
3940

4041
pub fn init(state: State, global: Global) !*Self {

0 commit comments

Comments
 (0)