authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-05-29 08:40:32+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-05-29 10:48:03+02:00
log6f48842ddb2d8ca8fec639dc11a47753058ae325
treeeb029f7190abb21b1e048e84750ddb0a68008935
parentf1a4e1a70f4ecefbae7813d7170f465234c03273

Make ArgIterator.init() a compile error in WASI

Given that the previous design would require the use of a default allocator to have `ArgIterator.init()` work in WASI, and since in Zig we're trying to avoid default allocators, I've changed the design slightly in that now `init()` is a compile error in WASI, and instead in its message it points to `initWithAllocator(*mem.Allocator)`. The latter by virtue of requiring an allocator as an argument can safely be used in WASI as well as on other OSes (where the allocator argument is simply unused). When using `initWithAllocator` it is then natural to remember to call `deinit()` after being done with the iterator. Also, to make use of this, I've also added `argsWithAllocator` function which is equivalent to `args` minus the requirement of supplying an allocator and being fallible. Finally, I've also modified the WASI only test `process.ArgWasiIterator` to test all OSes.

1 files changed, 39 insertions(+), 29 deletions(-)

lib/std/process.zig+39-29
......@@ -258,18 +258,6 @@ pub const ArgIteratorWasi = struct {
258258 }
259259};
260260
261test "process.ArgIteratorWasi" {
262 if (builtin.os.tag != .wasi) return error.SkipZigTest;
263
264 var ga = std.testing.allocator;
265 var args_it = try ArgIteratorWasi.init(ga);
266 defer args_it.deinit();
267
268 testing.expectEqual(@as(usize, 1), args_it.args.len);
269 const prog_name = args_it.next() orelse unreachable;
270 testing.expect(mem.eql(u8, "test.wasm", prog_name));
271}
272
273261pub const ArgIteratorWindows = struct {
274262 index: usize,
275263 cmd_line: [*]const u8,
......@@ -429,15 +417,9 @@ pub const ArgIterator = struct {
429417 inner: InnerType,
430418
431419 /// Initialize the args iterator.
432 ///
433 /// On WASI, will panic if the default Wasm page allocator runs out of memory
434 /// or there is an error fetching the args from the runtime. If you want to
435 /// use custom allocator and handle the errors yourself, call `initWasi()` instead.
436 /// You also must remember to free the buffer with `deinitWasi()` call.
437420 pub fn init() ArgIterator {
438421 if (builtin.os.tag == .wasi) {
439 const allocator = std.heap.page_allocator;
440 return ArgIterator.initWasi(allocator) catch @panic("unexpected error occurred when initializing ArgIterator");
422 @compileError("In WASI, use initWithAllocator instead.");
441423 }
442424
443425 return ArgIterator{ .inner = InnerType.init() };
......@@ -445,10 +427,13 @@ pub const ArgIterator = struct {
445427
446428 pub const InitError = ArgIteratorWasi.InitError;
447429
448 /// If you are targeting WASI, you can call this to manually specify the allocator and
449 /// handle any errors.
450 pub fn initWasi(allocator: *mem.Allocator) InitError!ArgIterator {
451 return ArgIterator{ .inner = try InnerType.init(allocator) };
430 /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
431 pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator {
432 if (builtin.os.tag == .wasi) {
433 return ArgIterator{ .inner = try InnerType.init(allocator) };
434 }
435
436 return ArgIterator{ .inner = InnerType.init() };
452437 }
453438
454439 pub const NextError = ArgIteratorWindows.NextError;
......@@ -478,10 +463,13 @@ pub const ArgIterator = struct {
478463 return self.inner.skip();
479464 }
480465
481 /// If you are targeting WASI, call this to free the iterator's internal buffer
482 /// after you are done with it.
483 pub fn deinitWasi(self: *ArgIterator) void {
484 self.inner.deinit();
466 /// Call this to free the iterator's internal buffer if the iterator
467 /// was created with `initWithAllocator` function.
468 pub fn deinit(self: *ArgIterator) void {
469 // Unless we're targeting WASI, this is a no-op.
470 if (builtin.os.tag == .wasi) {
471 self.inner.deinit();
472 }
485473 }
486474};
487475
......@@ -489,11 +477,33 @@ pub fn args() ArgIterator {
489477 return ArgIterator.init();
490478}
491479
480/// You must deinitialize iterator's internal buffers by calling `deinit` when done.
481pub fn argsWithAllocator(allocator: *mem.Allocator) ArgIterator.InitError!ArgIterator {
482 return ArgIterator.initWithAllocator(allocator);
483}
484
485test "args iterator" {
486 var ga = std.testing.allocator;
487 var it = if (builtin.os.tag == .wasi) argsWithAllocator(ga) else args();
488 defer it.deinit(); // no-op unless WASI
489
490 testing.expect(it.skip());
491 const prog_name = it.next(ga) orelse unreachable;
492 defer ga.free(prog_name);
493
494 const expected_bin_name = switch (builtin.os.tag) {
495 .wasi => "test.wasm",
496 .windows => "test.exe",
497 else => "test",
498 };
499 testing.expect(mem.eql(u8, expected_bin_name, prog_name));
500}
501
492502/// Caller must call argsFree on result.
493503pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
494504 // TODO refactor to only make 1 allocation.
495 var it = args();
496 defer if (builtin.os.tag == .wasi) it.deinitWasi();
505 var it = if (builtin.os.tag == .wasi) argsWithAllocator(allocator) else args();
506 defer it.deinit();
497507
498508 var contents = std.ArrayList(u8).init(allocator);
499509 defer contents.deinit();