From d7d43852422f1abec1fba6b9ba38ca50ebadcacc Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 27 Apr 2022 20:01:00 -0300 Subject: [PATCH] fix memory leak on string arguments --- src/lang.zig | 19 +++++++++++++++++-- src/main.zig | 6 +++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/lang.zig b/src/lang.zig index 3ad0c41..f465895 100644 --- a/src/lang.zig +++ b/src/lang.zig @@ -479,6 +479,21 @@ pub const CommandList = struct { pub fn deinit(self: *Self) void { for (self.list.items) |cmd_ptr| { self.list.allocator.destroy(cmd_ptr); + inline for (@typeInfo(Command.Tag).Enum.fields) |field| { + if (cmd_ptr.tag == @field(Command.Tag, field.name)) { + const actual_tag = + @field(Command.Tag, field.name); + // if we find a match on the tag, we can get the type + const typ = Command.tagToType(actual_tag); + + inline for (@typeInfo(typ).Struct.fields) |cmd_field| { + switch (cmd_field.field_type) { + []u8, []const u8 => self.list.allocator.destroy(@field(typ, cmd_field.name)), + else => {}, + } + } + } + } } self.list.deinit(); } @@ -561,7 +576,7 @@ pub const Lang = struct { f32 => try std.fmt.parseFloat(f32, arg), u64 => try std.fmt.parseInt(u64, arg, 10), usize => try std.fmt.parseInt(usize, arg, 10), - []const u8 => arg, + []const u8 => @as([]const u8, try self.allocator.dupe(u8, arg)), else => @compileError("parameter struct has unsupported type " ++ @typeName(cmd_field.field_type)), }; @@ -586,7 +601,7 @@ pub const Lang = struct { usize => try std.fmt.parseInt(usize, arg, 10), i32 => try std.fmt.parseInt(i32, arg, 10), f32 => try std.fmt.parseFloat(f32, arg), - []const u8 => arg, + []const u8 => @as([]const u8, try self.allocator.dupe(u8, arg)), else => @compileError("Invalid parameter type (" ++ @typeName(cmd_field.field_type) ++ ") left on command struct " ++ @typeName(command_struct) ++ "."), }; diff --git a/src/main.zig b/src/main.zig index f1cb3c8..a096f4f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -40,6 +40,8 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: anytype) !void { var stdout_file = std.io.getStdOut(); const stdout = &stdout_file.writer(); const scri_path = try (args_it.next(allocator) orelse @panic("expected scri path")); + errdefer allocator.free(scri_path); + defer allocator.free(scri_path); var file_read_opt: ?std.fs.File = std.fs.cwd().openFile(scri_path, .{}) catch |err| blk: { if (err == error.FileNotFound) break :blk null; @@ -54,11 +56,9 @@ pub fn doRepl(allocator: *std.mem.Allocator, args_it: anytype) !void { defer lang.deinit(); if (total_bytes > 0) { - // this MUST BE long lived (a reference to it is kept inside - // existing_cmds, and then passed along to cmds), - // we can't defer them here var scri_existing = try allocator.alloc(u8, total_bytes); _ = try file_read_opt.?.read(scri_existing); + defer allocator.free(scri_existing); // we can't defer this directly because we copy the // Command pointers to the cmds list. running deinit() directly