1
0
Fork 0
This commit is contained in:
namedkitten 2020-09-17 18:21:24 +01:00
parent 5bd51782a6
commit 26d3b94a3b
4 changed files with 148 additions and 127 deletions

View file

@ -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));
comptime var obj_holder = obj;
return TInterface{
.vtable_ptr = &comptime makeVTable(TInterface.VTable, ImplType),
.storage = Comptime{
.erased_ptr = makeSelfPtr(&obj_holder),
.ImplType = @TypeOf(obj),
},
};
}
var obj = args[0];
return Comptime{
.erased_ptr = makeSelfPtr(&obj),
.ImplType = @TypeOf(args[0]),
};
}
@ -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.");
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),
},
};
}
return NonOwning{
.erased_ptr = makeSelfPtr(args[0]),
};
}
@ -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);
var ptr = try allocator.create(AllocT);
ptr.* = obj;
return TInterface{
.vtable_ptr = &comptime makeVTable(TInterface.VTable, PtrChildOrSelf(AllocT)),
.storage = Owning{
.allocator = allocator,
.mem = std.mem.asBytes(ptr)[0..],
},
};
}
const AllocT = @TypeOf(args[0]);
var obj = try args[1].create(AllocT);
obj.* = args[0];
return Owning{
.allocator = args[1],
.mem = std.mem.asBytes(obj)[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.");
}
const ImplSize = @sizeOf(@TypeOf(args[0]));
fn makeInit(comptime TInterface: type) type {
return struct {
fn init(value: anytype) !TInterface {
const ImplSize = @sizeOf(@TypeOf(value));
if (ImplSize > size) {
@compileError("Type does not fit in inline storage.");
}
var self: Self = undefined;
var self = Self{
.mem = 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,13 +258,12 @@ 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);
}
if (args.len > 0) {
const arg0_type = args[0].arg_type.?;
if (arg0_type != ImplT and arg0_type != *ImplT and arg0_type != *const ImplT) {
return null;
}
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) {
@ -265,6 +280,10 @@ fn getFunctionFromImpl(comptime name: []const u8, comptime FnT: type, comptime I
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) {
@ -300,6 +319,7 @@ fn getFunctionFromImpl(comptime name: []const u8, comptime FnT: type, comptime I
}.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,6 +466,7 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type {
return null;
} else @field(self.vtable_ptr, name);
if (is_method) {
const self_ptr = self.storage.getSelfPtr();
const new_args = .{self_ptr};
@ -463,6 +476,14 @@ pub fn Interface(comptime VTableT: type, comptime StorageT: type) type {
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
return await @asyncCall(&stack_frame, {}, fn_ptr, new_args ++ args);
}
} else {
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);
}
}
}
pub fn deinit(self: Self) void {

2
deps/time vendored

@ -1 +1 @@
Subproject commit 04d7cb5a54fbbc0399fd9299b71f461b5bf4ae2c
Subproject commit 6b5387fcd3007cf01c08b4f5136d24a6562be413

View file

@ -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", .{});

View file

@ -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", .{});