From 799e07e0fd1ceca5f4d5b5d27f0cfd44222716c2 Mon Sep 17 00:00:00 2001 From: luccie Date: Mon, 11 Aug 2025 23:52:27 +0200 Subject: [PATCH] [MODS/VM]: Changed calls to external functions to also be in Runtime.call --- src/mods/Parser.zig | 6 ++++ src/mods/external.zig | 36 +++++++++++++++++++++ src/mods/vm.zig | 75 +++++++++++++++++++++---------------------- 3 files changed, 78 insertions(+), 39 deletions(-) create mode 100644 src/mods/external.zig diff --git a/src/mods/Parser.zig b/src/mods/Parser.zig index 63ba62c..66dcbe8 100644 --- a/src/mods/Parser.zig +++ b/src/mods/Parser.zig @@ -401,6 +401,12 @@ fn parseImportsec(self: *Parser) !void { } self.functions = try self.allocator.realloc(self.functions, index + 1); self.functions[index].typ = .{ .external = index }; + self.functions[index].func_type = .{ + .parameters = try self.allocator.alloc(vm.Valtype, self.types[i.importdesc.func].parameters.len), + .returns = try self.allocator.alloc(vm.Valtype, self.types[i.importdesc.func].returns.len), + }; + @memcpy(self.functions[index].func_type.parameters, self.types[i.importdesc.func].parameters); + @memcpy(self.functions[index].func_type.returns, self.types[i.importdesc.func].returns); index += 1; }, .mem => { diff --git a/src/mods/external.zig b/src/mods/external.zig new file mode 100644 index 0000000..9deb08e --- /dev/null +++ b/src/mods/external.zig @@ -0,0 +1,36 @@ +const vmValue = @import("vm.zig").Value; +const vmRuntime = @import("vm.zig").Runtime; +const std = @import("std"); + +pub fn logDebug(self: *vmRuntime, params: []vmValue) ?vmValue { + const offset: usize = @intCast(params[0].i32); + const size: usize = @intCast(params[1].i64); + const ptr: []u8 = self.memory[offset .. offset + size]; + const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; + std.debug.print("[DEBUG]: {s}{c}", .{ptr, extra}); + return null; +} +pub fn logInfo(self: *vmRuntime, params: []vmValue) ?vmValue { + const offset: usize = @intCast(params[0].i32); + const size: usize = @intCast(params[1].i64); + const ptr: []u8 = self.memory[offset .. offset + size]; + const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; + std.debug.print("[INFO]: {s}{c}", .{ptr, extra}); + return null; +} +pub fn logWarn(self: *vmRuntime, params: []vmValue) ?vmValue { + const offset: usize = @intCast(params[0].i32); + const size: usize = @intCast(params[1].i64); + const ptr: []u8 = self.memory[offset .. offset + size]; + const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; + std.debug.print("[WARN]: {s}{c}", .{ptr, extra}); + return null; +} +pub fn logErr(self: *vmRuntime, params: []vmValue) ?vmValue { + const offset: usize = @intCast(params[0].i32); + const size: usize = @intCast(params[1].i64); + const ptr: []u8 = self.memory[offset .. offset + size]; + const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; + std.debug.print("[ERROR]: {s}{c}", .{ptr, extra}); + return null; +} \ No newline at end of file diff --git a/src/mods/vm.zig b/src/mods/vm.zig index 0ed0530..5809eb9 100644 --- a/src/mods/vm.zig +++ b/src/mods/vm.zig @@ -2,6 +2,7 @@ const std = @import("std"); const wasm = @import("wasm.zig"); const Parser = @import("Parser.zig"); const IR = @import("ir.zig"); +const External = @import("external.zig"); const Allocator = std.mem.Allocator; const AllocationError = error{OutOfMemory}; @@ -72,6 +73,7 @@ pub const Module = struct { }, .external => {} } + f.func_type.deinit(allocator); } allocator.free(self.functions); allocator.free(self.data); @@ -101,6 +103,11 @@ pub const Runtime = struct { stack: std.ArrayList(Value), memory: []u8, global_runtime: *wasm.GlobalRuntime, + externalFuncs: std.AutoHashMapUnmanaged(u32, ExternalFuncWrapper), + const ExternalFuncWrapper = struct { + func: *const fn (self: *Runtime, params: []Value) ?Value, + }; + pub fn init(allocator: Allocator, module: Module, global_runtime: *wasm.GlobalRuntime) !Runtime { // if memory max is not set the memory is allowed to grow but it is not supported at the moment @@ -108,7 +115,21 @@ pub const Runtime = struct { const memory = try allocator.alloc(u8, max); @memset(memory, 0); @memcpy(memory[0..module.data.len], module.data); + var externalFuncs: std.AutoHashMapUnmanaged(u32, ExternalFuncWrapper) = .{}; + if (module.exports.logDebug != null){ + try externalFuncs.put(allocator, module.exports.logDebug.?, .{.func = External.logDebug}); + } + if (module.exports.logInfo != null){ + try externalFuncs.put(allocator, module.exports.logInfo.?, .{.func = External.logInfo}); + } + if (module.exports.logWarn != null){ + try externalFuncs.put(allocator, module.exports.logWarn.?, .{.func = External.logWarn}); + } + if (module.exports.logErr != null){ + try externalFuncs.put(allocator, module.exports.logErr.?, .{.func = External.logErr}); + } return Runtime{ + .externalFuncs = externalFuncs, .module = module, .stack = try std.ArrayList(Value).initCapacity(allocator, 10), .memory = memory, @@ -118,8 +139,9 @@ pub const Runtime = struct { pub fn deinit(self: *Runtime, allocator: Allocator) void { self.stack.deinit(); - self.module.deinit(allocator); self.global_runtime.deinit(); + self.module.deinit(allocator); + self.externalFuncs.deinit(allocator); allocator.free(self.memory); } @@ -153,40 +175,13 @@ pub const Runtime = struct { continue; }, .@"return" => break :loop, - // TODO: Move this to callExternal .call => { - if (index.u32 == self.module.exports.logDebug) { - const size: usize = @intCast(self.stack.pop().?.i64); - const offset: usize = @intCast(self.stack.pop().?.i32); - const ptr: []u8 = self.memory[offset .. offset + size]; - const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; - std.debug.print("[DEBUG]: {s}{c}", .{ptr, extra}); - } else if (index.u32 == self.module.exports.logInfo) { - const size: usize = @intCast(self.stack.pop().?.i64); - const offset: usize = @intCast(self.stack.pop().?.i32); - const ptr: []u8 = self.memory[offset .. offset + size]; - const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; - std.debug.print("[INFO]: {s}{c}", .{ptr, extra}); - } else if (index.u32 == self.module.exports.logWarn) { - const size: usize = @intCast(self.stack.pop().?.i64); - const offset: usize = @intCast(self.stack.pop().?.i32); - const ptr: []u8 = self.memory[offset .. offset + size]; - const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; - std.debug.print("[WARN]: {s}{c}", .{ptr, extra}); - } else if (index.u32 == self.module.exports.logErr) { - const size: usize = @intCast(self.stack.pop().?.i64); - const offset: usize = @intCast(self.stack.pop().?.i32); - const ptr: []u8 = self.memory[offset .. offset + size]; - const extra: u8 = if (ptr.len > 0 and ptr[ptr.len - 1] != '\n') 0x0a else 0; - std.debug.print("[ERROR]: {s}{c}", .{ptr, extra}); - } else { - var parameters = std.ArrayList(Value).init(allocator); - defer parameters.deinit(); - for (self.module.functions[index.u32].func_type.parameters) |_| { - try parameters.append(self.stack.pop().?); - } - try self.call(allocator, index.u32, parameters.items); + var parameters = std.ArrayList(Value).init(allocator); + defer parameters.deinit(); + for (self.module.functions[index.u32].func_type.parameters) |_| { + try parameters.append(self.stack.pop().?); } + try self.call(allocator, index.u32, parameters.items); }, .call_indirect => { if (self.module.tables[index.indirect.x].et != std.wasm.RefType.funcref) { @@ -710,12 +705,14 @@ pub const Runtime = struct { allocator.free(frame.locals); }, .external => { - std.debug.panic("TODO: Handle external function {any} {any}\n", .{f.typ.external, self.module.exports}); - // TODO(ernesto): handle external functions - // const name = self.module.imports[f.external].name; - // if (self.global_runtime.functions.get(name)) |external| { - // external(&self.stack); - // } + const func = self.externalFuncs.get(@intCast(function)); + if (func == null){ + std.debug.panic("ERROR: WASM tried calling out of bounds external function\n", .{}); + } + const ret = func.?.func(self, parameters); + if (ret != null){ + try self.stack.append(ret.?); + } }, } }