summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.zig15
-rw-r--r--src/mods/Parser.zig93
-rw-r--r--src/mods/vm.zig51
3 files changed, 115 insertions, 44 deletions
diff --git a/src/main.zig b/src/main.zig
index dffaa4d..72c2d91 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -21,20 +21,21 @@ pub fn main() !void {
const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
- var parser = mods.Parser{
- .bytes = all,
- .byte_idx = 0,
- .allocator = allocator,
- };
- const module = parser.parseModule() catch |err| {
+ 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 parameters = [_]mods.VM.Value{.{ .i32 = 17 }};
- try runtime.callExternal(allocator, "preinit", &parameters);
+ 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");
diff --git a/src/mods/Parser.zig b/src/mods/Parser.zig
index 544ef31..05824e3 100644
--- a/src/mods/Parser.zig
+++ b/src/mods/Parser.zig
@@ -7,10 +7,10 @@ bytes: []const u8,
byte_idx: usize,
allocator: Allocator,
-types: ?[]vm.Functype = null,
-functions: ?[]vm.Function = null,
-memory: ?Memtype = null,
-exports: std.StringHashMapUnmanaged(u32) = .{},
+types: []vm.Functype,
+functions: []vm.Function,
+memory: Memtype,
+exports: vm.Exports,
const Parser = @This();
@@ -33,10 +33,49 @@ pub const Error = error{
invalid_exportdesc,
double_else,
duplicated_funcsec,
+ duplicated_typesec,
unresolved_branch,
unterminated_wasm,
};
+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 = .{},
+ };
+}
+
+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);
+}
+
+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,
+ };
+}
+
// TODO: This function should not exists
fn warn(self: Parser, s: []const u8) void {
std.debug.print("[WARN]: Parsing of {s} unimplemented at byte index {d}\n", .{ s, self.byte_idx });
@@ -231,7 +270,7 @@ fn parseGlobaltype(self: *Parser) !Globaltype {
// ===========
// NOTE: This should not return anything but modify IR
-pub fn parseModule(self: *Parser) !vm.Module {
+pub fn parseModule(self: *Parser) !void {
if (!std.mem.eql(u8, try self.read(4), &.{ 0x00, 0x61, 0x73, 0x6d })) return Error.invalid_magic;
if (!std.mem.eql(u8, try self.read(4), &.{ 0x01, 0x00, 0x00, 0x00 })) return Error.invalid_version;
// TODO: Ensure only one section of each type (except for custom section), some code depends on it
@@ -253,15 +292,6 @@ pub fn parseModule(self: *Parser) !vm.Module {
else => return Error.invalid_section,
};
}
-
- return .{
- .memory = .{
- .min = self.memory.?.lim.min,
- .max = self.memory.?.lim.max,
- },
- .exports = self.exports,
- .functions = self.functions.?,
- };
}
fn parseCustomsec(self: *Parser) !void {
@@ -275,6 +305,8 @@ fn parseTypesec(self: *Parser) !void {
const end_idx = self.byte_idx + size;
const ft = try self.parseVector(Parser.parseFunctype);
+
+ if (self.types.len != 0) return Error.duplicated_typesec;
self.types = ft;
// TODO(ernesto): run this check not only on debug
@@ -310,7 +342,7 @@ fn parseImportsec(self: *Parser) !void {
// TODO(ernesto): this should be used to do name resolution.
const imports = try self.parseVector(Parser.parseImport);
- _ = imports;
+ defer self.allocator.free(imports);
// TODO: run this check not only on debug
std.debug.assert(self.byte_idx == end_idx);
@@ -321,16 +353,22 @@ fn parseFuncsec(self: *Parser) !void {
const end_idx = self.byte_idx + size;
const types = try self.parseVector(Parser.readU32);
+ defer self.allocator.free(types);
- if (self.functions != null) return Error.duplicated_funcsec;
+ if (self.functions.len != 0) return Error.duplicated_funcsec;
self.functions = try self.allocator.alloc(vm.Function, types.len);
for (types, 0..) |t, i| {
- self.functions.?[i].func_type = self.types.?[t];
+ 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),
+ };
+ @memcpy(self.functions[i].func_type.parameters, self.types[t].parameters);
+ @memcpy(self.functions[i].func_type.returns, self.types[t].returns);
}
// TODO(ernesto): run this check not only in debug
- std.debug.assert(types.len == self.functions.?.len);
+ std.debug.assert(types.len == self.functions.len);
// TODO: run this check not only on debug
std.debug.assert(self.byte_idx == end_idx);
@@ -347,6 +385,7 @@ fn parseMemsec(self: *Parser) !void {
const end_idx = self.byte_idx + size;
const mems = try self.parseVector(Parser.parseMemtype);
+ defer self.allocator.free(mems);
if (mems.len == 0) {
// WTF?
} else if (mems.len == 1) {
@@ -391,9 +430,19 @@ fn parseExportsec(self: *Parser) !void {
const end_idx = self.byte_idx + size;
const exports = try self.parseVector(Parser.parseExport);
+ defer {
+ for (exports) |e| self.allocator.free(e.name);
+ self.allocator.free(exports);
+ }
for (exports) |e| {
switch (e.exportdesc) {
- .func => try self.exports.put(self.allocator, e.name, e.exportdesc.func),
+ .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});
+ }
+ },
else => std.debug.print("[WARN]: export ignored\n", .{}),
}
}
@@ -434,6 +483,7 @@ fn parseCode(self: *Parser) !Func {
const end_idx = self.byte_idx + size;
const locals = try self.parseVector(Parser.parseLocal);
+ defer self.allocator.free(locals);
var local_count: usize = 0;
for (locals) |l| {
local_count += l.n;
@@ -465,10 +515,11 @@ fn parseCodesec(self: *Parser) !void {
const end_idx = self.byte_idx + size;
const codes = try self.parseVector(Parser.parseCode);
+ defer self.allocator.free(codes);
// TODO: run this check not only on debug
- std.debug.assert(codes.len == self.functions.?.len);
+ std.debug.assert(codes.len == self.functions.len);
- for (codes, self.functions.?) |code, *f| {
+ for (codes, self.functions) |code, *f| {
f.typ = .{ .internal = .{
.locals = code.locals,
.ir = code.ir,
diff --git a/src/mods/vm.zig b/src/mods/vm.zig
index a636cb3..03e370c 100644
--- a/src/mods/vm.zig
+++ b/src/mods/vm.zig
@@ -24,22 +24,37 @@ pub const Functype = struct {
allocator.free(self.returns);
}
};
-pub const Function = struct { func_type: Functype, typ: union(enum) {
- internal: struct {
- locals: []Valtype,
- ir: IR,
- },
- external: void,
-} };
+pub const Function = struct {
+ func_type: Functype,
+ typ: union(enum) {
+ internal: struct {
+ locals: []Valtype,
+ ir: IR,
+ },
+ external: void,
+ }
+};
+
+pub const ExportFunction = enum {
+ preinit,
+};
+pub const Exports = struct {
+ preinit: ?u32 = null,
+};
+comptime {
+ std.debug.assert(@typeInfo(ExportFunction).@"enum".fields.len == @typeInfo(Exports).@"struct".fields.len );
+}
+
pub const Module = struct {
memory: Memory,
functions: []Function,
- exports: std.StringHashMapUnmanaged(u32),
+ exports: Exports,
- fn deinit(self: *Module, allocator: Allocator) void {
- self.exports.deinit(allocator);
+ pub fn deinit(self: Module, allocator: Allocator) void {
+ // self.exports.deinit(allocator);
for (self.functions) |f| {
+ std.debug.print("Freeing function parameters at {*}\n", .{f.func_type.parameters.ptr});
allocator.free(f.func_type.parameters);
allocator.free(f.func_type.returns);
switch (f.typ) {
@@ -877,12 +892,16 @@ pub const Runtime = struct {
}
// TODO: Do name resolution at parseTime
- pub fn callExternal(self: *Runtime, allocator: Allocator, name: []const u8, parameters: []Value) !void {
- if (self.module.exports.get(name)) |function| {
- try self.call(allocator, function, parameters);
- } else {
- std.debug.panic("Function `{s}` not avaliable", .{name});
- }
+ pub fn callExternal(self: *Runtime, allocator: Allocator, name: ExportFunction, parameters: []Value) !void {
+ switch (name) {
+ .preinit => {
+ if (self.module.exports.preinit) |func| {
+ try self.call(allocator, func, parameters);
+ } else {
+ std.debug.panic("Function preinit unavailable\n", .{});
+ }
+ },
+ }
}
pub fn call(self: *Runtime, allocator: Allocator, function: usize, parameters: []Value) AllocationError!void {