From 8e6657096b1dc813ea721ecea882ed14252a5e20 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Thu, 16 May 2024 09:15:51 +0200 Subject: [PATCH] fix(wasm): Fixed wasm build closes #142 --- .github/workflows/ci.yaml | 15 +++++++ build.zig | 4 -- src/Codegen.zig | 25 +++++------ src/Jit.zig | 23 ++++++----- src/Parser.zig | 37 +++++++++-------- src/Reporter.zig | 3 +- src/buzz_api.zig | 85 +++++++++++++++++++------------------- src/disassembler.zig | 3 +- src/io.zig | 65 +++++++++++++++++++++++++++++ src/jit_extern_api.zig | 3 +- src/lib/buzz_debug.zig | 3 +- src/lib/buzz_io.zig | 1 + src/lib/buzz_std.zig | 9 ++-- src/lib/io.zig | 1 + src/main.zig | 87 +++++++++++++++++---------------------- src/memory.zig | 77 +++++++++++++++++----------------- src/obj.zig | 9 ++-- src/repl.zig | 9 ++-- src/vm.zig | 19 +++++---- src/wasm.zig | 39 ++++++++++++++++++ src/wasm_repl.zig | 11 ++--- 21 files changed, 326 insertions(+), 202 deletions(-) create mode 100644 src/io.zig create mode 120000 src/lib/io.zig diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a1364df7..a915b0f7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -93,6 +93,21 @@ jobs: run: zig build -Doptimize=ReleaseFast -Djit_always_on test - name: Cleanup run: rm -rf zig-out zig-cache + wasm-build: + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v3.0.0 + - name: Checkout submodules + run: git submodule update --init --recursive + - name: Setup nightly Zig + uses: goto-bus-stop/setup-zig@v1 + with: + version: master + - name: Build for wasm + run: zig build -Dtarget=wasm32-freestanding -freference-trace -Doptimize=ReleaseSmall + - name: Cleanup + run: rm -rf zig-out zig-cache lint: runs-on: macos-latest steps: diff --git a/build.zig b/build.zig index 972204bd..44368ad2 100644 --- a/build.zig +++ b/build.zig @@ -510,10 +510,6 @@ pub fn build(b: *Build) !void { run_tests.setEnvironmentVariable("BUZZ_PATH", try getBuzzPrefix(b)); run_tests.step.dependOn(install_step); // wait for libraries to be installed test_step.dependOn(&run_tests.step); - - if (is_wasm) { - buildWasmReplDemo(b, exe); - } } pub fn buildPcre2(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) !*Build.Step.Compile { diff --git a/src/Codegen.zig b/src/Codegen.zig index a0764639..ccd3dac5 100644 --- a/src/Codegen.zig +++ b/src/Codegen.zig @@ -15,6 +15,7 @@ const Reporter = @import("Reporter.zig"); const BuildOptions = @import("build_options"); const JIT = if (!is_wasm) @import("Jit.zig") else void; const disassembler = @import("disassembler.zig"); +const io = @import("io.zig"); const Self = @This(); @@ -162,7 +163,7 @@ pub fn generate(self: *Self, ast: Ast) anyerror!?*obj.ObjFunction { // try root.node.toJson(&root.node, &out.writer()); - // try std.io.getStdOut().writer().print("\n{s}", .{out.items}); + // try io.stdOutWriter.print("\n{s}", .{out.items}); // } const function = self.generateNode(self.ast.root.?, null); @@ -1770,8 +1771,8 @@ fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj. } const loop_start = self.currentCode(); - const jit_jump = try self.emitJump(locations[node], .OP_HOTSPOT); - try self.emit(locations[node], node); + const jit_jump = if (!is_wasm) try self.emitJump(locations[node], .OP_HOTSPOT) else {}; + if (!is_wasm) try self.emit(locations[node], node); const condition_type_def = type_defs[components.condition].?; if (condition_type_def.def_type == .Placeholder) { @@ -1831,7 +1832,7 @@ fn generateFor(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*obj. try self.patchOptJumps(node); try self.endScope(node); - self.patchTryOrJit(jit_jump); + if (!is_wasm) self.patchTryOrJit(jit_jump); return null; } @@ -2038,8 +2039,8 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?* _ = try self.generateNode(components.iterable, breaks); const loop_start: usize = self.currentCode(); - const jit_jump = try self.emitJump(locations[node], .OP_HOTSPOT); - try self.emit(locations[node], node); + const jit_jump = if (!is_wasm) try self.emitJump(locations[node], .OP_HOTSPOT) else {}; + if (!is_wasm) try self.emit(locations[node], node); // Calls `next` and update key and value locals try self.emitOpCode( @@ -2105,7 +2106,7 @@ fn generateForEach(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?* ); try self.endScope(node); - self.patchTryOrJit(jit_jump); + if (!is_wasm) self.patchTryOrJit(jit_jump); return null; } @@ -2285,7 +2286,7 @@ fn generateFunction(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!? if (BuildOptions.debug) { disassembler.disassembleChunk(¤t_function.chunk, current_function.name.string); - std.debug.print("\n\n", .{}); + io.print("\n\n", .{}); } self.current = frame.enclosing; @@ -3059,7 +3060,7 @@ fn generateObjectInit(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error self.reporter.reportPlaceholder(self.ast, value_type_def.resolved_type.?.Placeholder); } else if (!prop.eql(value_type_def)) { if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "prop {}({}), value {}({})\n", .{ @intFromPtr(prop.resolved_type.?.ObjectInstance), @@ -3852,8 +3853,8 @@ fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*ob const loop_start: usize = self.currentCode(); - const jit_jump = try self.emitJump(locations[node], .OP_HOTSPOT); - try self.emit(locations[node], node); + const jit_jump = if (!is_wasm) try self.emitJump(locations[node], .OP_HOTSPOT) else {}; + if (!is_wasm) try self.emit(locations[node], node); if (condition_type_def.def_type == .Placeholder) { self.reporter.reportPlaceholder(self.ast, condition_type_def.resolved_type.?.Placeholder); @@ -3893,7 +3894,7 @@ fn generateWhile(self: *Self, node: Ast.Node.Index, breaks: ?*Breaks) Error!?*ob try self.patchOptJumps(node); try self.endScope(node); - self.patchTryOrJit(jit_jump); + if (!is_wasm) self.patchTryOrJit(jit_jump); return null; } diff --git a/src/Jit.zig b/src/Jit.zig index 42ea3d2b..968384ee 100644 --- a/src/Jit.zig +++ b/src/Jit.zig @@ -10,6 +10,7 @@ const VM = r.VM; const ZigType = @import("zigtypes.zig").Type; const ExternApi = @import("jit_extern_api.zig").ExternApi; const api = @import("lib/buzz_api.zig"); +const io = @import("io.zig"); pub const Error = error{ CantCompile, @@ -157,7 +158,7 @@ pub fn compileFunction(self: *Self, ast: Ast, closure: *o.ObjClosure) Error!void &seen, )) { if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Not compiling node {s}#{}, likely because it uses a fiber\n", .{ @tagName(ast.nodes.items(.tag)[ast_node]), @@ -237,7 +238,7 @@ pub fn compileHotSpot(self: *Self, ast: Ast, hotspot_node: Ast.Node.Index) Error &seen, )) { if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Not compiling node {s}#{}, likely because it uses a fiber\n", .{ @tagName(ast.nodes.items(.tag)[hotspot_node]), @@ -370,7 +371,7 @@ fn buildFunction(self: *Self, ast: Ast, closure: ?*o.ObjClosure, ast_node: Ast.N try self.compiled_closures.put(uclosure, {}); if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Compiling function `{s}` because it was called {}/{} times\n", .{ qualified_name.items, @@ -383,7 +384,7 @@ fn buildFunction(self: *Self, ast: Ast, closure: ?*o.ObjClosure, ast_node: Ast.N try self.compiled_hotspots.put(ast_node, {}); } else { if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Compiling closure `{s}`\n", .{ qualified_name.items, @@ -398,7 +399,7 @@ fn buildFunction(self: *Self, ast: Ast, closure: ?*o.ObjClosure, ast_node: Ast.N self.generateNode(ast_node)) catch |err| { if (err == Error.CantCompile) { if (BuildOptions.jit_debug) { - std.debug.print("Not compiling `{s}`, likely because it uses a fiber\n", .{qualified_name.items}); + io.print("Not compiling `{s}`, likely because it uses a fiber\n", .{qualified_name.items}); } m.MIR_finish_func(self.ctx); @@ -509,7 +510,7 @@ fn generateNode(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t { => return Error.CantCompile, else => { - std.debug.print("{} NYI\n", .{tag}); + io.print("{} NYI\n", .{tag}); unreachable; }, }; @@ -4434,7 +4435,7 @@ fn generateHotspotFunction(self: *Self, node: Ast.Node.Index) Error!?m.MIR_op_t _ = self.generateNode(node) catch |err| { if (err == error.CantCompile) { if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Not compiling node {s}#{}, likely because it uses a fiber\n", .{ @tagName(self.state.?.ast.nodes.items(.tag)[self.state.?.ast_node]), @@ -4692,7 +4693,7 @@ fn getQualifiedName(self: *Self, node: Ast.Node.Index, raw: bool) !std.ArrayList else => { if (BuildOptions.debug) { - std.debug.print( + io.print( "Ast {s} node are not valid hotspots", .{ @tagName(tag), @@ -4715,7 +4716,7 @@ pub fn compileZdefContainer(self: *Self, ast: Ast, zdef_element: Ast.Zdef.ZdefEl defer m.MIR_finish_module(self.ctx); if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Compiling zdef struct getters/setters for `{s}` of type `{s}`\n", .{ zdef_element.zdef.name, @@ -5004,7 +5005,7 @@ pub fn compileZdef(self: *Self, buzz_ast: Ast, zdef: Ast.Zdef.ZdefElement) Error defer m.MIR_finish_module(self.ctx); if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Compiling zdef wrapper for `{s}` of type `{s}`\n", .{ zdef.zdef.name, @@ -5113,7 +5114,7 @@ fn zigToMIRRegType(zig_type: ZigType) m.MIR_type_t { // Optional are only allowed on pointers .Optional => m.MIR_T_I64, else => { - std.debug.print("{}\n", .{zig_type}); + io.print("{}\n", .{zig_type}); unreachable; }, }; diff --git a/src/Parser.zig b/src/Parser.zig index 8fef7e92..60a4d8d5 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -15,6 +15,7 @@ const Reporter = @import("Reporter.zig"); const StringParser = @import("string_parser.zig").StringParser; const pcre = if (!is_wasm) @import("pcre.zig") else void; const buzz_api = @import("lib/buzz_api.zig"); +const io = @import("io.zig"); // In the wasm build, libraries are statically linked const std_lib = if (is_wasm) @import("lib/buzz_std.zig") else void; @@ -109,7 +110,7 @@ const debug_api = if (is_wasm) std.StaticStringMap(buzz_api.NativeFn).initCompti const serialize_lib = if (is_wasm) @import("lib/buzz_serialize.zig") else void; const serialize_api = if (is_wasm) std.StaticStringMap(buzz_api.NativeFn).initComptime( .{ - .{ "serialize", &serialize_lib.serialize }, + .{ "serialize", &serialize_lib.serializeValue }, }, ) else void; @@ -477,7 +478,7 @@ const rules = [_]ParseRule{ .{ .prefix = null, .infix = null, .precedence = .None }, // var .{ .prefix = null, .infix = null, .precedence = .None }, // out .{ .prefix = null, .infix = null, .precedence = .None }, // namespace - .{ .prefix = null, .infix = null, .precedence = .None }, // range + .{ .prefix = null, .infix = null, .precedence = .None }, // rg }; ast: Ast, @@ -846,7 +847,7 @@ pub fn parse(self: *Self, source: []const u8, file_name: []const u8) !?Ast { // When running in REPL, global scope is allowed to run statements since the global scope becomes procedural if (self.declarationOrStatement(null) catch |err| { if (BuildOptions.debug) { - std.debug.print("Parsing failed with error {}\n", .{err}); + io.print("Parsing failed with error {}\n", .{err}); } return null; }) |decl| { @@ -867,7 +868,7 @@ pub fn parse(self: *Self, source: []const u8, file_name: []const u8) !?Ast { } if (BuildOptions.debug) { - std.debug.print("Parsing failed with error {}\n", .{err}); + io.print("Parsing failed with error {}\n", .{err}); } return null; }) |decl| { @@ -1691,7 +1692,7 @@ fn resolvePlaceholderWithRelation( const child_placeholder = child.resolved_type.?.Placeholder; if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "Attempts to resolve @{} child placeholder @{} ({s}) with relation {}\n", .{ @intFromPtr(resolved_type), @@ -2013,7 +2014,7 @@ pub fn resolvePlaceholder(self: *Self, placeholder: *obj.ObjTypeDef, resolved_ty std.debug.assert(placeholder.def_type == .Placeholder); if (BuildOptions.debug_placeholders) { - std.debug.print("Attempts to resolve @{} ({s}) with @{} a {}({})\n", .{ + io.print("Attempts to resolve @{} ({s}) with @{} a {}({})\n", .{ @intFromPtr(placeholder), if (placeholder.resolved_type.?.Placeholder.name) |name| name.string else "unknown", @intFromPtr(resolved_type), @@ -2025,7 +2026,7 @@ pub fn resolvePlaceholder(self: *Self, placeholder: *obj.ObjTypeDef, resolved_ty // Both placeholders, we have to connect the child placeholder to a root placeholder so its not orphan if (resolved_type.def_type == .Placeholder) { if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "Replaced linked placeholder @{} ({s}) with rooted placeholder @{} ({s})\n", .{ @intFromPtr(placeholder), @@ -2064,7 +2065,7 @@ pub fn resolvePlaceholder(self: *Self, placeholder: *obj.ObjTypeDef, resolved_ty const placeholder_def = placeholder.resolved_type.?.Placeholder; if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "Resolved placeholder @{} {s}({}) with @{}.{}({})\n", .{ @intFromPtr(placeholder), @@ -2185,7 +2186,7 @@ fn declareVariable(self: *Self, variable_type: *obj.ObjTypeDef, name_token: ?Ast // The placeholder resolution occurs after we parsed the functions body in `funDeclaration` if (variable_type.resolved_type != null or @intFromEnum(variable_type.def_type) < @intFromEnum(obj.ObjTypeDef.Type.ObjectInstance)) { if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "Global placeholder @{} resolve with @{} {s} (opt {})\n", .{ @intFromPtr(global.type_def), @@ -2271,7 +2272,7 @@ fn declarePlaceholder(self: *Self, name: Ast.TokenIndex, placeholder: ?*obj.ObjT self.globals.items[global].initialized = true; if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "global placeholder @{} for `{s}` at {}\n", .{ @intFromPtr(placeholder_type), @@ -4127,7 +4128,7 @@ fn dot(self: *Self, can_assign: bool, callee: Ast.Node.Index) Error!Ast.Node.Ind ); if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "static placeholder @{} for `{s}`\n", .{ @intFromPtr(placeholder), @@ -4235,7 +4236,7 @@ fn dot(self: *Self, can_assign: bool, callee: Ast.Node.Index) Error!Ast.Node.Ind ); if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "property placeholder @{} for `{s}.{s}`\n", .{ @intFromPtr(placeholder), @@ -5443,7 +5444,7 @@ fn asyncCall(self: *Self, _: bool) Error!Ast.Node.Index { ); } else { if (function_type.def_type != .Function) { - std.debug.print( + io.print( "function_type.def_type {}\ncall_components.Call.callee {}\ncallable.tag {}\ncall_node tag {}\n", .{ function_type.def_type, @@ -6197,7 +6198,7 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index { // Now we know the placeholder was a method if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "resolved static method for `{s}`\n", .{ method_name, @@ -6212,7 +6213,7 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index { // Now we know the placeholder was a method if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "resolved method placeholder for `{s}`\n", .{ method_name, @@ -6271,7 +6272,7 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index { // Now we know the placeholder was a field if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "resolved static property placeholder for `{s}`\n", .{ property_name.lexeme, @@ -6286,7 +6287,7 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index { // Now we know the placeholder was a field if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "resolved property placeholder for `{s}`\n", .{ property_name.lexeme, @@ -7184,7 +7185,7 @@ fn readStaticScript(self: *Self, file_name: []const u8) ?[2][]const u8 { } else if (std.mem.eql(u8, file_name, "test")) [_][]const u8{ - @embedFile("lib/test.buzz"), + @embedFile("lib/testing.buzz"), file_name, } else if (std.mem.eql(u8, file_name, "crypto")) diff --git a/src/Reporter.zig b/src/Reporter.zig index 512d75a5..0aedeb46 100644 --- a/src/Reporter.zig +++ b/src/Reporter.zig @@ -8,6 +8,7 @@ const Scanner = @import("Scanner.zig"); const Ast = @import("Ast.zig"); const builtin = @import("builtin"); const is_wasm = builtin.cpu.arch.isWasm(); +const io = @import("io.zig"); const Self = @This(); @@ -181,7 +182,7 @@ pub const Report = struct { options: ReportOptions = .{}, pub inline fn reportStderr(self: *Report, reporter: *Self) !void { - return self.report(reporter, std.io.getStdErr().writer()); + return self.report(reporter, io.stdErrWriter); } pub fn report(self: *Report, reporter: *Self, out: anytype) !void { diff --git a/src/buzz_api.zig b/src/buzz_api.zig index 33323205..d1035217 100644 --- a/src/buzz_api.zig +++ b/src/buzz_api.zig @@ -19,6 +19,7 @@ const dumpStack = @import("disassembler.zig").dumpStack; const ZigType = @import("zigtypes.zig").Type; const Token = @import("Token.zig"); const Ast = @import("Ast.zig"); +const io = @import("io.zig"); pub const os = if (is_wasm) @import("wasm.zig") @@ -143,17 +144,17 @@ export fn bz_valueToCString(value: Value) ?[*:0]const u8 { fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), depth: usize) void { if (depth > 50) { - std.debug.print("...", .{}); + io.print("...", .{}); return; } if (value.isNull()) { - std.debug.print("null", .{}); + io.print("null", .{}); } else if (!value.isObj() or seen.get(value.obj()) != null) { const string = value.toStringAlloc(vm.gc.allocator) catch std.ArrayList(u8).init(vm.gc.allocator); defer string.deinit(); - std.debug.print("{s}", .{string.items}); + io.print("{s}", .{string.items}); } else { seen.put(value.obj(), {}) catch unreachable; @@ -170,7 +171,7 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep const string = value.toStringAlloc(vm.gc.allocator) catch std.ArrayList(u8).init(vm.gc.allocator); defer string.deinit(); - std.debug.print("{s}", .{string.items}); + io.print("{s}", .{string.items}); }, .UpValue => { @@ -182,94 +183,94 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep .String => { const string = ObjString.cast(value.obj()).?; - std.debug.print("\"{s}\"", .{string.string}); + io.print("\"{s}\"", .{string.string}); }, .Pattern => { const pattern = ObjPattern.cast(value.obj()).?; - std.debug.print("$\"{s}\"", .{pattern.source}); + io.print("$\"{s}\"", .{pattern.source}); }, .List => { const list = ObjList.cast(value.obj()).?; - std.debug.print("[ ", .{}); + io.print("[ ", .{}); for (list.items.items) |item| { valueDump(item, vm, seen, depth + 1); - std.debug.print(", ", .{}); + io.print(", ", .{}); } - std.debug.print("]", .{}); + io.print("]", .{}); }, .Range => { const range = ObjRange.cast(value.obj()).?; - std.debug.print("{}..{}", .{ range.low, range.high }); + io.print("{}..{}", .{ range.low, range.high }); }, .Map => { const map = ObjMap.cast(value.obj()).?; - std.debug.print("{{ ", .{}); + io.print("{{ ", .{}); var it = map.map.iterator(); while (it.next()) |kv| { const key = kv.key_ptr.*; valueDump(key, vm, seen, depth + 1); - std.debug.print(": ", .{}); + io.print(": ", .{}); valueDump(kv.value_ptr.*, vm, seen, depth + 1); - std.debug.print(", ", .{}); + io.print(", ", .{}); } - std.debug.print("}}", .{}); + io.print("}}", .{}); }, .Enum => { const enumeration = ObjEnum.cast(value.obj()).?; const enum_type_def = enumeration.type_def.resolved_type.?.Enum; - std.debug.print("enum({s}) {s} {{ ", .{ enum_type_def.name.string, enumeration.name.string }); + io.print("enum({s}) {s} {{ ", .{ enum_type_def.name.string, enumeration.name.string }); for (enum_type_def.cases.items, 0..) |case, i| { - std.debug.print("{s} -> ", .{case}); + io.print("{s} -> ", .{case}); valueDump(enumeration.cases[i], vm, seen, depth); - std.debug.print(", ", .{}); + io.print(", ", .{}); } - std.debug.print("}}", .{}); + io.print("}}", .{}); }, .Object => { const object = ObjObject.cast(value.obj()).?; const object_def = object.type_def.resolved_type.?.Object; - std.debug.print("object", .{}); + io.print("object", .{}); if (object_def.conforms_to.count() > 0) { - std.debug.print("(", .{}); + io.print("(", .{}); var it = object_def.conforms_to.iterator(); while (it.next()) |kv| { - std.debug.print("{s}, ", .{kv.key_ptr.*.resolved_type.?.Protocol.name.string}); + io.print("{s}, ", .{kv.key_ptr.*.resolved_type.?.Protocol.name.string}); } - std.debug.print(")", .{}); + io.print(")", .{}); } - std.debug.print(" {s} {{ ", .{object_def.name.string}); + io.print(" {s} {{ ", .{object_def.name.string}); var it = object_def.static_fields.iterator(); while (it.next()) |kv| { const static_field_type_str = kv.value_ptr.*.toStringAlloc(vm.gc.allocator) catch std.ArrayList(u8).init(vm.gc.allocator); defer static_field_type_str.deinit(); - std.debug.print("static {s} {s}", .{ static_field_type_str.items, kv.key_ptr.* }); + io.print("static {s} {s}", .{ static_field_type_str.items, kv.key_ptr.* }); var static_it = object.static_fields.iterator(); while (static_it.next()) |static_kv| { if (std.mem.eql(u8, static_kv.key_ptr.*.string, kv.key_ptr.*)) { - std.debug.print(" = ", .{}); + io.print(" = ", .{}); valueDump(static_kv.value_ptr.*, vm, seen, depth + 1); break; } } - std.debug.print(", ", .{}); + io.print(", ", .{}); } it = object_def.fields.iterator(); @@ -277,14 +278,14 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep const field_type_str = kv.value_ptr.*.toStringAlloc(vm.gc.allocator) catch std.ArrayList(u8).init(vm.gc.allocator); defer field_type_str.deinit(); - std.debug.print("{s} {s}", .{ field_type_str.items, kv.key_ptr.* }); + io.print("{s} {s}", .{ field_type_str.items, kv.key_ptr.* }); if (object.fields.get(vm.gc.copyString(kv.key_ptr.*) catch unreachable)) |v| { - std.debug.print(" = ", .{}); + io.print(" = ", .{}); valueDump(v, vm, seen, depth + 1); } - std.debug.print(", ", .{}); + io.print(", ", .{}); } it = object_def.methods.iterator(); @@ -292,16 +293,16 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep const method_type_str = kv.value_ptr.*.toStringAlloc(vm.gc.allocator) catch std.ArrayList(u8).init(vm.gc.allocator); defer method_type_str.deinit(); - std.debug.print("{s}, ", .{method_type_str.items}); + io.print("{s}, ", .{method_type_str.items}); } - std.debug.print("}}", .{}); + io.print("}}", .{}); }, .ObjectInstance => { const object_instance = ObjObjectInstance.cast(value.obj()).?; - std.debug.print( + io.print( "{s}{{ ", .{ if (object_instance.object) |object| @@ -312,25 +313,25 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep ); var it = object_instance.fields.iterator(); while (it.next()) |kv| { - std.debug.print("{s} = ", .{kv.key_ptr.*.string}); + io.print("{s} = ", .{kv.key_ptr.*.string}); valueDump(kv.value_ptr.*, vm, seen, depth + 1); - std.debug.print(", ", .{}); + io.print(", ", .{}); } - std.debug.print("}}", .{}); + io.print("}}", .{}); }, .ForeignContainer => { const foreign = ObjForeignContainer.cast(value.obj()).?; const foreign_def = foreign.type_def.resolved_type.?.ForeignContainer; - std.debug.print( + io.print( "{s}{{ ", .{foreign_def.name.string}, ); var it = foreign_def.fields.iterator(); while (it.next()) |kv| { - std.debug.print("{s} = ", .{kv.key_ptr.*}); + io.print("{s} = ", .{kv.key_ptr.*}); valueDump( kv.value_ptr.*.getter( vm, @@ -340,9 +341,9 @@ fn valueDump(value: Value, vm: *VM, seen: *std.AutoHashMap(*_obj.Obj, void), dep seen, depth + 1, ); - std.debug.print(", ", .{}); + io.print(", ", .{}); } - std.debug.print("}}", .{}); + io.print("}}", .{}); }, } @@ -1326,8 +1327,8 @@ export fn bz_closure( } export fn bz_dumpStack(ctx: *NativeCtx, off: usize) void { - std.debug.print("base is {}, top is {}\n", .{ @intFromPtr(ctx.base), @intFromPtr(ctx.vm.current_fiber.stack_top) }); - std.debug.print("#{}:\n", .{off}); + io.print("base is {}, top is {}\n", .{ @intFromPtr(ctx.base), @intFromPtr(ctx.vm.current_fiber.stack_top) }); + io.print("#{}:\n", .{off}); dumpStack(ctx.vm); } @@ -1510,7 +1511,7 @@ export fn bz_clone(vm: *VM, value: Value) Value { } export fn dumpInt(value: u64) void { - std.debug.print("-> {x}\n", .{value}); + io.print("-> {x}\n", .{value}); } export fn bz_zigType(vm: *VM, ztype: [*]const u8, len: usize, expected_type: *Value) ?*const ZigType { diff --git a/src/disassembler.zig b/src/disassembler.zig index c10dce64..c3110c36 100644 --- a/src/disassembler.zig +++ b/src/disassembler.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const print = std.debug.print; +const io = @import("io.zig"); +const print = io.print; const Chunk = @import("Chunk.zig"); const Value = @import("value.zig").Value; const obj = @import("obj.zig"); diff --git a/src/io.zig b/src/io.zig new file mode 100644 index 00000000..82770434 --- /dev/null +++ b/src/io.zig @@ -0,0 +1,65 @@ +// Because of https://ziglang.org/download/0.12.0/release-notes.html#Bring-Your-Own-OS-API-Layer-Regressed +// we have to add this abstraction layer to avoid using io.getStdIn/Err/Out + +const std = @import("std"); +const builtin = @import("builtin"); +const is_wasm = builtin.cpu.arch.isWasm(); +const wasm = @import("wasm.zig"); + +fn stdErrWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { + return std.io.getStdErr().write(bytes); +} + +fn stdOutWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { + return std.io.getStdOut().write(bytes); +} + +fn stdInRead(_: void, buffer: []u8) std.posix.ReadError!usize { + return std.io.getStdIn().read(buffer); +} + +const StdErrWriter = std.io.Writer( + void, + std.posix.WriteError, + if (is_wasm) + wasm.stdErrWrite + else + stdErrWrite, +); + +pub const stdErrWriter = StdErrWriter{ .context = {} }; + +const StdOutWriter = std.io.Writer( + void, + std.posix.WriteError, + if (is_wasm) + wasm.stdOutWrite + else + stdOutWrite, +); + +pub const stdOutWriter = StdOutWriter{ .context = {} }; + +const StdInReader = std.io.Reader( + void, + std.posix.ReadError, + if (is_wasm) + wasm.stdInRead + else + stdInRead, +); + +pub const stdInReader = StdInReader{ .context = {} }; + +var stderr_mutex = std.Thread.Mutex{}; + +pub fn print(comptime fmt: []const u8, args: anytype) void { + if (is_wasm) { + stderr_mutex.lock(); + defer stderr_mutex.unlock(); + + stdErrWriter.print(fmt, args) catch return; + } else { + std.debug.print(fmt, args); + } +} diff --git a/src/jit_extern_api.zig b/src/jit_extern_api.zig index 9207e5f4..9b336259 100644 --- a/src/jit_extern_api.zig +++ b/src/jit_extern_api.zig @@ -4,6 +4,7 @@ const m = @import("mir.zig"); const api = @import("lib/buzz_api.zig"); const JIT = @import("Jit.zig"); const jmp = @import("jmp.zig").jmp; +const io = @import("io.zig"); export fn bz_exit(code: c_int) noreturn { std.process.exit(@truncate(@as(c_uint, @bitCast(code)))); @@ -1054,7 +1055,7 @@ pub const ExternApi = enum { .dumpInt => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.dumpInt))), .bz_valueDump => @as(*anyopaque, @ptrFromInt(@intFromPtr(&api.Value.bz_valueDump))), else => { - std.debug.print("{s}\n", .{self.name()}); + io.print("{s}\n", .{self.name()}); unreachable; }, }; diff --git a/src/lib/buzz_debug.zig b/src/lib/buzz_debug.zig index 119e3017..1f0619e8 100644 --- a/src/lib/buzz_debug.zig +++ b/src/lib/buzz_debug.zig @@ -1,5 +1,6 @@ const std = @import("std"); const api = @import("buzz_api.zig"); +const io = @import("io.zig"); const builtin = @import("builtin"); const is_wasm = builtin.cpu.arch.isWasm(); @@ -12,7 +13,7 @@ else pub export fn dump(ctx: *api.NativeCtx) c_int { ctx.vm.bz_peek(0).bz_valueDump(ctx.vm); - std.debug.print("\n", .{}); + io.print("\n", .{}); return 0; } diff --git a/src/lib/buzz_io.zig b/src/lib/buzz_io.zig index 4ee196cb..feea888a 100644 --- a/src/lib/buzz_io.zig +++ b/src/lib/buzz_io.zig @@ -1,5 +1,6 @@ const std = @import("std"); const api = @import("buzz_api.zig"); +const io = @import("io.zig"); pub export fn getStdIn(ctx: *api.NativeCtx) c_int { ctx.vm.bz_pushInteger(@intCast(std.io.getStdIn().handle)); diff --git a/src/lib/buzz_std.zig b/src/lib/buzz_std.zig index 9c6e66a6..cfd3d647 100644 --- a/src/lib/buzz_std.zig +++ b/src/lib/buzz_std.zig @@ -2,6 +2,7 @@ const std = @import("std"); const api = @import("buzz_api.zig"); const builtin = @import("builtin"); const is_wasm = builtin.cpu.arch.isWasm(); +const io = @import("io.zig"); pub const os = if (is_wasm) @import("wasm.zig") @@ -44,8 +45,8 @@ pub export fn print(ctx: *api.NativeCtx) c_int { return 0; } - _ = std.io.getStdOut().write(string.?[0..len]) catch return 0; - _ = std.io.getStdOut().write("\n") catch return 0; + _ = io.stdOutWriter.write(string.?[0..len]) catch return 0; + _ = io.stdOutWriter.write("\n") catch return 0; return 0; } @@ -210,14 +211,14 @@ pub export fn assert(ctx: *api.NativeCtx) c_int { if (message_value.isObj()) { var len: usize = 0; const message = api.Value.bz_valueToString(message_value, &len).?; - std.io.getStdOut().writer().print( + io.stdOutWriter.print( "Assert failed: {s}\n", .{ message[0..len], }, ) catch unreachable; } else { - std.io.getStdOut().writer().print("Assert failed\n", .{}) catch unreachable; + io.stdOutWriter.print("Assert failed\n", .{}) catch unreachable; } if (!is_wasm) { diff --git a/src/lib/io.zig b/src/lib/io.zig new file mode 120000 index 00000000..9b90932e --- /dev/null +++ b/src/lib/io.zig @@ -0,0 +1 @@ +../io.zig \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index b6525182..addd9b7a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,6 +19,7 @@ const JIT = if (!is_wasm) @import("Jit.zig") else void; const is_wasm = builtin.cpu.arch.isWasm(); const repl = if (!is_wasm) @import("repl.zig").repl else void; const wasm_repl = @import("wasm_repl.zig"); +const io = @import("io.zig"); pub export const initRepl_export = wasm_repl.initRepl; pub export const runLine_export = wasm_repl.runLine; @@ -28,7 +29,7 @@ pub const os = if (is_wasm) else std.os; -fn printBanner(out: std.fs.File.Writer, full: bool) void { +fn printBanner(out: anytype, full: bool) void { out.print( "\n👨‍🚀 buzz {}-{s} Copyright (C) 2021-present Benoit Giannangeli\n", .{ @@ -116,7 +117,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor: std.fs.openFileAbsolute(file_name, .{}) else std.fs.cwd().openFile(file_name, .{})) catch { - std.debug.print("File not found", .{}); + io.print("File not found", .{}); return; }; defer file.close(); @@ -158,7 +159,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor: } if (BuildOptions.show_perf and flavor != .Check and flavor != .Fmt) { - std.debug.print( + io.print( "\x1b[2mParsing: {[parsing]d}\nCodegen: {[codegen]d}\nRun: {[running]d}\nJIT: {[jit]d}\nGC: {[gc]d}\nTotal: {[total]}\nFull GC: {[gc_full]} | GC: {[gc_light]} | Max allocated: {[max_alloc]}\n\x1b[0m", .{ .parsing = std.fmt.fmtDuration(parsing_time), @@ -174,7 +175,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor: ); } } else { - std.debug.print("Formatting and Ast dump is deactivated", .{}); + io.print("Formatting and Ast dump is deactivated", .{}); // switch (flavor) { // .Run, .Test => unreachable, // .Fmt => { @@ -183,7 +184,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor: // // try function_node.render(function_node, &formatted.writer(), 0); // - // std.debug.print("{s}", .{formatted.items}); + // io.print("{s}", .{formatted.items}); // }, // .Ast => { // var json = std.ArrayList(u8).init(allocator); @@ -204,7 +205,7 @@ fn runFile(allocator: Allocator, file_name: []const u8, args: [][:0]u8, flavor: } } -pub fn main() !void { +pub fn main() u8 { var gpa = std.heap.GeneralPurposeAllocator(.{ .safety = builtin.mode == .Debug }){}; var allocator: std.mem.Allocator = if (builtin.mode == .Debug or is_wasm) gpa.allocator() @@ -236,32 +237,30 @@ pub fn main() !void { }, ) catch |err| { // Report useful error and exit - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + diag.report(io.stdErrWriter, err) catch {}; + return 1; }; defer res.deinit(); if (res.args.version == 1) { - printBanner(std.io.getStdOut().writer(), true); + printBanner(io.stdOutWriter, true); - if (!is_wasm) { - std.process.exit(0); - } + return 0; } if (res.args.help == 1) { - std.debug.print("👨‍🚀 buzz A small/lightweight typed scripting language\n\nUsage: buzz ", .{}); + io.print("👨‍🚀 buzz A small/lightweight typed scripting language\n\nUsage: buzz ", .{}); - try clap.usage( - std.io.getStdErr().writer(), + clap.usage( + io.stdErrWriter, clap.Help, ¶ms, - ); + ) catch return 1; - std.debug.print("\n\n", .{}); + io.print("\n\n", .{}); - try clap.help( - std.io.getStdErr().writer(), + clap.help( + io.stdErrWriter, clap.Help, ¶ms, .{ @@ -269,11 +268,9 @@ pub fn main() !void { .description_indent = 4, .spacing_between_parameters = 0, }, - ); + ) catch return 1; - if (!is_wasm) { - std.process.exit(0); - } + return 0; } if (res.args.library.len > 0) { @@ -281,7 +278,7 @@ pub fn main() !void { defer list.shrinkAndFree(list.items.len); for (res.args.library) |path| { - try list.append(path); + list.append(path) catch return 1; } Parser.user_library_paths = list.items; @@ -289,7 +286,9 @@ pub fn main() !void { var positionals = std.ArrayList([:0]u8).init(allocator); for (res.positionals) |pos| { - try positionals.append(try allocator.dupeZ(u8, pos)); + positionals.append( + allocator.dupeZ(u8, pos) catch return 1, + ) catch return 1; } defer { for (positionals.items) |pos| { @@ -313,11 +312,7 @@ pub fn main() !void { if (!is_wasm and flavor == .Repl) { repl(allocator) catch { - if (!is_wasm) { - std.process.exit(1); - } - - std.debug.print("REPL stopped", .{}); + return 1; }; } else if (!is_wasm and positionals.items.len > 0) { runFile( @@ -326,21 +321,15 @@ pub fn main() !void { positionals.items[1..], flavor, ) catch { - if (!is_wasm) { - std.process.exit(1); - } - - std.debug.print("VM stopped", .{}); + return 1; }; } else if (is_wasm) { - std.debug.print("NYI wasm repl", .{}); + io.print("NYI wasm repl", .{}); } else { - std.debug.print("Nothing to run", .{}); + io.print("Nothing to run", .{}); } - if (!is_wasm) { - std.process.exit(0); - } + return 0; } test "Testing behavior" { @@ -365,7 +354,7 @@ test "Testing behavior" { const file_name: []u8 = try allocator.alloc(u8, 6 + file.name.len); defer allocator.free(file_name); - std.debug.print("{s}\n", .{file.name}); + io.print("{s}\n", .{file.name}); var had_error: bool = false; runFile( @@ -374,13 +363,13 @@ test "Testing behavior" { &[_][:0]u8{}, .Test, ) catch { - std.debug.print("\u{001b}[31m[{s} ✕]\u{001b}[0m\n", .{file.name}); + io.print("\u{001b}[31m[{s} ✕]\u{001b}[0m\n", .{file.name}); had_error = true; fail_count += 1; }; if (!had_error) { - std.debug.print("\u{001b}[32m[{s} ✓]\u{001b}[0m\n", .{file.name}); + io.print("\u{001b}[32m[{s} ✓]\u{001b}[0m\n", .{file.name}); } } } @@ -428,7 +417,7 @@ test "Testing behavior" { if (!std.mem.containsAtLeast(u8, result.stderr, 1, first_line[2..])) { fail_count += 1; - std.debug.print( + io.print( "Expected error `{s}` got `{s}`\n", .{ first_line[2..], @@ -436,21 +425,21 @@ test "Testing behavior" { }, ); - std.debug.print("\u{001b}[31m[{s}... ✕]\u{001b}[0m\n", .{file.name}); + io.print("\u{001b}[31m[{s}... ✕]\u{001b}[0m\n", .{file.name}); } else { - std.debug.print("\u{001b}[32m[{s}... ✓]\u{001b}[0m\n", .{file.name}); + io.print("\u{001b}[32m[{s}... ✓]\u{001b}[0m\n", .{file.name}); } } } } if (fail_count == 0) { - std.debug.print("\n\u{001b}[32m", .{}); + io.print("\n\u{001b}[32m", .{}); } else { - std.debug.print("\n\u{001b}[31m", .{}); + io.print("\n\u{001b}[31m", .{}); } - std.debug.print("Ran {}, Failed: {}\u{001b}[0m\n", .{ + io.print("Ran {}, Failed: {}\u{001b}[0m\n", .{ count, fail_count, }); diff --git a/src/memory.zig b/src/memory.zig index 1a819220..26b45b0a 100644 --- a/src/memory.zig +++ b/src/memory.zig @@ -12,6 +12,7 @@ const Token = @import("Token.zig"); const buzz_api = @import("buzz_api.zig"); const Reporter = @import("Reporter.zig"); const is_wasm = builtin.cpu.arch.isWasm(); +const io = @import("io.zig"); const Value = _value.Value; const Obj = _obj.Obj; @@ -101,7 +102,7 @@ pub const TypeRegistry = struct { const type_def_ptr = try self.gc.allocateObject(ObjTypeDef, type_def); if (BuildOptions.debug_placeholders) { - std.debug.print("`{s}` @{}\n", .{ type_def_str, @intFromPtr(type_def_ptr) }); + io.print("`{s}` @{}\n", .{ type_def_str, @intFromPtr(type_def_ptr) }); } _ = try self.registry.put(type_def_str, type_def_ptr); @@ -261,7 +262,7 @@ pub const GarbageCollector = struct { const allocated = try self.allocator.create(T); if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "Allocated @{} {}\n", .{ std.fmt.fmtIntSizeDec(@intFromPtr(allocated)), @@ -326,8 +327,8 @@ pub const GarbageCollector = struct { }; // if (BuildOptions.gc_debug) { - // std.debug.print("allocated {*} {*}\n", .{ obj, object }); - // std.debug.print("(from {}) {} allocated, total {}\n", .{ before, self.bytes_allocated - before, self.bytes_allocated }); + // io.print("allocated {*} {*}\n", .{ obj, object }); + // io.print("(from {}) {} allocated, total {}\n", .{ before, self.bytes_allocated - before, self.bytes_allocated }); // } // Add new object at start of vm.objects linked list @@ -394,7 +395,7 @@ pub const GarbageCollector = struct { std.mem.copyForwards(u8, copy, chars); if (BuildOptions.gc_debug) { - std.debug.print("Allocated slice {*} `{s}`\n", .{ copy, copy }); + io.print("Allocated slice {*} `{s}`\n", .{ copy, copy }); } return try allocateString(self, copy); @@ -404,14 +405,14 @@ pub const GarbageCollector = struct { var timer = if (!is_wasm) std.time.Timer.start() catch unreachable else {}; if (BuildOptions.gc_debug) { - std.debug.print("Going to free {*}\n", .{pointer}); + io.print("Going to free {*}\n", .{pointer}); } self.bytes_allocated -= @sizeOf(T); self.allocator.destroy(pointer); if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "(from {}), collected {}, {} allocated\n", .{ std.fmt.fmtIntSizeDec(self.bytes_allocated + @sizeOf(T)), @@ -430,7 +431,7 @@ pub const GarbageCollector = struct { var timer = if (!is_wasm) std.time.Timer.start() catch unreachable else {}; if (BuildOptions.gc_debug) { - std.debug.print("Going to free slice {*} `{s}`\n", .{ pointer, pointer }); + io.print("Going to free slice {*} `{s}`\n", .{ pointer, pointer }); } const n: usize = (@sizeOf(T) * pointer.len); @@ -438,7 +439,7 @@ pub const GarbageCollector = struct { self.allocator.free(pointer); if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "(from {}), collected {}, {} allocated\n", .{ std.fmt.fmtIntSizeDec(self.bytes_allocated + n), @@ -457,7 +458,7 @@ pub const GarbageCollector = struct { if (!obj.is_dirty and self.obj_collected != obj) { obj.is_dirty = true; - // std.debug.print( + // io.print( // "Marked obj @{} {} dirty, gray_stack @{} or GC @{} will be {} items long\n", // .{ // @intFromPtr(obj), @@ -477,7 +478,7 @@ pub const GarbageCollector = struct { pub fn markObj(self: *Self, obj: *Obj) !void { if (obj.is_marked or self.obj_collected == obj) { if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "{*} {s} already marked or old\n", .{ obj, @@ -489,8 +490,8 @@ pub const GarbageCollector = struct { } if (BuildOptions.gc_debug) { - std.debug.print("marking {*}: ", .{obj}); - std.debug.print( + io.print("marking {*}: ", .{obj}); + io.print( "{s}\n", .{ (try Value.fromObj(obj).toStringAlloc(self.allocator)).items, @@ -515,7 +516,7 @@ pub const GarbageCollector = struct { fn blackenObject(self: *Self, obj: *Obj) !void { if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "blackening @{} {}\n", .{ @intFromPtr(obj), @@ -548,7 +549,7 @@ pub const GarbageCollector = struct { }; if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "done blackening @{} {}\n", .{ @intFromPtr(obj), @@ -560,7 +561,7 @@ pub const GarbageCollector = struct { fn freeObj(self: *Self, obj: *Obj) (std.mem.Allocator.Error || std.fmt.BufPrintError)!void { if (BuildOptions.gc_debug) { - std.debug.print(">> freeing {} {}\n", .{ @intFromPtr(obj), obj.obj_type }); + io.print(">> freeing {} {}\n", .{ @intFromPtr(obj), obj.obj_type }); } if (BuildOptions.gc_debug_access) { @@ -599,10 +600,10 @@ pub const GarbageCollector = struct { if (registered_obj == obj_typedef) { _ = self.type_registry.registry.remove(str.items); if (BuildOptions.gc_debug) { - std.debug.print("Removed registered type @{} `{s}`\n", .{ @intFromPtr(registered_obj), str.items }); + io.print("Removed registered type @{} `{s}`\n", .{ @intFromPtr(registered_obj), str.items }); } } else { - // std.debug.print( + // io.print( // "ObjTypeDef {*} `{s}` was allocated outside of type registry\n", // .{ // obj_typedef, @@ -736,19 +737,19 @@ pub const GarbageCollector = struct { try self.markObj(@constCast(ufiber.type_def.toObj())); // Mark main fiber if (BuildOptions.gc_debug) { - std.debug.print("MARKING STACK OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("MARKING STACK OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } var i: [*]Value = @ptrCast(fiber.stack); while (@intFromPtr(i) < @intFromPtr(fiber.stack_top)) : (i += 1) { try self.markValue(i[0]); } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING STACK OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("DONE MARKING STACK OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } // Mark closure if (BuildOptions.gc_debug) { - std.debug.print("MARKING FRAMES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("MARKING FRAMES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } for (fiber.frames.items) |frame| { try self.markObj(frame.closure.toObj()); @@ -760,12 +761,12 @@ pub const GarbageCollector = struct { } } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING FRAMES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("DONE MARKING FRAMES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } // Mark opened upvalues if (BuildOptions.gc_debug) { - std.debug.print("MARKING UPVALUES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("MARKING UPVALUES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } if (fiber.open_upvalues) |open_upvalues| { var upvalue: ?*ObjUpValue = open_upvalues; @@ -774,7 +775,7 @@ pub const GarbageCollector = struct { } } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING UPVALUES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); + io.print("DONE MARKING UPVALUES OF FIBER @{}\n", .{@intFromPtr(ufiber)}); } current_fiber = ufiber.parent_fiber; @@ -783,7 +784,7 @@ pub const GarbageCollector = struct { fn markMethods(self: *Self) !void { if (BuildOptions.gc_debug) { - std.debug.print("MARKING BASIC TYPES METHOD\n", .{}); + io.print("MARKING BASIC TYPES METHOD\n", .{}); } // Mark basic types methods { @@ -847,7 +848,7 @@ pub const GarbageCollector = struct { } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING BASIC TYPES METHOD\n", .{}); + io.print("DONE MARKING BASIC TYPES METHOD\n", .{}); } } @@ -876,13 +877,13 @@ pub const GarbageCollector = struct { // Mark globals if (BuildOptions.gc_debug) { - std.debug.print("MARKING GLOBALS OF VM @{}\n", .{@intFromPtr(vm)}); + io.print("MARKING GLOBALS OF VM @{}\n", .{@intFromPtr(vm)}); } for (vm.globals.items) |global| { try self.markValue(global); } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING GLOBALS OF VM @{}\n", .{@intFromPtr(vm)}); + io.print("DONE MARKING GLOBALS OF VM @{}\n", .{@intFromPtr(vm)}); } // Mark ast constant values (some are only referenced by the JIT so might be collected before) @@ -896,13 +897,13 @@ pub const GarbageCollector = struct { fn traceReference(self: *Self) !void { if (BuildOptions.gc_debug) { - std.debug.print("TRACING REFERENCE\n", .{}); + io.print("TRACING REFERENCE\n", .{}); } while (self.gray_stack.items.len > 0) { try blackenObject(self, self.gray_stack.pop()); } if (BuildOptions.gc_debug) { - std.debug.print("DONE TRACING REFERENCE\n", .{}); + io.print("DONE TRACING REFERENCE\n", .{}); } } @@ -915,7 +916,7 @@ pub const GarbageCollector = struct { while (obj_node) |node| : (count += 1) { if (node.data.is_marked) { if (BuildOptions.gc_debug and mode == .Full) { - std.debug.print("UNMARKING @{}\n", .{@intFromPtr(node.data)}); + io.print("UNMARKING @{}\n", .{@intFromPtr(node.data)}); } // If not a full gc, we reset is_marked, this object is now 'old' node.data.is_marked = if (mode == .Full) false else node.data.is_marked; @@ -940,10 +941,10 @@ pub const GarbageCollector = struct { if (BuildOptions.gc_debug or BuildOptions.gc_debug_light) { if (swept < self.bytes_allocated) { - std.debug.print("Warn: sweep gained memory, possibly due to an Object collector that takes up memory\n", .{}); + io.print("Warn: sweep gained memory, possibly due to an Object collector that takes up memory\n", .{}); } - std.debug.print( + io.print( "\nSwept {} objects for {}, now {}\n", .{ obj_count, @@ -972,7 +973,7 @@ pub const GarbageCollector = struct { const mode: Mode = if (self.bytes_allocated > self.next_full_gc and self.last_gc != null) .Full else .Young; if (BuildOptions.gc_debug or BuildOptions.gc_debug_light) { - std.debug.print( + io.print( "-- gc starts mode {s}, {}, {} objects\n", .{ @tagName(mode), @@ -990,7 +991,7 @@ pub const GarbageCollector = struct { var vm = kv.key_ptr.*; if (BuildOptions.gc_debug) { - std.debug.print( + io.print( "\tMarking VM @{}, on fiber @{} and closure @{} (function @{} {s})\n", .{ @intFromPtr(vm), @@ -1022,7 +1023,7 @@ pub const GarbageCollector = struct { self.last_gc = mode; if (BuildOptions.gc_debug or BuildOptions.gc_debug_light) { - std.debug.print( + io.print( "-- gc end, {}, {} objects, next_gc {}, next_full_gc {}\n", .{ std.fmt.fmtIntSizeDec(self.bytes_allocated), @@ -1032,7 +1033,7 @@ pub const GarbageCollector = struct { }, ); } - // std.debug.print("gc took {}ms\n", .{timer.read() / 1000000}); + // io.print("gc took {}ms\n", .{timer.read() / 1000000}); if (!is_wasm) { self.gc_time += timer.read(); @@ -1166,7 +1167,7 @@ pub const GarbageCollectorDebugger = struct { }, ); } else { - std.debug.print( + io.print( "Untracked obj {*}\n", .{ ptr, diff --git a/src/obj.zig b/src/obj.zig index 054daa6f..2bff9f4f 100644 --- a/src/obj.zig +++ b/src/obj.zig @@ -22,6 +22,7 @@ const buzz_api = @import("buzz_api.zig"); const buzz_builtin = @import("builtin.zig"); const ZigType = @import("zigtypes.zig").Type; const Ast = @import("Ast.zig"); +const io = @import("io.zig"); pub const pcre = if (!is_wasm) @import("pcre.zig") else void; @@ -1053,13 +1054,13 @@ pub const ObjFunction = struct { try gc.markObj(self.name.toObj()); try gc.markObj(@constCast(self.type_def.toObj())); if (BuildOptions.gc_debug) { - std.debug.print("MARKING CONSTANTS OF FUNCTION @{} {s}\n", .{ @intFromPtr(self), self.name.string }); + io.print("MARKING CONSTANTS OF FUNCTION @{} {s}\n", .{ @intFromPtr(self), self.name.string }); } for (self.chunk.constants.items) |constant| { try gc.markValue(constant); } if (BuildOptions.gc_debug) { - std.debug.print("DONE MARKING CONSTANTS OF FUNCTION @{} {s}\n", .{ @intFromPtr(self), self.name.string }); + io.print("DONE MARKING CONSTANTS OF FUNCTION @{} {s}\n", .{ @intFromPtr(self), self.name.string }); } } @@ -4757,7 +4758,7 @@ pub const PlaceholderDef = struct { if (child.resolved_type.?.Placeholder.parent != null) { if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( ">>> Placeholder @{} ({s}) has already a {} relation with @{} ({s})\n", .{ @intFromPtr(child), @@ -4776,7 +4777,7 @@ pub const PlaceholderDef = struct { child.resolved_type.?.Placeholder.parent_relation = relation; if (BuildOptions.debug_placeholders) { - std.debug.print( + io.print( "Linking @{} (root: {}) with @{} as {s}\n", .{ @intFromPtr(parent), diff --git a/src/repl.zig b/src/repl.zig index 91c3aee5..209584f0 100644 --- a/src/repl.zig +++ b/src/repl.zig @@ -38,8 +38,9 @@ const dumpStack = disassembler.dumpStack; const DumpState = disassembler.DumpState; const CodeGen = @import("Codegen.zig"); const Scanner = @import("Scanner.zig"); +const io = @import("io.zig"); -pub fn printBanner(out: std.fs.File.Writer, full: bool) void { +pub fn printBanner(out: anytype, full: bool) void { out.print( "\n👨‍🚀 buzz {}-{s} Copyright (C) 2021-present Benoit Giannangeli\n", .{ @@ -131,8 +132,8 @@ pub fn repl(allocator: std.mem.Allocator) !void { // TODO: free type_registry and its keys which are on the heap } - var stdout = std.io.getStdOut().writer(); - var stderr = std.io.getStdErr().writer(); + var stdout = io.stdOutWriter; + var stderr = io.stdErrWriter; printBanner(stdout, false); var buzz_history_path = std.ArrayList(u8).init(allocator); @@ -294,7 +295,7 @@ fn runSource( } if (BuildOptions.show_perf) { - std.debug.print( + io.print( "\u{001b}[2mParsing: {d}\nCodegen: {d}\nRun: {d}\nJIT: {d}\nGC: {d}\nTotal: {d}\nFull GC: {} | GC: {} | Max allocated: {} bytes\n\u{001b}[0m", .{ std.fmt.fmtDuration(parsing_time), diff --git a/src/vm.zig b/src/vm.zig index 26784134..0a2efa47 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -18,6 +18,7 @@ const Token = @import("Token.zig"); const Reporter = @import("Reporter.zig"); const FFI = if (!is_wasm) @import("FFI.zig") else void; const dispatch_call_modifier: std.builtin.CallModifier = if (!is_wasm) .always_tail else .auto; +const io = @import("io.zig"); const ObjType = _obj.ObjType; const Obj = _obj.Obj; @@ -3849,6 +3850,8 @@ pub const VM = struct { // Never generated if jit is disabled fn OP_HOTSPOT(self: *Self, _: *CallFrame, _: u32, _: OpCode, end_ip: u24) void { + if (is_wasm) unreachable; + const node = self.readInstruction(); const count = (self.hotspots.get(node) orelse 0) + 1; @@ -3884,7 +3887,7 @@ pub const VM = struct { obj_native.mark(self.gc); if (BuildOptions.jit_debug) { - std.debug.print( + io.print( "Compiled hotspot {s} in function `{s}` in {d} ms\n", .{ @tagName(self.current_ast.nodes.items(.tag)[node]), @@ -3937,6 +3940,8 @@ pub const VM = struct { } fn OP_HOTSPOT_CALL(self: *Self, _: *CallFrame, _: u32, _: OpCode, _: u24) void { + if (is_wasm) unreachable; + if (self.callHotspot( @ptrCast( @alignCast( @@ -3968,7 +3973,7 @@ pub const VM = struct { const next_arg: u24 = getArg(next_full_instruction); if (BuildOptions.debug_current_instruction) { - std.debug.print( + io.print( "{}: {}\n", .{ next_current_frame.ip, @@ -4206,7 +4211,7 @@ pub const VM = struct { }; if (BuildOptions.jit_debug and success) { - std.debug.print( + io.print( "Compiled function `{s}` in {d} ms\n", .{ closure.function.type_def.resolved_type.?.Function.name.string, @@ -4226,7 +4231,7 @@ pub const VM = struct { // Is there a compiled version of it? if (native != null) { if (BuildOptions.jit_debug) { - std.debug.print("Calling compiled version of function `{s}`\n", .{closure.function.name.string}); + io.print("Calling compiled version of function `{s}`\n", .{closure.function.name.string}); } try self.callCompiled( @@ -4270,7 +4275,7 @@ pub const VM = struct { } if (self.flavor == .Test and closure.function.type_def.resolved_type.?.Function.function_type == .Test) { - std.debug.print( + io.print( "\x1b[33m▶ Test: {s}\x1b[0m\n", .{ self.current_ast.tokens.items(.lexeme)[self.current_ast.nodes.items(.components)[closure.function.node].Function.test_message.?], @@ -4296,7 +4301,7 @@ pub const VM = struct { self.current_fiber.frame_count += 1; if (BuildOptions.jit_debug) { - std.debug.print("Calling uncompiled version of function `{s}`\n", .{closure.function.name.string}); + io.print("Calling uncompiled version of function `{s}`\n", .{closure.function.name.string}); } } @@ -4312,7 +4317,7 @@ pub const VM = struct { fn callHotspot(self: *Self, native: NativeFn) bool { if (BuildOptions.jit_debug) { - std.debug.print("Calling hotspot {*}\n", .{native}); + io.print("Calling hotspot {*}\n", .{native}); } const was_in_native_call = self.currentFrame().?.in_native_call; diff --git a/src/wasm.zig b/src/wasm.zig index 96af9f5b..87117e53 100644 --- a/src/wasm.zig +++ b/src/wasm.zig @@ -4,6 +4,45 @@ extern fn writeToStderr(string_ptr: [*]const u8, string_length: usize) void; extern fn writeToStdout(string_ptr: [*]const u8, string_length: usize) void; extern fn readFromStdin(buffer_ptr: [*]const u8, buffer_length: usize) isize; +pub fn stdErrWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { + writeToStderr(bytes.ptr, bytes.len); + + return bytes.len; +} + +pub fn stdOutWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { + writeToStderr(bytes.ptr, bytes.len); + + return bytes.len; +} + +pub fn stdInRead(_: void, buffer: []u8) std.posix.ReadError!usize { + if (buffer.len == 0) { + return 0; + } + + return @intCast( + readFromStdin( + buffer.ptr, + buffer.len, + ), + ); +} + +pub const io = struct { + pub fn getStdInHandle() std.posix.fd_t { + return std.os.emscripten.STDIN_FILENO; + } + + pub fn getStdErrHandle() std.posix.fd_t { + return std.os.emscripten.STDERR_FILENO; + } + + pub fn getStdOutHandle() std.posix.fd_t { + return std.os.emscripten.STDOUT_FILENO; + } +}; + pub const system = struct { var errno: E = undefined; diff --git a/src/wasm_repl.zig b/src/wasm_repl.zig index 267e9884..b2d87ef5 100644 --- a/src/wasm_repl.zig +++ b/src/wasm_repl.zig @@ -18,6 +18,7 @@ const disassembler = @import("disassembler.zig"); const DumpState = disassembler.DumpState; const Scanner = @import("Scanner.zig"); const printBanner = @import("repl.zig").printBanner; +const io = @import("io.zig"); pub const ReplCtx = extern struct { vm: *VM, @@ -64,7 +65,7 @@ pub export fn initRepl() *ReplCtx { null, ); - printBanner(std.io.getStdOut().writer(), true); + printBanner(io.stdOutWriter, true); // Import std and debug as commodity _ = runSource( @@ -74,7 +75,7 @@ pub export fn initRepl() *ReplCtx { codegen, parser, ) catch |err| { - std.debug.print("Failed with error: {}\n", .{err}); + io.print("Failed with error: {}\n", .{err}); unreachable; }; @@ -90,9 +91,9 @@ pub export fn initRepl() *ReplCtx { } pub export fn runLine(ctx: *ReplCtx) void { - var stdout = std.io.getStdOut().writer(); - var stderr = std.io.getStdErr().writer(); - var stdin = std.io.getStdIn().reader(); + var stdout = io.stdOutWriter; + var stderr = io.stdErrWriter; + var stdin = io.stdInReader; var previous_global_top = ctx.vm.globals_count; var previous_parser_globals = ctx.parser.globals.clone() catch unreachable;