Added support for importing functions and begin working on a modding tools
This commit is contained in:
parent
59e3997056
commit
e0d44a5f84
5 changed files with 132 additions and 62 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
.zig-cache/
|
||||
zig-out/
|
||||
.zigversion
|
||||
test.*
|
||||
19
modding/log.zig
Normal file
19
modding/log.zig
Normal 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;
|
||||
40
src/main.zig
40
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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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", .{}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue