Added support for importing functions and begin working on a modding tools

This commit is contained in:
luccie-cmd 2025-08-04 16:03:06 +02:00
parent 59e3997056
commit e0d44a5f84
5 changed files with 132 additions and 62 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
.zig-cache/ .zig-cache/
zig-out/ zig-out/
.zigversion .zigversion
test.*

19
modding/log.zig Normal file
View file

@ -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;

View file

@ -18,29 +18,31 @@ pub fn main() !void {
const allocator = gpa.allocator(); const allocator = gpa.allocator();
defer if (gpa.deinit() != .ok) @panic("Leaked memory"); defer if (gpa.deinit() != .ok) @panic("Leaked memory");
//var global_runtime = mods.GlobalRuntime.init(allocator); var global_runtime = mods.GlobalRuntime.init(allocator);
//defer global_runtime.deinit(); defer global_runtime.deinit();
//try global_runtime.addFunction("debug", mods.Wasm.debug); try global_runtime.addFunction("debug", mods.Wasm.debug);
//const file = try std.fs.cwd().openFile("assets/core.wasm", .{}); //const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
//const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB const file = try std.fs.cwd().openFile("./test.wasm", .{});
//defer allocator.free(all); const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
//var parser = try mods.Parser.init(allocator, all); defer allocator.free(all);
//defer parser.deinit(); var parser = try mods.Parser.init(allocator, all);
//parser.parseModule() catch |err| { defer parser.deinit();
// std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] }); parser.parseModule() catch |err| {
// return 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 module = parser.module();
// defer module.deinit(allocator);
//var runtime = try mods.Runtime.init(allocator, module, &global_runtime); var runtime = try mods.Runtime.init(allocator, module, &global_runtime);
//defer runtime.deinit(allocator); defer runtime.deinit(allocator);
var parameters = [_]mods.VM.Value{.{ .i32 = 17 }};
try runtime.callExternal(allocator, .preinit, &parameters);
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, &parameters);
//const result = runtime.stack.pop().?;
//std.debug.print("Result of preinit: {any}\n", .{result});
//var w = try Renderer.Window.create(800, 600, "sideros"); //var w = try Renderer.Window.create(800, 600, "sideros");
//defer w.destroy(); //defer w.destroy();

View file

