Skip to content

Commit

Permalink
Use the linker for selectors instead of the runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbshaw committed Aug 18, 2024
1 parent a4f91f7 commit fc40767
Show file tree
Hide file tree
Showing 8 changed files with 1,728 additions and 6,275 deletions.
1 change: 1 addition & 0 deletions avf_audio_manual.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const c = @import("c.zig");
const cf = @import("core_foundation.zig");
const ns = @import("foundation.zig");
const objc = @import("objc.zig");

// ------------------------------------------------------------------------------------------------
// Types
Expand Down
40 changes: 9 additions & 31 deletions generator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,6 @@ fn Generator(comptime WriterType: type) type {
try self.generateEnumerations();
try self.generateContainers();
try self.generateClasses();
try self.generateSelectors();
try self.generateInit();
}

Expand All @@ -1077,22 +1076,9 @@ fn Generator(comptime WriterType: type) type {
}
}

fn generateSelectors(self: *Self) !void {
var it = self.selectors.iterator();
while (it.next()) |entry| {
const method_name = entry.key_ptr.*;
try self.writer.print("var sel_", .{});
try self.generateSelectorName(method_name);
try self.writer.print(": *c.objc_selector = undefined;\n", .{});
}
try self.writer.print("\n", .{});
}

fn generateInit(self: *Self) !void {
try self.writer.print("pub fn init() void {{\n", .{});
try self.generateInitClasses();
try self.writer.print("\n", .{});
try self.generateInitSelectors();
try self.writer.print("}}\n", .{});
}

Expand All @@ -1106,16 +1092,6 @@ fn Generator(comptime WriterType: type) type {
}
}

fn generateInitSelectors(self: *Self) !void {
var it = self.selectors.iterator();
while (it.next()) |entry| {
const method_name = entry.key_ptr.*;
try self.writer.print(" sel_", .{});
try self.generateSelectorName(method_name);
try self.writer.print(" = c.sel_registerName(\"{s}\").?;\n", .{method_name});
}
}

fn generateEnumerations(self: *Self) !void {
for (self.enums.items) |e| {
try self.writer.writeAll("\n");
Expand Down Expand Up @@ -1284,9 +1260,7 @@ fn Generator(comptime WriterType: type) type {
try self.generateType(method.return_type);
try self.writer.print(" {{\n", .{});
try self.generateBlockHelpers(method);
try self.writer.writeAll(" return @as(");
try self.generateObjcSignature(method);
try self.writer.writeAll(", @ptrCast(&c.objc_msgSend))(");
try self.writer.writeAll(" return objc.msgSend(");
try self.generateMethodArgs(method);
try self.writer.print(");\n", .{});
try self.writer.print(" }}\n", .{});
Expand Down Expand Up @@ -1395,17 +1369,21 @@ fn Generator(comptime WriterType: type) type {
} else {
try self.writer.print("T.class()", .{});
}
try self.writer.print(", sel_", .{});
try self.generateSelectorName(method.name);
try self.writer.print(", \"{s}\", ", .{method.name});
try self.generateType(method.return_type);
try self.writer.writeAll(", .{");

var first = true;
for (method.params.items) |param| {
try self.writer.writeAll(", ");
if (!first) try self.writer.writeAll(", ");
first = false;
if (getBlockType(param)) |_| {
try self.writer.writeAll("@ptrCast(&block)");
try self.writer.writeAll("&block");
} else {
try self.writer.print("{s}_", .{param.name});
}
}
try self.writer.writeAll("}");
}

fn getBlockType(param: Param) ?Type.Function {
Expand Down
1 change: 1 addition & 0 deletions metal_manual.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const c = @import("c.zig");
const cf = @import("core_foundation.zig");
const ns = @import("foundation.zig");
const objc = @import("objc.zig");

// ------------------------------------------------------------------------------------------------
// Opaque types
Expand Down
2,256 changes: 19 additions & 2,237 deletions src/app_kit.zig

Large diffs are not rendered by default.

393 changes: 98 additions & 295 deletions src/avf_audio.zig

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/core_midi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ const ns = @import("foundation.zig");

// ------------------------------------------------------------------------------------------------
// Types

pub fn init() void {}
5,247 changes: 1,538 additions & 3,709 deletions src/metal.zig

Large diffs are not rendered by default.

64 changes: 62 additions & 2 deletions src/objc.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,68 @@
const std = @import("std");
const builtin = @import("builtin");

extern fn objc_autoreleasePoolPop(pool: *anyopaque) void;
extern fn objc_autoreleasePoolPush() *anyopaque;

pub const autoreleasePoolPop = objc_autoreleasePoolPop;
pub const autoreleasePoolPush = objc_autoreleasePoolPush;

const c = @import("c.zig");
pub const Protocol = c.objc_object;
// APIs that are part of libobjc's public API.
pub const Protocol = opaque {};

/// Calls `objc_msgSend(receiver, selector, args...)` (or `objc_msgSend_stret` if needed).
///
/// Be careful. The return type and argument types *must* match the Objective-C method's signature.
/// No compile-time verification is performed.
pub fn msgSend(receiver: anytype, comptime selector: []const u8, return_type: type, args: anytype) return_type {
const n_colons = comptime std.mem.count(u8, selector, ":");
if (comptime n_colons != args.len) {
@compileError(std.fmt.comptimePrint(
"Selector `{s}` has {} argument{s}, but {} were given",
.{ selector, n_colons, (if (n_colons == 1) "" else "s"), args.len },
));
}

// TODO: Consider run-time signature verification if `builtin.mode == .Debug` (or use some other
// toggle). Register the selector, then call `class_getInstanceMethod()` or
// `class_getClassMethod()`, then call `method_getTypeEncoding()`, and then parse the string and
// validate it against `receiver` and `args`.

const fn_type = comptime init: {
var params: []const std.builtin.Type.Fn.Param = &.{
.{
.is_generic = false,
.is_noalias = false,
.type = @TypeOf(receiver),
},
.{
.is_generic = false,
.is_noalias = false,
.type = [*:0]c_char,
},
};
for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
params = params ++
.{.{
.is_generic = false,
.is_noalias = false,
.type = field.type,
}};
}
break :init std.builtin.Type{
.Fn = .{
.calling_convention = .C,
.is_generic = false,
.is_var_args = false,
.return_type = return_type,
.params = params,
},
};
};

const needs_fpret = comptime builtin.target.cpu.arch == .x86_64 and (return_type == f32 or return_type == f64);
const needs_stret = comptime builtin.target.cpu.arch == .x86_64 and @sizeOf(return_type) > 16;
const msg_send_fn_name = comptime if (needs_stret) "objc_msgSend_stret" else if (needs_fpret) "objc_msgSend_fpret" else "objc_msgSend";
const msg_send_fn = @extern(*const @Type(fn_type), .{ .name = msg_send_fn_name ++ "$" ++ selector });
return @call(.auto, msg_send_fn, .{ receiver, undefined } ++ args);
}

0 comments on commit fc40767

Please sign in to comment.