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,
|
as_typed_array: bool = false,
|
||||||
null_as_undefined: bool = false,
|
null_as_undefined: bool = false,
|
||||||
cache: ?Caching = null,
|
cache: ?Caching = null,
|
||||||
|
embedded_receiver: bool = false,
|
||||||
|
|
||||||
// We support two ways to cache a value directly into a v8::Object. The
|
// 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
|
// difference between the two is like the difference between a Map
|
||||||
@@ -579,6 +580,9 @@ pub const Function = struct {
|
|||||||
var args: ParameterTypes(F) = undefined;
|
var args: ParameterTypes(F) = undefined;
|
||||||
if (comptime opts.static) {
|
if (comptime opts.static) {
|
||||||
args = try getArgs(F, 0, local, info);
|
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 {
|
} else {
|
||||||
args = try getArgs(F, 1, local, info);
|
args = try getArgs(F, 1, local, info);
|
||||||
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
|
@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 ==
|
// == Callbacks ==
|
||||||
// Callback from V8, asking us to load a module. The "specifier" is
|
// Callback from V8, asking us to load a module. The "specifier" is
|
||||||
// the src of the module to load.
|
// 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
|
// last value of the module. But, for module loading, we need to
|
||||||
// resolve to the module's namespace.
|
// 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 {
|
pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void {
|
||||||
var c: Caller = undefined;
|
var c: Caller = undefined;
|
||||||
c.initFromHandle(callback_handle);
|
c.initFromHandle(callback_handle);
|
||||||
@@ -881,7 +890,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
|||||||
}
|
}
|
||||||
}.callback, @ptrCast(state));
|
}.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 {
|
pub fn callback(callback_handle: ?*const v8.FunctionCallbackInfo) callconv(.c) void {
|
||||||
var c: Caller = undefined;
|
var c: Caller = undefined;
|
||||||
c.initFromHandle(callback_handle);
|
c.initFromHandle(callback_handle);
|
||||||
|
|||||||
@@ -82,13 +82,17 @@ pub fn createTypedArray(self: *const Local, comptime array_type: js.ArrayType, s
|
|||||||
return .init(self, size);
|
return .init(self, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newFunctionWithData(
|
pub fn newCallback(
|
||||||
self: *const Local,
|
self: *const Local,
|
||||||
comptime callback: *const fn (?*const v8.FunctionCallbackInfo) callconv(.c) void,
|
callback: anytype,
|
||||||
data: *anyopaque,
|
data: anytype,
|
||||||
) js.Function {
|
) js.Function {
|
||||||
const external = self.isolate.createExternal(data);
|
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 };
|
return .{ .local = self, .handle = handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -279,7 +279,6 @@ pub fn pipeTo(self: *ReadableStream, destination: *WritableStream, page: *Page)
|
|||||||
const PipeState = struct {
|
const PipeState = struct {
|
||||||
reader: *ReadableStreamDefaultReader,
|
reader: *ReadableStreamDefaultReader,
|
||||||
writable: *WritableStream,
|
writable: *WritableStream,
|
||||||
page: *Page,
|
|
||||||
context_id: usize,
|
context_id: usize,
|
||||||
resolver: ?js.PromiseResolver.Global,
|
resolver: ?js.PromiseResolver.Global,
|
||||||
|
|
||||||
@@ -294,107 +293,69 @@ const PipeState = struct {
|
|||||||
state.* = .{
|
state.* = .{
|
||||||
.reader = reader,
|
.reader = reader,
|
||||||
.writable = writable,
|
.writable = writable,
|
||||||
.page = page,
|
|
||||||
.context_id = page.js.id,
|
.context_id = page.js.id,
|
||||||
.resolver = resolver,
|
.resolver = resolver,
|
||||||
};
|
};
|
||||||
|
try state.pumpRead(page);
|
||||||
try state.pumpRead();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pumpRead(state: *PipeState) !void {
|
fn pumpRead(state: *PipeState, page: *Page) !void {
|
||||||
const local = state.page.js.local.?;
|
const local = page.js.local.?;
|
||||||
|
|
||||||
// Call reader.read() which returns a Promise
|
// 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()
|
// Create JS callback functions for .then() and .catch()
|
||||||
const then_fn = local.newFunctionWithData(&onReadFulfilled, state);
|
const then_fn = local.newCallback(onReadFulfilled, state);
|
||||||
const catch_fn = local.newFunctionWithData(&onReadRejected, state);
|
const catch_fn = local.newCallback(onReadRejected, state);
|
||||||
|
|
||||||
_ = read_promise.thenAndCatch(then_fn, catch_fn) catch {
|
_ = read_promise.thenAndCatch(then_fn, catch_fn) catch {
|
||||||
state.finish(local);
|
state.finish(local);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onReadFulfilled(callback_handle: ?*const js.v8.FunctionCallbackInfo) callconv(.c) void {
|
const ReadData = struct {
|
||||||
var c: js.Caller = undefined;
|
done: bool,
|
||||||
c.initFromHandle(callback_handle);
|
value: js.Value,
|
||||||
defer c.deinit();
|
};
|
||||||
|
fn onReadFulfilled(self: *PipeState, data_: ?ReadData, page: *Page) void {
|
||||||
const info = js.Caller.FunctionCallbackInfo{ .handle = callback_handle.? };
|
const local = page.js.local.?;
|
||||||
const state: *PipeState = @ptrCast(@alignCast(info.getData() orelse return));
|
const data = data_ orelse {
|
||||||
|
return self.finish(local);
|
||||||
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 done = done_val.toBool();
|
|
||||||
|
|
||||||
if (done) {
|
if (data.done) {
|
||||||
// Stream is finished, close the writable side
|
// Stream is finished, close the writable side
|
||||||
state.writable.closeStream(state.page) catch {};
|
self.writable.closeStream(page) catch {};
|
||||||
state.finishResolve(l);
|
self.reader.releaseLock();
|
||||||
|
if (self.resolver) |r| {
|
||||||
|
local.toLocal(r).resolve("pipeTo complete", {});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the chunk value and write it to the writable side
|
const value = data.value;
|
||||||
const chunk_val = result_obj.get("value") catch {
|
if (value.isUndefined()) {
|
||||||
state.finish(l);
|
return self.finish(local);
|
||||||
return;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
state.writable.writeChunk(chunk_val, state.page) catch {
|
self.writable.writeChunk(value, page) catch {
|
||||||
state.finish(l);
|
return self.finish(local);
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Continue reading the next chunk
|
// Continue reading the next chunk
|
||||||
state.pumpRead() catch {
|
self.pumpRead(page) catch {
|
||||||
state.finish(l);
|
self.finish(local);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn onReadRejected(callback_handle: ?*const js.v8.FunctionCallbackInfo) callconv(.c) void {
|
fn onReadRejected(self: *PipeState, page: *Page) void {
|
||||||
var c: js.Caller = undefined;
|
self.finish(page.js.local.?);
|
||||||
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 finishResolve(state: *PipeState, local: *const js.Local) void {
|
fn finish(self: *PipeState, local: *const js.Local) void {
|
||||||
state.reader.releaseLock();
|
self.reader.releaseLock();
|
||||||
if (state.resolver) |r| {
|
if (self.resolver) |r| {
|
||||||
local.toLocal(r).resolve("pipeTo complete", {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(state: *PipeState, local: *const js.Local) void {
|
|
||||||
state.reader.releaseLock();
|
|
||||||
if (state.resolver) |r| {
|
|
||||||
local.toLocal(r).resolve("pipe finished", {});
|
local.toLocal(r).resolve("pipe finished", {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const TransformStream = @This();
|
|||||||
|
|
||||||
pub const DefaultController = TransformStreamDefaultController;
|
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,
|
_readable: *ReadableStream,
|
||||||
_writable: *WritableStream,
|
_writable: *WritableStream,
|
||||||
|
|||||||
Reference in New Issue
Block a user