mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Create Zig wrapper generator for js.Function creation
This allows us to leverage the Caller.Function.call method, which does type mapping, caching, etc... and allows the Zig function callback to be written like any other Zig WebAPI function.
This commit is contained in:
@@ -509,6 +509,7 @@ pub const Function = struct {
|
||||
as_typed_array: bool = false,
|
||||
null_as_undefined: bool = false,
|
||||
cache: ?Caching = null,
|
||||
embedded_receiver: bool = false,
|
||||
|
||||
// We support two ways to cache a value directly into a v8::Object. The
|
||||
// difference between the two is like the difference between a Map
|
||||
@@ -579,6 +580,9 @@ pub const Function = struct {
|
||||
var args: ParameterTypes(F) = undefined;
|
||||
if (comptime opts.static) {
|
||||
args = try getArgs(F, 0, local, info);
|
||||
} else if (comptime opts.embedded_receiver) {
|
||||
args = try getArgs(F, 1, local, info);
|
||||
@field(args, "0") = @ptrCast(@alignCast(info.getData() orelse unreachable));
|
||||
} else {
|
||||
args = try getArgs(F, 1, local, info);
|
||||
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
|
||||
|
||||
@@ -539,6 +539,15 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: *
|
||||
}
|
||||
}
|
||||
|
||||
fn newFunctionWithData(local: *const js.Local, comptime callback: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void, data: *anyopaque) js.Function {
|
||||
const external = local.isolate.createExternal(data);
|
||||
const handle = v8.v8__Function__New__DEFAULT2(local.handle, callback, @ptrCast(external)).?;
|
||||
return .{
|
||||
.local = local,
|
||||
.handle = handle,
|
||||
};
|
||||
}
|
||||
|
||||
// == Callbacks ==
|
||||
// Callback from V8, asking us to load a module. The "specifier" is
|
||||
// the src of the module to load.
|
||||
@@ -857,7 +866,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
||||
// last value of the module. But, for module loading, we need to
|
||||
// resolve to the module's namespace.
|
||||
|
||||
const then_callback = local.newFunctionWithData(struct {
|
||||
const then_callback = newFunctionWithData(local, struct {
|
||||
pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void {
|
||||
var c: Caller = undefined;
|
||||
c.initFromHandle(callback_handle);
|
||||
@@ -881,7 +890,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
||||
}
|
||||
}.callback, @ptrCast(state));
|
||||
|
||||
const catch_callback = local.newFunctionWithData(struct {
|
||||
const catch_callback = newFunctionWithData(local, struct {
|
||||
pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void {
|
||||
var c: Caller = undefined;
|
||||
c.initFromHandle(callback_handle);
|
||||
|
||||
@@ -82,13 +82,17 @@ pub fn createTypedArray(self: *const Local, comptime array_type: js.ArrayType, s
|
||||
return .init(self, size);
|
||||
}
|
||||
|
||||
pub fn newFunctionWithData(
|
||||
pub fn newCallback(
|
||||
self: *const Local,
|
||||
comptime callback: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void,
|
||||
data: *anyopaque,
|
||||
callback: anytype,
|
||||
data: anytype,
|
||||
) js.Function {
|
||||
const external = self.isolate.createExternal(data);
|
||||
const handle = v8.v8__Function__New__DEFAULT2(self.handle, callback, @ptrCast(external)).?;
|
||||
const handle = v8.v8__Function__New__DEFAULT2(self.handle, struct {
|
||||
fn wrap(info_handle: ?*const js.v8.FunctionCallbackInfo) callconv(.c) void {
|
||||
Caller.Function.call(@TypeOf(data), info_handle.?, callback, .{ .embedded_receiver = true });
|
||||
}
|
||||
}.wrap, @ptrCast(external)).?;
|
||||
return .{ .local = self, .handle = handle };
|
||||
}
|
||||
|
||||
|
||||
@@ -279,7 +279,6 @@ pub fn pipeTo(self: *ReadableStream, destination: *WritableStream, page: *Page)
|
||||
const PipeState = struct {
|
||||
reader: *ReadableStreamDefaultReader,
|
||||
writable: *WritableStream,
|
||||
page: *Page,
|
||||
context_id: usize,
|
||||
resolver: ?js.PromiseResolver.Global,
|
||||
|
||||
@@ -294,107 +293,69 @@ const PipeState = struct {
|
||||
state.* = .{
|
||||
.reader = reader,
|
||||
.writable = writable,
|
||||
.page = page,
|
||||
.context_id = page.js.id,
|
||||
.resolver = resolver,
|
||||
};
|
||||
|
||||
try state.pumpRead();
|
||||
try state.pumpRead(page);
|
||||
}
|
||||
|
||||
fn pumpRead(state: *PipeState) !void {
|
||||
const local = state.page.js.local.?;
|
||||
fn pumpRead(state: *PipeState, page: *Page) !void {
|
||||
const local = page.js.local.?;
|
||||
|
||||
// Call reader.read() which returns a Promise
|
||||
const read_promise = try state.reader.read(state.page);
|
||||
const read_promise = try state.reader.read(page);
|
||||
|
||||
// Create JS callback functions for .then() and .catch()
|
||||
const then_fn = local.newFunctionWithData(&onReadFulfilled, state);
|
||||
const catch_fn = local.newFunctionWithData(&onReadRejected, state);
|
||||
const then_fn = local.newCallback(onReadFulfilled, state);
|
||||
const catch_fn = local.newCallback(onReadRejected, state);
|
||||
|
||||
_ = read_promise.thenAndCatch(then_fn, catch_fn) catch {
|
||||
state.finish(local);
|
||||
};
|
||||
}
|
||||
|
||||
fn onReadFulfilled(callback_handle: ?*const js.v8.FunctionCallbackInfo) callconv(.c) void {
|
||||
var c: js.Caller = undefined;
|
||||
c.initFromHandle(callback_handle);
|
||||
defer c.deinit();
|
||||
|
||||
const info = js.Caller.FunctionCallbackInfo{ .handle = callback_handle.? };
|
||||
const state: *PipeState = @ptrCast(@alignCast(info.getData() orelse return));
|
||||
|
||||
if (state.context_id != c.local.ctx.id) return;
|
||||
|
||||
const l = &c.local;
|
||||
defer l.runMicrotasks();
|
||||
|
||||
// Get the read result argument {done, value}
|
||||
const result_val = info.getArg(0, l);
|
||||
|
||||
if (!result_val.isObject()) {
|
||||
state.finish(l);
|
||||
return;
|
||||
}
|
||||
|
||||
const result_obj = result_val.toObject();
|
||||
const done_val = result_obj.get("done") catch {
|
||||
state.finish(l);
|
||||
return;
|
||||
const ReadData = struct {
|
||||
done: bool,
|
||||
value: js.Value,
|
||||
};
|
||||
fn onReadFulfilled(self: *PipeState, data_: ?ReadData, page: *Page) void {
|
||||
const local = page.js.local.?;
|
||||
const data = data_ orelse {
|
||||
return self.finish(local);
|
||||
};
|
||||
const done = done_val.toBool();
|
||||
|
||||
if (done) {
|
||||
if (data.done) {
|
||||
// Stream is finished, close the writable side
|
||||
state.writable.closeStream(state.page) catch {};
|
||||
state.finishResolve(l);
|
||||
self.writable.closeStream(page) catch {};
|
||||
self.reader.releaseLock();
|
||||
if (self.resolver) |r| {
|
||||
local.toLocal(r).resolve("pipeTo complete", {});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the chunk value and write it to the writable side
|
||||
const chunk_val = result_obj.get("value") catch {
|
||||
state.finish(l);
|
||||
return;
|
||||
};
|
||||
const value = data.value;
|
||||
if (value.isUndefined()) {
|
||||
return self.finish(local);
|
||||
}
|
||||
|
||||
state.writable.writeChunk(chunk_val, state.page) catch {
|
||||
state.finish(l);
|
||||
return;
|
||||
self.writable.writeChunk(value, page) catch {
|
||||
return self.finish(local);
|
||||
};
|
||||
|
||||
// Continue reading the next chunk
|
||||
state.pumpRead() catch {
|
||||
state.finish(l);
|
||||
self.pumpRead(page) catch {
|
||||
self.finish(local);
|
||||
};
|
||||
}
|
||||
|
||||
fn onReadRejected(callback_handle: ?*const js.v8.FunctionCallbackInfo) callconv(.c) void {
|
||||
var c: js.Caller = undefined;
|
||||
c.initFromHandle(callback_handle);
|
||||
defer c.deinit();
|
||||
|
||||
const info = js.Caller.FunctionCallbackInfo{ .handle = callback_handle.? };
|
||||
const state: *PipeState = @ptrCast(@alignCast(info.getData() orelse return));
|
||||
|
||||
if (state.context_id != c.local.ctx.id) return;
|
||||
|
||||
const l = &c.local;
|
||||
defer l.runMicrotasks();
|
||||
|
||||
state.finish(l);
|
||||
fn onReadRejected(self: *PipeState, page: *Page) void {
|
||||
self.finish(page.js.local.?);
|
||||
}
|
||||
|
||||
fn finishResolve(state: *PipeState, local: *const js.Local) void {
|
||||
state.reader.releaseLock();
|
||||
if (state.resolver) |r| {
|
||||
local.toLocal(r).resolve("pipeTo complete", {});
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(state: *PipeState, local: *const js.Local) void {
|
||||
state.reader.releaseLock();
|
||||
if (state.resolver) |r| {
|
||||
fn finish(self: *PipeState, local: *const js.Local) void {
|
||||
self.reader.releaseLock();
|
||||
if (self.resolver) |r| {
|
||||
local.toLocal(r).resolve("pipe finished", {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const TransformStream = @This();
|
||||
|
||||
pub const DefaultController = TransformStreamDefaultController;
|
||||
|
||||
pub const ZigTransformFn = *const fn (*TransformStreamDefaultController, js.Value) anyerror!void;
|
||||
const ZigTransformFn = *const fn (*TransformStreamDefaultController, js.Value) anyerror!void;
|
||||
|
||||
_readable: *ReadableStream,
|
||||
_writable: *WritableStream,
|
||||
|
||||
Reference in New Issue
Block a user