@ -11,6 +11,7 @@ types: []vm.Functype,
functions: []vm.Function, functions: []vm.Function,
memory: Memtype, memory: Memtype,
exports: vm.Exports, exports: vm.Exports,
importCount: u32,
const Parser = @This(); const Parser = @This();
@ -39,41 +40,42 @@ pub const Error = error{
}; };
pub fn init(allocator: Allocator, bytes: []const u8) !Parser { pub fn init(allocator: Allocator, bytes: []const u8) !Parser {
return .{ return .{
.bytes = bytes, .importCount = 0,
.byte_idx = 0, .bytes = bytes,
.allocator = allocator, .byte_idx = 0,
.types = &.{}, .allocator = allocator,
.functions = &.{}, .types = &.{},
.memory = .{ .functions = &.{},
.lim = .{ .memory = .{
.min = 0, .lim = .{
.max = 0, .min = 0,
}, .max = 0,
}, },
.exports = .{}, },
}; .exports = .{},
};
} }
pub fn deinit(self: Parser) void { pub fn deinit(self: Parser) void {
for (self.types) |t| { for (self.types) |t| {
self.allocator.free(t.parameters); self.allocator.free(t.parameters);
self.allocator.free(t.returns); self.allocator.free(t.returns);
} }
self.allocator.free(self.types); self.allocator.free(self.types);
self.allocator.free(self.functions); self.allocator.free(self.functions);
} }
pub fn module(self: *Parser) vm.Module { pub fn module(self: *Parser) vm.Module {
defer self.functions = &.{}; defer self.functions = &.{};
return .{ return .{
.memory = .{ .memory = .{
.min = self.memory.lim.min, .min = self.memory.lim.min,
.max = self.memory.lim.max, .max = self.memory.lim.max,
}, },
.functions = self.functions, .functions = self.functions,
.exports = self.exports, .exports = self.exports,
}; };
} }
// TODO: This function should not exists // TODO: This function should not exists
@ -292,6 +294,21 @@ pub fn parseModule(self: *Parser) !void {
else => return Error.invalid_section, 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 { fn parseCustomsec(self: *Parser) !void {
@ -316,7 +333,7 @@ fn parseTypesec(self: *Parser) !void {
pub const Import = struct { pub const Import = struct {
name: []const u8, name: []const u8,
module: []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 { pub fn deinit(self: Import, allocator: Allocator) void {
allocator.free(self.name); allocator.free(self.name);
allocator.free(self.module); allocator.free(self.module);
@ -324,8 +341,8 @@ pub const Import = struct {
}; };
fn parseImport(self: *Parser) !Import { fn parseImport(self: *Parser) !Import {
return .{ return .{
.name = try self.readName(),
.module = try self.readName(), .module = try self.readName(),
.name = try self.readName(),
.importdesc = switch (try self.readByte()) { .importdesc = switch (try self.readByte()) {
0x00 => .{ .func = try self.readU32() }, 0x00 => .{ .func = try self.readU32() },
0x01 => .{ .table = try self.parseTabletype() }, 0x01 => .{ .table = try self.parseTabletype() },
@ -342,6 +359,26 @@ fn parseImportsec(self: *Parser) !void {
// TODO(ernesto): this should be used to do name resolution. // TODO(ernesto): this should be used to do name resolution.
const imports = try self.parseVector(Parser.parseImport); 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); defer self.allocator.free(imports);
// TODO: run this check not only on debug // TODO: run this check not only on debug
@ -360,8 +397,8 @@ fn parseFuncsec(self: *Parser) !void {
for (types, 0..) |t, i| { for (types, 0..) |t, i| {
self.functions[i].func_type = .{ self.functions[i].func_type = .{
.parameters = try self.allocator.alloc(vm.Valtype, self.types[t].parameters.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), .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.parameters, self.types[t].parameters);
@memcpy(self.functions[i].func_type.returns, self.types[t].returns); @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); const exports = try self.parseVector(Parser.parseExport);
defer { defer {
for (exports) |e| self.allocator.free(e.name); for (exports) |e| self.allocator.free(e.name);
self.allocator.free(exports); self.allocator.free(exports);
} }
for (exports) |e| { for (exports) |e| {
switch (e.exportdesc) { switch (e.exportdesc) {
.func => { .func => {
if (std.mem.eql(u8, e.name, "preinit")) { if (std.mem.eql(u8, e.name, "preinit")) {
self.exports.preinit = e.exportdesc.func; self.exports.preinit = e.exportdesc.func;
} else { } else {
std.log.warn("exported function {s} not supported\n", .{e.name}); std.log.warn("exported function {s} not supported\n", .{e.name});
} }
}, },
else => std.debug.print("[WARN]: export ignored\n", .{}), else => std.debug.print("[WARN]: export ignored\n", .{}),
} }

View file

@ -37,9 +37,17 @@ pub const Function = struct {
pub const ExportFunction = enum { pub const ExportFunction = enum {
preinit, preinit,
logErr,
logWarn,
logInfo,
logDebug,
}; };
pub const Exports = struct { pub const Exports = struct {
preinit: ?u32 = null, preinit: ?u32 = null,
logErr: ?u32 = null,
logWarn: ?u32 = null,
logInfo: ?u32 = null,
logDebug: ?u32 = null,
}; };
comptime { comptime {
std.debug.assert(@typeInfo(ExportFunction).@"enum".fields.len == @typeInfo(Exports).@"struct".fields.len ); 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]; const index = frame.code.indices[frame.program_counter];
switch (opcode) { switch (opcode) {
.@"unreachable" => { .@"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 => {}, .nop => {},
.br => { .br => {
@ -433,6 +441,9 @@ pub const Runtime = struct {
std.debug.panic("Function preinit unavailable\n", .{}); 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); allocator.free(frame.locals);
}, },
.external => { .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 // TODO(ernesto): handle external functions
// const name = self.module.imports[f.external].name; // const name = self.module.imports[f.external].name;
// if (self.global_runtime.functions.get(name)) |external| { // if (self.global_runtime.functions.get(name)) |external| {