diff --git a/.gitignore b/.gitignore index ff7af6e..7852c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .zig-cache/ zig-out/ -.zigversion \ No newline at end of file +.zigversion +test.* \ No newline at end of file diff --git a/modding/log.zig b/modding/log.zig new file mode 100644 index 0000000..53298d2 --- /dev/null +++ b/modding/log.zig @@ -0,0 +1,19 @@ +pub extern fn logErr( + format: [*:0]const u8, + ..., +) void; + +pub extern fn logWarn( + format: [*:0]const u8, + ..., +) void; + +pub extern fn logInfo( + format: [*:0]const u8, + ..., +) void; + +pub extern fn logDebug( + format: [*:0]const u8, + ..., +) void; diff --git a/src/main.zig b/src/main.zig index ec4367b..0d93b0b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,29 +18,31 @@ pub fn main() !void { const allocator = gpa.allocator(); defer if (gpa.deinit() != .ok) @panic("Leaked memory"); - //var global_runtime = mods.GlobalRuntime.init(allocator); - //defer global_runtime.deinit(); - //try global_runtime.addFunction("debug", mods.Wasm.debug); + var global_runtime = mods.GlobalRuntime.init(allocator); + defer global_runtime.deinit(); + try global_runtime.addFunction("debug", mods.Wasm.debug); //const file = try std.fs.cwd().openFile("assets/core.wasm", .{}); - //const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB - //defer allocator.free(all); - //var parser = try mods.Parser.init(allocator, all); - //defer parser.deinit(); - //parser.parseModule() catch |err| { - // std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] }); - // return err; - //}; - //const module = parser.module(); - //// defer module.deinit(allocator); + const file = try std.fs.cwd().openFile("./test.wasm", .{}); + const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB + defer allocator.free(all); + var parser = try mods.Parser.init(allocator, all); + defer parser.deinit(); + parser.parseModule() catch |err| { + std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] }); + return err; + }; + const module = parser.module(); + // defer module.deinit(allocator); - //var runtime = try mods.Runtime.init(allocator, module, &global_runtime); - //defer runtime.deinit(allocator); + var runtime = try mods.Runtime.init(allocator, module, &global_runtime); + defer runtime.deinit(allocator); + + var parameters = [_]mods.VM.Value{.{ .i32 = 17 }}; + try runtime.callExternal(allocator, .preinit, ¶meters); + const result = runtime.stack.pop().?; + std.debug.print("Result of preinit: {any}\n", .{result}); - //var parameters = [_]mods.VM.Value{.{ .i32 = 17 }}; - //try runtime.callExternal(allocator, .preinit, ¶meters); - //const result = runtime.stack.pop().?; - //std.debug.print("Result of preinit: {any}\n", .{result}); //var w = try Renderer.Window.create(800, 600, "sideros"); //defer w.destroy(); diff --git a/src/mods/Parser.zig b/src/mods/Parser.zig index a37f8be..624e2b5 100644 --- a/src/mods/Parser.zig +++ b/src/mods/Parser.zig @@ -11,6 +11,7 @@ types: []vm.Functype, functions: []vm.Function, memory: Memtype, exports: vm.Exports, +importCount: u32, const Parser = @This(); @@ -39,41 +40,42 @@ pub const Error = error{ }; pub fn init(allocator: Allocator, bytes: []const u8) !Parser { - return .{ - .bytes = bytes, - .byte_idx = 0, - .allocator = allocator, - .types = &.{}, - .functions = &.{}, - .memory = .{ - .lim = .{ - .min = 0, - .max = 0, - }, - }, - .exports = .{}, - }; + return .{ + .importCount = 0, + .bytes = bytes, + .byte_idx = 0, + .allocator = allocator, + .types = &.{}, + .functions = &.{}, + .memory = .{ + .lim = .{ + .min = 0, + .max = 0, + }, + }, + .exports = .{}, + }; } pub fn deinit(self: Parser) void { - for (self.types) |t| { - self.allocator.free(t.parameters); - self.allocator.free(t.returns); - } - self.allocator.free(self.types); - self.allocator.free(self.functions); + for (self.types) |t| { + self.allocator.free(t.parameters); + self.allocator.free(t.returns); + } + self.allocator.free(self.types); + self.allocator.free(self.functions); } pub fn module(self: *Parser) vm.Module { - defer self.functions = &.{}; - return .{ - .memory = .{ - .min = self.memory.lim.min, - .max = self.memory.lim.max, - }, - .functions = self.functions, - .exports = self.exports, - }; + defer self.functions = &.{}; + return .{ + .memory = .{ + .min = self.memory.lim.min, + .max = self.memory.lim.max, + }, + .functions = self.functions, + .exports = self.exports, + }; } // TODO: This function should not exists @@ -292,6 +294,21 @@ pub fn parseModule(self: *Parser) !void { else => return Error.invalid_section, }; } + if (self.exports.preinit != null and self.exports.preinit.? != 0){ + self.exports.preinit.? -= self.importCount; + } + if (self.exports.logDebug != null and self.exports.logDebug.? != 0){ + self.exports.logDebug.? -= self.importCount; + } + if (self.exports.logInfo != null and self.exports.logInfo.? != 0){ + self.exports.logInfo.? -= self.importCount; + } + if (self.exports.logWarn != null and self.exports.logWarn.? != 0){ + self.exports.logWarn.? -= self.importCount; + } + if (self.exports.logErr != null and self.exports.logErr.? != 0){ + self.exports.logErr.? -= self.importCount; + } } fn parseCustomsec(self: *Parser) !void { @@ -316,7 +333,7 @@ fn parseTypesec(self: *Parser) !void { pub const Import = struct { name: []const u8, module: []const u8, - importdesc: union { func: u32, table: Tabletype, mem: Memtype, global: Globaltype }, + importdesc: union(enum) { func: u32, table: Tabletype, mem: Memtype, global: Globaltype }, pub fn deinit(self: Import, allocator: Allocator) void { allocator.free(self.name); allocator.free(self.module); @@ -324,8 +341,8 @@ pub const Import = struct { }; fn parseImport(self: *Parser) !Import { return .{ - .name = try self.readName(), .module = try self.readName(), + .name = try self.readName(), .importdesc = switch (try self.readByte()) { 0x00 => .{ .func = try self.readU32() }, 0x01 => .{ .table = try self.parseTabletype() }, @@ -342,6 +359,26 @@ fn parseImportsec(self: *Parser) !void { // TODO(ernesto): this should be used to do name resolution. const imports = try self.parseVector(Parser.parseImport); + self.importCount = @intCast(imports.len); + + for (imports) |i| { + switch (i.importdesc) { + .func => { + if (std.mem.eql(u8, i.name, "logDebug")) { + self.exports.logDebug = i.importdesc.func; + } else if (std.mem.eql(u8, i.name, "logInfo")) { + self.exports.logInfo = i.importdesc.func; + } else if (std.mem.eql(u8, i.name, "logWarn")) { + self.exports.logWarn = i.importdesc.func; + } else if (std.mem.eql(u8, i.name, "logErr")) { + self.exports.logErr = i.importdesc.func; + } else { + std.log.warn("imported function {s} not supported\n", .{i.name}); + } + }, + else => std.debug.print("[TODO]: Handle import desc {any}\n", .{i.importdesc}), + } + } defer self.allocator.free(imports); // TODO: run this check not only on debug @@ -360,8 +397,8 @@ fn parseFuncsec(self: *Parser) !void { for (types, 0..) |t, i| { self.functions[i].func_type = .{ - .parameters = try self.allocator.alloc(vm.Valtype, self.types[t].parameters.len), - .returns = try self.allocator.alloc(vm.Valtype, self.types[t].returns.len), + .parameters = try self.allocator.alloc(vm.Valtype, self.types[t].parameters.len), + .returns = try self.allocator.alloc(vm.Valtype, self.types[t].returns.len), }; @memcpy(self.functions[i].func_type.parameters, self.types[t].parameters); @memcpy(self.functions[i].func_type.returns, self.types[t].returns); @@ -431,17 +468,17 @@ fn parseExportsec(self: *Parser) !void { const exports = try self.parseVector(Parser.parseExport); defer { - for (exports) |e| self.allocator.free(e.name); - self.allocator.free(exports); + for (exports) |e| self.allocator.free(e.name); + self.allocator.free(exports); } for (exports) |e| { switch (e.exportdesc) { .func => { - if (std.mem.eql(u8, e.name, "preinit")) { - self.exports.preinit = e.exportdesc.func; - } else { - std.log.warn("exported function {s} not supported\n", .{e.name}); - } + if (std.mem.eql(u8, e.name, "preinit")) { + self.exports.preinit = e.exportdesc.func; + } else { + std.log.warn("exported function {s} not supported\n", .{e.name}); + } }, else => std.debug.print("[WARN]: export ignored\n", .{}), } diff --git a/src/mods/vm.zig b/src/mods/vm.zig index aa9a5a5..0066e24 100644 --- a/src/mods/vm.zig +++ b/src/mods/vm.zig @@ -37,9 +37,17 @@ pub const Function = struct { pub const ExportFunction = enum { preinit, + logErr, + logWarn, + logInfo, + logDebug, }; pub const Exports = struct { preinit: ?u32 = null, + logErr: ?u32 = null, + logWarn: ?u32 = null, + logInfo: ?u32 = null, + logDebug: ?u32 = null, }; comptime { std.debug.assert(@typeInfo(ExportFunction).@"enum".fields.len == @typeInfo(Exports).@"struct".fields.len ); @@ -117,7 +125,7 @@ pub const Runtime = struct { const index = frame.code.indices[frame.program_counter]; switch (opcode) { .@"unreachable" => { - std.log.err("Reached unreachable statement at IR counter {any}\n", .{frame.program_counter}); + std.debug.panic("Reached unreachable statement at IR counter {any}\n", .{frame.program_counter}); }, .nop => {}, .br => { @@ -433,6 +441,9 @@ pub const Runtime = struct { std.debug.panic("Function preinit unavailable\n", .{}); } }, + else => { + std.debug.panic("Function {any} not handled\n", .{name}); + } } } @@ -470,7 +481,7 @@ pub const Runtime = struct { allocator.free(frame.locals); }, .external => { - std.log.err("TODO: Handle external function {any}\n", .{function}); + std.debug.panic("TODO: Handle external function {any}\n", .{function}); // TODO(ernesto): handle external functions // const name = self.module.imports[f.external].name; // if (self.global_runtime.functions.get(name)) |external| {