Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ pub fn build(b: *std.Build) !void {
.optimize = optimize,
}),
});
tests.linkSystemLibrary("objc");
tests.linkFramework("Foundation");
tests.linkFramework("AppKit"); // Required by 'tagged pointer' test.
tests.root_module.linkSystemLibrary("objc", .{});
tests.root_module.linkFramework("Foundation", .{});
tests.root_module.linkFramework("AppKit", .{}); // Required by 'tagged pointer' test.
try addAppleSDK(b, tests.root_module);
b.installArtifact(tests);

Expand Down Expand Up @@ -68,6 +68,7 @@ pub fn addAppleSDK(b: *std.Build, m: *std.Build.Module) !void {
if (!gop.found_existing) {
gop.value_ptr.* = std.zig.system.darwin.getSdk(
b.allocator,
b.graph.io,
&m.resolved_target.?.result,
);
}
Expand Down
106 changes: 54 additions & 52 deletions src/block.zig
Original file line number Diff line number Diff line change
Expand Up @@ -150,21 +150,21 @@ pub fn Block(
/// the first arg. The first arg is a pointer so from an ABI perspective
/// this is always the same and can be safely casted.
fn FnType(comptime ContextArg: type) type {
var params: [Args.len + 1]std.builtin.Type.Fn.Param = undefined;
params[0] = .{ .is_generic = false, .is_noalias = false, .type = *const ContextArg };
var params: [Args.len + 1]std.builtin.Type.Fn.Param.Attributes = undefined;
var types: [Args.len + 1]type = undefined;
params[0] = .{};
types[0] = *const ContextArg;
for (Args, 1..) |Arg, i| {
params[i] = .{ .is_generic = false, .is_noalias = false, .type = Arg };
params[i] = .{};
types[i] = Arg;
}

return @Type(.{
.@"fn" = .{
.calling_convention = .c,
.is_generic = false,
.is_var_args = false,
.return_type = Return,
.params = &params,
},
});
return @Fn(
&types,
&params,
Return,
.{.@"callconv" = .c},
);
}
};
}
Expand All @@ -173,41 +173,43 @@ pub fn Block(
/// argument to any block invocation. See Block.
fn BlockContext(comptime Captures: type, comptime InvokeFn: type) type {
const captures_info = @typeInfo(Captures).@"struct";
var fields: [captures_info.fields.len + 5]std.builtin.Type.StructField = undefined;
fields[0] = .{
.name = "isa",
.type = ?*anyopaque,
var field_names: [captures_info.fields.len + 5][]const u8 = undefined;
var field_types: [captures_info.fields.len + 5]type = undefined ;
var field_attrs: [captures_info.fields.len + 5]std.builtin.Type.StructField.Attributes = undefined;
field_names[0] = "isa";
field_types[0] = ?*anyopaque;
field_attrs[0] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(*anyopaque),
.@"comptime" = false,
.@"align" = @alignOf(*anyopaque),
};
fields[1] = .{
.name = "flags",
.type = BlockFlags,
field_names[1] = "flags";
field_types[1] = BlockFlags;
field_attrs[1] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(c_int),
.@"comptime" = false,
.@"align" = @alignOf(c_int),
};
fields[2] = .{
.name = "reserved",
.type = c_int,
field_names[2] = "reserved";
field_types[2] = c_int;
field_attrs[2] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(c_int),
.@"comptime" = false,
.@"align" = @alignOf(c_int),
};
fields[3] = .{
.name = "invoke",
.type = *const InvokeFn,
field_names[3] = "invoke";
field_types[3] = *const InvokeFn;
field_attrs[3] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = @typeInfo(*const InvokeFn).pointer.alignment,
.@"comptime" = false,
.@"align" = @typeInfo(*const InvokeFn).pointer.alignment,
};
fields[4] = .{
.name = "descriptor",
.type = *const Descriptor,
field_names[4] = "descriptor";
field_types[4] = *const Descriptor;
field_attrs[4] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf(*Descriptor),
.@"comptime" = false,
.@"align" = @alignOf(*Descriptor),
};

for (captures_info.fields, 5..) |capture, i| {
Expand All @@ -216,24 +218,24 @@ fn BlockContext(comptime Captures: type, comptime InvokeFn: type) type {
comptime_float => @compileError("capture should not be a comptime_float, try using @as"),
else => {},
}

fields[i] = .{
.name = capture.name,
.type = capture.type,
field_names[i] = capture.name;
field_types[i] = capture.type;
field_attrs[i] = .{
.default_value_ptr = null,
.is_comptime = false,
.alignment = capture.alignment,
.@"comptime" = false,
.@"align" = capture.alignment,
};

}

return @Type(.{
.@"struct" = .{
.layout = .@"extern",
.fields = &fields,
.decls = &.{},
.is_tuple = false,
},
});
return @Struct(
.@"extern",
null,
&field_names,
&field_types,
&field_attrs
);
}

// Pointer to opaque instead of anyopaque: https://github.com/ziglang/zig/issues/18461
Expand Down
16 changes: 8 additions & 8 deletions src/encoding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ fn comptimeN(comptime T: type) usize {
const encoding = objc.Encoding.init(T);

// Figure out how much space we need
var stream: std.io.Writer.Discarding = .init(&.{});
stream.writer.print("{f}", .{encoding}) catch unreachable;
return stream.count;
var buffer: [512]u8 = undefined;
var discarding: std.Io.Writer.Discarding = .init(&buffer);
discarding.writer.print("{f}", .{encoding}) catch unreachable;
return discarding.fullCount();
}
}

Expand All @@ -23,11 +24,10 @@ pub fn comptimeEncode(comptime T: type) [comptimeN(T):0]u8 {

// Build our final signature
var buf: [comptimeN(T) + 1]u8 = undefined;
var fbs: std.io.Writer = .fixed(buf[0 .. buf.len - 1]);
fbs.print("{f}", .{encoding}) catch unreachable;
buf[buf.len - 1] = 0;
const result = std.fmt.bufPrint(buf[0 .. buf.len - 1], "{f}", .{encoding}) catch unreachable;
buf[result.len] = 0;

return buf[0 .. buf.len - 1 :0].*;
return buf[0..result.len :0].*;
}
}

