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 {...@@ -258,18 +258,6 @@ pub const ArgIteratorWasi = struct {
258 }258 }
259};259};
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
273pub const ArgIteratorWindows = struct {261pub const ArgIteratorWindows = struct {
274 index: usize,262 index: usize,
275 cmd_line: [*]const u8,263 cmd_line: [*]const u8,
...@@ -429,15 +417,9 @@ pub const ArgIterator = struct {...@@ -429,15 +417,9 @@ pub const ArgIterator = struct {
429 inner: InnerType,417 inner: InnerType,
430418
431 /// Initialize the args iterator.419 /// 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.
437 pub fn init() ArgIterator {420 pub fn init() ArgIterator {
438 if (builtin.os.tag == .wasi) {421 if (builtin.os.tag == .wasi) {
439 const allocator = std.heap.page_allocator;422 @compileError("In WASI, use initWithAllocator instead.");
440 return ArgIterator.initWasi(allocator) catch @panic("unexpected error occurred when initializing ArgIterator");
441 }423 }
442424
443 return ArgIterator{ .inner = InnerType.init() };425 return ArgIterator{ .inner = InnerType.init() };
...@@ -445,10 +427,13 @@ pub const ArgIterator = struct {...@@ -445,10 +427,13 @@ pub const ArgIterator = struct {
445427
446 pub const InitError = ArgIteratorWasi.InitError;428 pub const InitError = ArgIteratorWasi.InitError;
447429
448 /// If you are targeting WASI, you can call this to manually specify the allocator and430 /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
449 /// handle any errors.431 pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator {
450 pub fn initWasi(allocator: *mem.Allocator) InitError!ArgIterator {432 if (builtin.os.tag == .wasi) {
451 return ArgIterator{ .inner = try InnerType.init(allocator) };433 return ArgIterator{ .inner = try InnerType.init(allocator) };
434 }
435
436 return ArgIterator{ .inner = InnerType.init() };
452 }437 }
453438
454 pub const NextError = ArgIteratorWindows.NextError;439 pub const NextError = ArgIteratorWindows.NextError;
...@@ -478,10 +463,13 @@ pub const ArgIterator = struct {...@@ -478,10 +463,13 @@ pub const ArgIterator = struct {
478 return self.inner.skip();463 return self.inner.skip();
479 }464 }
480465
481 /// If you are targeting WASI, call this to free the iterator's internal buffer466 /// Call this to free the iterator's internal buffer if the iterator
482 /// after you are done with it.467 /// was created with `initWithAllocator` function.
483 pub fn deinitWasi(self: *ArgIterator) void {468 pub fn deinit(self: *ArgIterator) void {
484 self.inner.deinit();469 // Unless we're targeting WASI, this is a no-op.
470 if (builtin.os.tag == .wasi) {
471 self.inner.deinit();
472 }
485 }473 }
486};474};
487475
...@@ -489,11 +477,33 @@ pub fn args() ArgIterator {...@@ -489,11 +477,33 @@ pub fn args() ArgIterator {
489 return ArgIterator.init();477 return ArgIterator.init();
490}478}
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
492/// Caller must call argsFree on result.502/// Caller must call argsFree on result.
493pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {503pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
494 // TODO refactor to only make 1 allocation.504 // TODO refactor to only make 1 allocation.
495 var it = args();505 var it = if (builtin.os.tag == .wasi) argsWithAllocator(allocator) else args();
496 defer if (builtin.os.tag == .wasi) it.deinitWasi();506 defer it.deinit();
497507
498 var contents = std.ArrayList(u8).init(allocator);508 var contents = std.ArrayList(u8).init(allocator);
499 defer contents.deinit();509 defer contents.deinit();