Lift only the handled event in handle-event

The generated export lifted the whole Event variant, which pulls in code for
every event there is. A plugin now only compiles the cases it handles, the
example builds in about 6s instead of 75s.
This commit is contained in:
MegalithOfficial 2026-09-21 18:33:30 +02:00
parent 00bae41ebd
commit 60bb3cd429
No known key found for this signature in database
2 changed files with 64 additions and 9 deletions

View File

@ -97,7 +97,7 @@ pub fn stats() Stats {
/// Drops the handles nobody kept. This calls into the host, which is not
/// allowed from a post-return function, so it can't be left to `endCall`.
fn releaseHandles() void {
pub fn releaseHandles() void {
while (tracked.pop()) |item| item.drop(@bitCast(item.handle));
}
@ -419,6 +419,37 @@ pub fn liftFlat(comptime T: type, slots: []const u64) T {
}
}
// A plugin only ever looks at a few cases of the big variants (there are
// hundreds of events). These let it lift and lower one case without pulling
// in code for all the others.
pub fn loadTag(comptime U: type, ptr: [*]const u8) std.meta.Tag(U) {
return @enumFromInt(get(Discriminant(caseCount(U)), ptr));
}
pub fn loadCase(comptime U: type, comptime tag: std.meta.Tag(U), ptr: [*]const u8) @FieldType(U, @tagName(tag)) {
return load(@FieldType(U, @tagName(tag)), ptr + payloadOffset(U));
}
pub fn storeCase(comptime U: type, comptime tag: std.meta.Tag(U), value: @FieldType(U, @tagName(tag)), ptr: [*]u8) void {
put(Discriminant(caseCount(U)), ptr, @intFromEnum(tag));
store(@FieldType(U, @tagName(tag)), value, ptr + payloadOffset(U));
}
/// Offset of field `index` inside the record `T` in linear memory.
pub fn offsetOf(comptime T: type, comptime index: usize) comptime_int {
return fieldOffset(T, index);
}
/// Whether parameters of this shape arrive as a pointer instead of flat values.
pub fn passedInMemory(comptime Args: type) bool {
return flatten(Args).len > max_flat_params;
}
pub fn alloc(comptime T: type) [*]u8 {
return scratch(T);
}
fn put(comptime Int: type, ptr: [*]u8, value: Int) void {
std.mem.writeInt(Int, ptr[0..@sizeOf(Int)], value, .little);
}

View File

@ -122,15 +122,25 @@ pub fn on(
comptime handler: fn (Server, *EventData(event_type)) void,
options: EventOptions,
) void {
const tag = comptime std.meta.stringToEnum(std.meta.Tag(Event), @tagName(event_type)).?;
const glue = struct {
fn dispatch(srv: Server, ev: *Event) void {
handler(srv, &@field(ev, @tagName(event_type)));
// Only this event's payload gets lifted. Going through the whole
// `Event` union would compile code for every event there is.
fn dispatch(srv: Server, in: [*]const u8, out: [*]u8) void {
if (abi.loadTag(Event, in) != tag) {
@memcpy(out[0..abi.sizeOf(Event)], in[0..abi.sizeOf(Event)]);
return;
}
var data = abi.loadCase(Event, tag, in);
handler(srv, &data);
abi.storeCase(Event, tag, data, out);
}
};
ctx.registerEvent(handlerId(&glue.dispatch), event_type, options.priority, options.blocking);
}
pub const EventHandler = fn (Server, *Event) void;
const EventDispatch = fn (Server, in: [*]const u8, out: [*]u8) void;
pub const CommandHandler = fn (CommandSender, Server, ConsumedArgs) CommandResult;
pub const SuggestionHandler = fn (CommandSender, Server, command.SuggestionRequest) command.CommandSuggestions;
pub const TaskHandler = fn (Server) void;
@ -223,10 +233,23 @@ pub fn register(comptime Plugin: type) void {
return .ok;
}
fn handleEvent(id: u32, srv: Server, ev: Event) Event {
var out = ev;
handlerFromId(EventHandler, id)(srv, &out);
return out;
// Written by hand instead of going through `exports.handleEvent`, see `on`.
fn handleEvent(raw_args: i32) callconv(.c) i32 {
const Args = struct { u32, Server, Event };
comptime std.debug.assert(abi.passedInMemory(Args));
const args: [*]const u8 = @ptrFromInt(@as(u32, @bitCast(raw_args)));
const id = abi.load(u32, args + abi.offsetOf(Args, 0));
const srv = abi.load(Server, args + abi.offsetOf(Args, 1));
const out = abi.alloc(Event);
handlerFromId(EventDispatch, id)(srv, args + abi.offsetOf(Args, 2), out);
abi.releaseHandles();
return @bitCast(@as(u32, @intFromPtr(out)));
}
fn postHandleEvent(_: i32) callconv(.c) void {
abi.endCall();
}
fn handleCommand(id: u32, sender: CommandSender, srv: Server, args: ConsumedArgs) CommandResult {
@ -281,7 +304,8 @@ pub fn register(comptime Plugin: type) void {
exports.getMetadata(glue.getMetadata);
exports.onLoad(glue.onLoad);
exports.onUnload(glue.onUnload);
exports.handleEvent(glue.handleEvent);
@export(&glue.handleEvent, .{ .name = "handle-event" });
@export(&glue.postHandleEvent, .{ .name = "cabi_post_handle-event" });
exports.handleCommand(glue.handleCommand);
exports.handleCommandSuggestion(glue.handleCommandSuggestion);
exports.handleTask(glue.handleTask);