Re-implement native function binding / calling
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details

This commit is contained in:
kayomn 2023-08-12 14:11:19 +01:00
parent bc3b9051dd
commit b012e1e5f6
3 changed files with 68 additions and 18 deletions

View File

@ -8,6 +8,6 @@ options = {
.tick_rate = 60,
}
# @log_info(options.title)
@log_info(options.title)
return options

View File

@ -27,7 +27,7 @@ pub const RuntimeEnv = struct {
interned_symbols: SymbolSet,
allocator: coral.io.Allocator,
error_handler: ErrorHandler,
syscallables: RefTable,
system_bindings: RefTable,
locals: RefList,
frames: FrameStack,
@ -348,7 +348,7 @@ pub const RuntimeEnv = struct {
},
.push_system => |push_system| {
if (self.env.syscallables.lookup(self.constants.values[push_system])) |syscallable| {
if (self.env.system_bindings.lookup(self.constants.values[push_system])) |syscallable| {
try self.env.locals.push_one(try self.env.acquire(syscallable));
} else {
try self.env.locals.push_one(null);
@ -413,12 +413,20 @@ pub const RuntimeEnv = struct {
try self.env.set(indexable, index, value);
},
.object_call => {
.object_call => |object_call| {
const result = call: {
const callable = try self.env.expect(try self.pop_local());
defer self.env.discard(callable);
try self.env.frames.push_one(.{
.name = "<lambda>",
.arg_count = object_call,
.locals_top = self.env.locals.values.len,
});
defer coral.debug.assert(self.env.frames.pop() != null);
break: call try switch (callable.object().payload) {
.dynamic => |dynamic| dynamic.typeinfo().call(.{
.userdata = dynamic.userdata(),
@ -429,6 +437,12 @@ pub const RuntimeEnv = struct {
};
};
for (0 .. object_call) |_| {
if (try self.pop_local()) |popped_arg| {
self.env.discard(popped_arg);
}
}
errdefer {
if (result) |ref| {
self.env.discard(ref);
@ -901,8 +915,19 @@ pub const RuntimeEnv = struct {
return null;
}
pub fn bind_syscaller(_: *RuntimeEnv, comptime _: []const coral.io.Byte, _: Caller) RuntimeError!void {
pub fn bind_system(self: *RuntimeEnv, name: []const coral.io.Byte, value: *const RuntimeRef) RuntimeError!void {
const name_symbol = try self.new_symbol(name);
errdefer self.discard(name_symbol);
const acquired_value = try self.acquire(value);
errdefer self.discard(acquired_value);
if (try self.system_bindings.replace(name_symbol, acquired_value)) |replaced| {
self.discard(replaced.key);
self.discard(replaced.value);
}
}
pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef {
@ -1006,16 +1031,17 @@ pub const RuntimeEnv = struct {
}
{
var iterable = self.syscallables.as_iterable();
var iterable = self.system_bindings.as_iterable();
while (iterable.next()) |entry| {
self.discard(entry.key);
self.discard(entry.value);
}
}
self.frames.free();
self.locals.free();
self.syscallables.free();
self.system_bindings.free();
self.interned_symbols.free();
}
@ -1042,7 +1068,7 @@ pub const RuntimeEnv = struct {
return RuntimeEnv{
.locals = RefList.make(allocator),
.frames = FrameStack.make(allocator),
.syscallables = RefTable.make(allocator, .{}),
.system_bindings = RefTable.make(allocator, .{}),
.interned_symbols = SymbolSet.make(allocator, .{}),
.error_handler = error_handler,
.allocator = allocator,
@ -1056,13 +1082,17 @@ pub const RuntimeEnv = struct {
});
}
pub fn new_dynamic(self: *RuntimeEnv, typeinfo: *const Typeinfo) RuntimeError!*RuntimeRef {
pub fn new_dynamic(
self: *RuntimeEnv,
userdata: [*]const coral.io.Byte,
typeinfo: *const Typeinfo,
) RuntimeError!*RuntimeRef {
const dynamic = try self.allocator.reallocate(null, @sizeOf(usize) + typeinfo.size);
errdefer self.allocator.deallocate(dynamic);
coral.io.copy(dynamic, coral.io.bytes_of(&typeinfo));
coral.io.zero(dynamic[@sizeOf(usize) ..]);
coral.io.copy(dynamic[@sizeOf(usize) ..], userdata[0 .. typeinfo.size]);
return RuntimeRef.allocate(self.allocator, .{
.ref_count = 1,
@ -1134,17 +1164,13 @@ pub const RuntimeEnv = struct {
table.contiguous.free();
}
const newed = try self.new_dynamic(&.{
return try self.new_dynamic(coral.io.bytes_of(&table).ptr, &.{
.name = "table",
.size = @sizeOf(Table),
.destruct = Table.typeinfo_destruct,
.get = Table.typeinfo_get,
.set = Table.typeinfo_set,
});
coral.io.copy(self.unbox_dynamic(newed) catch unreachable, coral.io.bytes_of(&table));
return newed;
}
pub fn raise(self: *RuntimeEnv, error_value: RuntimeError, message: []const coral.io.Byte) RuntimeError {
@ -1364,3 +1390,27 @@ pub const Typeinfo = struct {
return method.env.raise(error.TypeMismatch, "object is not index-settable");
}
};
pub fn bind_syscaller(env: *RuntimeEnv, name: []const coral.io.Byte, caller: Caller) RuntimeError!void {
const callable = try new_caller(env, caller);
defer env.discard(callable);
try env.bind_system(name, callable);
}
pub fn new_caller(env: *RuntimeEnv, value: Caller) RuntimeError!*RuntimeRef {
const Callable = struct {
fn call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef {
coral.debug.assert(method.userdata.len == @sizeOf(Caller));
return @as(*Caller, @ptrCast(@alignCast(method.userdata))).invoke(method.env);
}
};
return env.new_dynamic(coral.io.bytes_of(&value).ptr, &.{
.name = "<native>",
.size = @sizeOf(Caller),
.call = Callable.call,
});
}

View File

@ -75,15 +75,15 @@ pub fn run_app(file_access: file.Access) void {
defer script_env.free();
script_env.bind_syscaller("log_info", kym.Caller.from(kym_log_info)) catch {
kym.bind_syscaller(&script_env, "log_info", kym.Caller.from(kym_log_info)) catch {
return app.log_fail("failed to bind `log_info` syscall");
};
script_env.bind_syscaller("log_warn", kym.Caller.from(kym_log_warn)) catch {
kym.bind_syscaller(&script_env, "log_warn", kym.Caller.from(kym_log_warn)) catch {
return app.log_fail("failed to bind `log_warn` syscall");
};
script_env.bind_syscaller("log_fail", kym.Caller.from(kym_log_fail)) catch {
kym.bind_syscaller(&script_env, "log_fail", kym.Caller.from(kym_log_fail)) catch {
return app.log_fail("failed to bind `log_fail` syscall");
};