h
This commit is contained in:
parent
5bd51782a6
commit
26d3b94a3b
269
deps/interfaces/interface.zig
vendored
269
deps/interfaces/interface.zig
vendored
|
@ -6,7 +6,7 @@ const assert = std.debug.assert;
|
|||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
|
||||
pub const SelfType = @OpaqueType();
|
||||
pub const SelfType = @Type(.Opaque);
|
||||
|
||||
fn makeSelfPtr(ptr: anytype) *SelfType {
|
||||
if (comptime !trait.isSingleItemPtr(@TypeOf(ptr))) {
|
||||
|
@ -43,16 +43,21 @@ pub const Storage = struct {
|
|||
erased_ptr: *SelfType,
|
||||
ImplType: type,
|
||||
|
||||
pub fn init(args: anytype) !Comptime {
|
||||
if (args.len != 1) {
|
||||
@compileError("Comptime storage expected a 1-tuple in initialization.");
|
||||
}
|
||||
fn makeInit(comptime TInterface: type) type {
|
||||
return struct {
|
||||
fn init(obj: anytype) !TInterface {
|
||||
const ImplType = PtrChildOrSelf(@TypeOf(obj));
|
||||
|
||||
var obj = args[0];
|
||||
comptime var obj_holder = obj;
|
||||
|
||||
return Comptime{
|
||||
.erased_ptr = makeSelfPtr(&obj),
|
||||
.ImplType = @TypeOf(args[0]),
|
||||
return TInterface{
|
||||
.vtable_ptr = &comptime makeVTable(TInterface.VTable, ImplType),
|
||||
.storage = Comptime{
|
||||
.erased_ptr = makeSelfPtr(&obj_holder),
|
||||
.ImplType = @TypeOf(obj),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -66,13 +71,16 @@ pub const Storage = struct {
|
|||
pub const NonOwning = struct {
|
||||
erased_ptr: *SelfType,
|
||||
|
||||
pub fn init(args: anytype) !NonOwning {
|
||||
if (args.len != 1) {
|
||||
@compileError("NonOwning storage expected a 1-tuple in initialization.");
|
||||
}
|
||||
|
||||
return NonOwning{
|
||||
.erased_ptr = makeSelfPtr(args[0]),
|
||||
fn makeInit(comptime TInterface: type) type {
|
||||
return struct {
|
||||
fn init(ptr: anytype) !TInterface {
|
||||
return TInterface{
|
||||
.vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(@TypeOf(ptr))),
|
||||
.storage = NonOwning{
|
||||
.erased_ptr = makeSelfPtr(ptr),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -87,19 +95,22 @@ pub const Storage = struct {
|
|||
allocator: *mem.Allocator,
|
||||
mem: []u8,
|
||||
|
||||
pub fn init(args: anytype) !Owning {
|
||||
if (args.len != 2) {
|
||||
@compileError("Owning storage expected a 2-tuple in initialization.");
|
||||
}
|
||||
fn makeInit(comptime TInterface: type) type {
|
||||
return struct {
|
||||
fn init(obj: anytype, allocator: *std.mem.Allocator) !TInterface {
|
||||
const AllocT = @TypeOf(obj);
|
||||
|
||||
const AllocT = @TypeOf(args[0]);
|
||||
var ptr = try allocator.create(AllocT);
|
||||
ptr.* = obj;
|
||||
|
||||
var obj = try args[1].create(AllocT);
|
||||
obj.* = args[0];
|
||||
|
||||
return Owning{
|
||||
.allocator = args[1],
|
||||
.mem = std.mem.asBytes(obj)[0..],
|
||||
return TInterface{
|
||||
.vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(AllocT)),
|
||||
.storage = Owning{
|
||||
.allocator = allocator,
|
||||
.mem = std.mem.asBytes(ptr)[0..],
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -108,7 +119,7 @@ pub const Storage = struct {
|
|||
}
|
||||
|
||||
pub fn deinit(self: Owning) void {
|
||||
const result = self.allocator.shrinkBytes(self.mem, 0, 0);
|
||||
const result = self.allocator.shrinkBytes(self.mem, 0, 0, 0, 0);
|
||||
assert(result == 0);
|
||||
}
|
||||
};
|
||||
|
@ -119,23 +130,28 @@ pub const Storage = struct {
|
|||
|
||||
mem: [size]u8,
|
||||
|
||||
pub fn init(args: anytype) !Self {
|
||||
if (args.len != 1) {
|
||||
@compileError("Inline storage expected a 1-tuple in initialization.");
|
||||
}
|
||||
fn makeInit(comptime TInterface: type) type {
|
||||
return struct {
|
||||
fn init(value: anytype) !TInterface {
|
||||
const ImplSize = @sizeOf(@TypeOf(value));
|
||||
|
||||
const ImplSize = @sizeOf(@TypeOf(args[0]));
|
||||
if (ImplSize > size) {
|
||||
@compileError("Type does not fit in inline storage.");
|
||||
}
|
||||
|
||||
if (ImplSize > size) {
|
||||
@compileError("Type does not fit in inline storage.");
|
||||
}
|
||||
var self = Self{
|
||||
.mem = undefined,
|
||||
};
|
||||
if (ImplSize > 0) {
|
||||
std.mem.copy(u8, self.mem[0..], @ptrCast([*]const u8, &args[0])[0..ImplSize]);
|
||||
}
|
||||
|
||||
var self: Self = undefined;
|
||||
|
||||
if (ImplSize > 0) {
|
||||
std.mem.copy(u8, self.mem[0..], @ptrCast([*]const u8, &args[0])[0..ImplSize]);
|
||||
}
|
||||
return self;
|
||||
return TInterface{
|
||||
.vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(@TypeOf(value))),
|
||||
.storage = self,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getSelfPtr(self: *Self) *SelfType {
|
||||
|
@ -242,64 +258,68 @@ fn getFunctionFromImpl(comptime name: []const u8, comptime FnT: type, comptime I
|
|||
const args = @typeInfo(fn_decl.fn_type).Fn.args;
|
||||
|
||||
if (args.len == 0) {
|
||||
return null;
|
||||
return @field(ImplT, name);
|
||||
}
|
||||
|
||||
const arg0_type = args[0].arg_type.?;
|
||||
if (arg0_type != ImplT and arg0_type != *ImplT and arg0_type != *const ImplT) {
|
||||
return null;
|
||||
if (args.len > 0) {
|
||||
const arg0_type = args[0].arg_type.?;
|
||||
const is_method = arg0_type == ImplT or arg0_type == *ImplT or arg0_type == *const ImplT;
|
||||
|
||||
const candidate_cc = @typeInfo(fn_decl.fn_type).Fn.calling_convention;
|
||||
switch (candidate_cc) {
|
||||
.Async, .Unspecified => {},
|
||||
else => return null,
|
||||
}
|
||||
|
||||
const Return = @typeInfo(FnT).Fn.return_type orelse noreturn;
|
||||
const CurrSelfType = @typeInfo(FnT).Fn.args[0].arg_type.?;
|
||||
|
||||
const call_type: GenCallType = switch (our_cc) {
|
||||
.Async => if (candidate_cc == .Async) .BothAsync else .AsyncCallsBlocking,
|
||||
.Unspecified => if (candidate_cc == .Unspecified) .BothBlocking else .BlockingCallsAsync,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
if (!is_method) {
|
||||
return @field(ImplT, name);
|
||||
}
|
||||
|
||||
// TODO: Make this less hacky somehow?
|
||||
// We need some new feature to do so unfortunately.
|
||||
return switch (args.len) {
|
||||
1 => struct {
|
||||
fn impl(self_ptr: CurrSelfType) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{} });
|
||||
}
|
||||
}.impl,
|
||||
2 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg: args[1].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{arg} });
|
||||
}
|
||||
}.impl,
|
||||
3 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2 } });
|
||||
}
|
||||
}.impl,
|
||||
4 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3 } });
|
||||
}
|
||||
}.impl,
|
||||
5 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4 } });
|
||||
}
|
||||
}.impl,
|
||||
6 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?, arg5: args[5].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4, arg5 } });
|
||||
}
|
||||
}.impl,
|
||||
else => @compileError("Unsupported number of arguments, please provide a manually written vtable."),
|
||||
};
|
||||
}
|
||||
|
||||
const candidate_cc = @typeInfo(fn_decl.fn_type).Fn.calling_convention;
|
||||
switch (candidate_cc) {
|
||||
.Async, .Unspecified => {},
|
||||
else => return null,
|
||||
}
|
||||
|
||||
const Return = @typeInfo(FnT).Fn.return_type orelse noreturn;
|
||||
const CurrSelfType = @typeInfo(FnT).Fn.args[0].arg_type.?;
|
||||
|
||||
const call_type: GenCallType = switch (our_cc) {
|
||||
.Async => if (candidate_cc == .Async) .BothAsync else .AsyncCallsBlocking,
|
||||
.Unspecified => if (candidate_cc == .Unspecified) .BothBlocking else .BlockingCallsAsync,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// TODO: Make this less hacky somehow?
|
||||
// We need some new feature to do so unfortunately.
|
||||
return switch (args.len) {
|
||||
1 => struct {
|
||||
fn impl(self_ptr: CurrSelfType) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{} });
|
||||
}
|
||||
}.impl,
|
||||
2 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg: args[1].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{arg} });
|
||||
}
|
||||
}.impl,
|
||||
3 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2 } });
|
||||
}
|
||||
}.impl,
|
||||
4 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3 } });
|
||||
}
|
||||
}.impl,
|
||||
5 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4 } });
|
||||
}
|
||||
}.impl,
|
||||
6 => struct {
|
||||
fn impl(self_ptr: CurrSelfType, arg1: args[1].arg_type.?, arg2: args[2].arg_type.?, arg3: args[3].arg_type.?, arg4: args[4].arg_type.?, arg5: args[5].arg_type.?) callconv(our_cc) Return {
|
||||
return @call(.{ .modifier = .always_inline }, makeCall, .{ name, CurrSelfType, Return, ImplT, call_type, self_ptr, .{ arg1, arg2, arg3, arg4, arg5 } });
|
||||
}
|
||||
}.impl,
|
||||
else => @compileError("Unsupported number of arguments, please provide a manually written vtable."),
|
||||
};
|
||||
},
|
||||
else => return null,
|
||||
}
|
||||
|
@ -368,23 +388,19 @@ fn checkVtableType(comptime VTableT: type) void {
|
|||
.Unspecified, .Async => {},
|
||||
else => @compileError("Virtual function's '" ++ field.name ++ "' calling convention is not default or async."),
|
||||
}
|
||||
|
||||
if (type_info.Fn.args.len == 0) {
|
||||
@compileError("Virtual function '" ++ field.name ++ "' must have at least one argument.");
|
||||
}
|
||||
|
||||
const arg_type = type_info.Fn.args[0].arg_type.?;
|
||||
if (arg_type != *SelfType and arg_type != *const SelfType) {
|
||||
@compileError("Virtual function's '" ++ field.name ++ "' first argument must be *SelfType or *const SelfType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn vtableHasMethod(comptime VTableT: type, comptime name: []const u8, is_optional: *bool, is_async: *bool) bool {
|
||||
fn vtableHasMethod(comptime VTableT: type, comptime name: []const u8, is_optional: *bool, is_async: *bool, is_method: *bool) bool {
|
||||
for (std.meta.fields(VTableT)) |field| {
|
||||
if (std.mem.eql(u8, name, field.name)) {
|
||||
is_optional.* = trait.is(.Optional)(field.field_type);
|
||||
is_async.* = @typeInfo(if (is_optional.*) std.meta.Child(field.field_type) else field.field_type).Fn.calling_convention == .Async;
|
||||
const fn_typeinfo = @typeInfo(if (is_optional.*) std.meta.Child(field.field_type) else field.field_type).Fn;
|
||||
is_async.* = fn_typeinfo.calling_convention == .Async;
|
||||
is_method.* = fn_typeinfo.args.len > 0 and blk: {
|
||||
const first_arg_type = fn_typeinfo.args[0].arg_type.?;
|
||||
break :blk first_arg_type == *SelfType or first_arg_type == *const SelfType;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -426,27 +442,23 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type {
|
|||
storage: StorageT,
|
||||
|
||||
const Self = @This();
|
||||
const VTable = VTableT;
|
||||
const Storage = StorageT;
|
||||
|
||||
pub fn init(args: anytype) !Self {
|
||||
const ImplType = PtrChildOrSelf(@TypeOf(args.@"0"));
|
||||
|
||||
return Self{
|
||||
.vtable_ptr = &comptime makeVTable(VTableT, ImplType),
|
||||
.storage = try StorageT.init(args),
|
||||
};
|
||||
}
|
||||
pub const init = StorageT.makeInit(Self).init;
|
||||
|
||||
pub fn initWithVTable(vtable_ptr: *const VTableT, args: anytype) !Self {
|
||||
return .{
|
||||
.vtable_ptr = vtable_ptr,
|
||||
.storage = try StorageT.init(args),
|
||||
.storage = try init(args),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn call(self: anytype, comptime name: []const u8, args: anytype) VTableReturnType(VTableT, name) {
|
||||
comptime var is_optional = true;
|
||||
comptime var is_async = true;
|
||||
comptime assert(vtableHasMethod(VTableT, name, &is_optional, &is_async));
|
||||
comptime var is_method = true;
|
||||
comptime assert(vtableHasMethod(VTableT, name, &is_optional, &is_async, &is_method));
|
||||
|
||||
const fn_ptr = if (is_optional) blk: {
|
||||
const val = @field(self.vtable_ptr, name);
|
||||
|
@ -454,14 +466,23 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type {
|
|||
return null;
|
||||
} else @field(self.vtable_ptr, name);
|
||||
|
||||
const self_ptr = self.storage.getSelfPtr();
|
||||
const new_args = .{self_ptr};
|
||||
if (is_method) {
|
||||
const self_ptr = self.storage.getSelfPtr();
|
||||
const new_args = .{self_ptr};
|
||||
|
||||
if (!is_async) {
|
||||
return @call(.{}, fn_ptr, new_args ++ args);
|
||||
if (!is_async) {
|
||||
return @call(.{}, fn_ptr, new_args ++ args);
|
||||
} else {
|
||||
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
|
||||
return await @asyncCall(&stack_frame, {}, fn_ptr, new_args ++ args);
|
||||
}
|
||||
} else {
|
||||
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
|
||||
return await @asyncCall(&stack_frame, {}, fn_ptr, new_args ++ args);
|
||||
if (!is_async) {
|
||||
return @call(.{}, fn_ptr, args);
|
||||
} else {
|
||||
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
|
||||
return await @asyncCall(&stack_frame, {}, fn_ptr, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
deps/time
vendored
2
deps/time
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 04d7cb5a54fbbc0399fd9299b71f461b5bf4ae2c
|
||||
Subproject commit 6b5387fcd3007cf01c08b4f5136d24a6562be413
|
|
@ -11,7 +11,7 @@ pub const Bar = struct {
|
|||
}, interface.Storage.NonOwning);
|
||||
iface: IFace,
|
||||
pub fn init(impl_ptr: anytype) Bar {
|
||||
return .{ .iface = try IFace.init(.{impl_ptr}) };
|
||||
return .{ .iface = try IFace.init(impl_ptr) };
|
||||
}
|
||||
pub fn keep_running(self: *Bar) bool {
|
||||
return self.iface.call("keep_running", .{});
|
||||
|
|
|
@ -14,7 +14,7 @@ pub const Widget = struct {
|
|||
}, interface.Storage.NonOwning);
|
||||
iface: IFace,
|
||||
pub fn init(impl_ptr: anytype) Widget {
|
||||
return .{ .iface = try IFace.init(.{impl_ptr}) };
|
||||
return .{ .iface = try IFace.init(impl_ptr) };
|
||||
}
|
||||
pub fn name(self: *Widget) []const u8 {
|
||||
return self.iface.call("name", .{});
|
||||
|
|
Loading…
Reference in a new issue