Expand Down Expand Up @@ -107,7 +107,7 @@ pub const Encoding = union(enum) {

pub fn format(
comptime self: Encoding,
writer: *std.io.Writer,
writer: anytype,
) !void {
switch (self) {
.char => try writer.writeAll("c"),
Expand Down
107 changes: 82 additions & 25 deletions src/msg_send.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,31 @@ pub fn MsgSend(comptime T: type) type {
const Fn = MsgSendFn(RealReturn, @TypeOf(target.value), @TypeOf(args));
const msg_send_fn = comptime msgSendPtr(RealReturn, false);
const msg_send_ptr: *const Fn = @ptrCast(msg_send_fn);
const result = @call(.auto, msg_send_ptr, .{ target.value, sel.value } ++ args);
const target_id: c.id = @ptrCast(@alignCast(target.value));

// Convert Object arguments to c.id by extracting their .value field
const converted_args = comptime blk: {
const argsInfo = @typeInfo(@TypeOf(args)).@"struct";
var result_types: [argsInfo.fields.len]type = undefined;
for (argsInfo.fields, 0..) |field, i| {
result_types[i] = if (field.type == objc.Object) c.id else field.type;
}
break :blk result_types;
};

const final_args: std.meta.Tuple(&converted_args) = blk: {
const argsInfo = @typeInfo(@TypeOf(args)).@"struct";
var result: std.meta.Tuple(&converted_args) = undefined;
inline for (argsInfo.fields, 0..) |field, i| {
result[i] = if (field.type == objc.Object)
@field(args, field.name).value
else
@field(args, field.name);
}
break :blk result;
};

const result = @call(.auto, msg_send_ptr, .{ target_id, sel.value } ++ final_args);

if (!is_object) return result;
return .{ .value = result };
Expand All @@ -63,11 +87,36 @@ pub fn MsgSend(comptime T: type) type {
const Fn = MsgSendFn(RealReturn, *c.objc_super, @TypeOf(args));
const msg_send_fn = comptime msgSendPtr(RealReturn, true);
const msg_send_ptr: *const Fn = @ptrCast(msg_send_fn);
const target_id: c.id = @ptrCast(@alignCast(target.value));
const superclass_id: c.Class = @ptrCast(@alignCast(superclass.value));
var super: c.objc_super = .{
.receiver = target.value,
.class = superclass.value,
.receiver = target_id,
.super_class = superclass_id,
};
const result = @call(.auto, msg_send_ptr, .{ &super, sel.value } ++ args);

// Convert Object arguments to c.id by extracting their .value field
const converted_args = comptime blk: {
const argsInfo = @typeInfo(@TypeOf(args)).@"struct";
var result_types: [argsInfo.fields.len]type = undefined;
for (argsInfo.fields, 0..) |field, i| {
result_types[i] = if (field.type == objc.Object) c.id else field.type;
}
break :blk result_types;
};

const final_args: std.meta.Tuple(&converted_args) = blk: {
const argsInfo = @typeInfo(@TypeOf(args)).@"struct";
var result: std.meta.Tuple(&converted_args) = undefined;
inline for (argsInfo.fields, 0..) |field, i| {
result[i] = if (field.type == objc.Object)
@field(args, field.name).value
else
@field(args, field.name);
}
break :blk result;
};

const result = @call(.auto, msg_send_ptr, .{ &super, sel.value } ++ final_args);

if (!is_object) return result;
return .{ .value = result };
Expand Down Expand Up @@ -183,34 +232,42 @@ fn MsgSendFn(

// Build up our argument types.
const Fn = std.builtin.Type.Fn;
const params: []Fn.Param = params: {
var acc: [argsInfo.fields.len + 2]Fn.Param = undefined;
const params = params: {
var acc: [argsInfo.fields.len + 2]Fn.Param.Attributes = undefined;

// First argument is always the target and selector.
acc[0] = .{ .type = Target, .is_generic = false, .is_noalias = false };
acc[1] = .{ .type = c.SEL, .is_generic = false, .is_noalias = false };
acc[0] = .{ .@"noalias" = false };
acc[1] = .{ .@"noalias" = false };

// Remaining arguments depend on the args given, in the order given
for (argsInfo.fields, 0..) |field, i| {
acc[i + 2] = .{
.type = field.type,
.is_generic = false,
.is_noalias = false,
};
for (argsInfo.fields, 0..) |_, i| {
acc[i + 2] = .{ .@"noalias" = false };
}

break :params &acc;
};

return @Type(.{
.@"fn" = .{
.calling_convention = .c,
.is_generic = false,
.is_var_args = false,
.return_type = Return,
.params = params,
},
});
const types = types: {
var acc: [argsInfo.fields.len + 2]type = undefined;

// For msgSendSuper, Target is *c.objc_super (a pointer), use it as-is.
// For regular msgSend, Target might be a non-pointer type, so use c.id.
acc[0] = if (@typeInfo(Target) == .pointer) Target else c.id;
acc[1] = c.SEL;

for (argsInfo.fields, 0..) |field, i| {
// If an argument is objc.Object, use c.id instead since Object
// cannot be passed with C calling convention.
acc[i + 2] = if (field.type == objc.Object) c.id else field.type;
}

break :types &acc;
};

return @Fn(
types,
params,
Return,
.{ .@"callconv" = .c, .varargs = false },
);
}

test {
Expand Down
4 changes: 3 additions & 1 deletion src/object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ pub const Object = struct {

/// Returns the class name of a given object.
pub fn getClassName(self: Object) [:0]const u8 {
return std.mem.sliceTo(c.object_getClassName(self.value), 0);
const ptr = c.object_getClassName(self.value);
const len = std.mem.indexOfSentinel(u8, 0, ptr);
return ptr[0..len :0];
}

/// Set a property. This is a helper around getProperty and is
Expand Down
11 changes: 6 additions & 5 deletions src/property.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ pub const Property = extern struct {

/// Returns the name of a property.
pub fn getName(self: Property) [:0]const u8 {
return std.mem.sliceTo(c.property_getName(self.value), 0);
const ptr = c.property_getName(self.value);
const len = std.mem.indexOfSentinel(u8, 0, ptr);
return ptr[0..len :0];
}

/// Returns the value of a property attribute given the attribute name.
pub fn copyAttributeValue(self: Property, attr: [:0]const u8) ?[:0]u8 {
return std.mem.sliceTo(
c.property_copyAttributeValue(self.value, attr.ptr) orelse return null,
0,
);
const ptr = c.property_copyAttributeValue(self.value, attr.ptr) orelse return null;
const len = std.mem.indexOfSentinel(u8, 0, ptr);
return ptr[0..len :0];
}

comptime {
Expand Down
4 changes: 3 additions & 1 deletion src/protocol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub const Protocol = extern struct {
}

pub fn getName(self: Protocol) [:0]const u8 {
return std.mem.sliceTo(c.protocol_getName(self.value), 0);
const ptr = c.protocol_getName(self.value);
const len = std.mem.indexOfSentinel(u8, 0, ptr);
return ptr[0..len :0];
}

pub fn getProperty(
Expand Down
4 changes: 3 additions & 1 deletion src/sel.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ pub const Sel = struct {

/// Returns the name of the method specified by a given selector.
pub fn getName(self: Sel) [:0]const u8 {
return std.mem.sliceTo(c.sel_getName(self.value), 0);
const ptr = c.sel_getName(self.value);
const len = std.mem.indexOfSentinel(u8, 0, ptr);
return ptr[0..len :0];
}
};

Expand Down