feat: preprocess native argv into sentinel slices

This commit is contained in:
2026-10-23 12:07:23 +02:00
parent eddca5c3bf
commit 8ebbf72388
8 changed files with 167 additions and 93 deletions
+57 -47
View File
@@ -3534,10 +3534,28 @@ main func() int ! Error { return try value() }
source=`process :: import "@std/process"
Error :: enum { failed }
fail func() void ! Error { return .failed }
main func(_ process.Init) void ! Error { try fail() }
main func(init process.Init) void ! Error {
if init.args.values.len != 3 { return }
if init.args.values[1].len != 0 or init.args.values[2].len != 9 { return }
if init.args.values[2][0] != 't' or init.args.values[2][8] != 's' { return }
try fail()
}
`,
exit_code=1,
},
{
name="process-success",
source=`process :: import "@std/process"
Error :: enum { failed }
main func(init process.Init) i32 ! Error {
if init.args.values.len != 3 { return .failed }
if init.args.values[1].len != 0 or init.args.values[2].len != 9 { return .failed }
if init.args.values[2][0] != 't' or init.args.values[2][8] != 's' { return .failed }
return 7
}
`,
exit_code=7,
},
{
name="inferred-main-try",
source=`Error :: enum { failed }
@@ -3578,7 +3596,12 @@ main func() void! { try dispatch(2) }
source=`process :: import "@std/process"
Error :: enum { failed }
fail func() void ! Error { return .failed }
main func(_ process.Init) void! { try fail() }
main func(init process.Init) void! {
if init.args.values.len != 3 { return }
if init.args.values[1].len != 0 or init.args.values[2].len != 9 { return }
if init.args.values[2][0] != 't' or init.args.values[2][8] != 's' { return }
try fail()
}
`,
exit_code=1,
},
@@ -3603,7 +3626,7 @@ main func(_ process.Init) void! { try fail() }
)
testing.expect_value(t, status, 0)
state, stdout, stderr, _ := os2.process_exec(
os2.Process_Desc{command=[]string{output}},
os2.Process_Desc{command=[]string{output, "", "two words"}},
context.allocator,
)
defer delete(stdout)
@@ -3941,7 +3964,7 @@ milestone_37_validates_process_init_and_hidden_system_provider :: proc(t: ^testi
system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*;0]u8 }
Args :: struct { values [][;0]u8 }
Init :: struct { io io.Io, args Args }
`,
},
@@ -3950,7 +3973,7 @@ Init :: struct { io io.Io, args Args }
@hide system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*;0]u8 }
Args :: struct { values [][;0]u8 }
Init :: struct { io io.Io, args Args, extra i32 }
`,
},
@@ -3959,7 +3982,7 @@ Init :: struct { io io.Io, args Args, extra i32 }
@hide system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*;0]u8 }
Args :: struct { values [][;0]u8 }
Init :: struct { io io.Io }
`,
},
@@ -3968,7 +3991,7 @@ Init :: struct { io io.Io }
@hide system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*]u8 }
Args :: struct { values [][]u8 }
Init :: struct { io io.Io, args Args }
`,
},
@@ -3977,7 +4000,7 @@ Init :: struct { io io.Io, args Args }
@hide system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*;0]u8, extra i32 }
Args :: struct { values [][;0]u8, extra i32 }
Init :: struct { io io.Io, args Args }
`,
},
@@ -3986,7 +4009,7 @@ Init :: struct { io io.Io, args Args }
@hide system func() Io { return Io {value = 0} }
`,
process_source=`io :: import "@std/io"
Args :: struct { values [][*;0]u8 }
Args :: struct { values [][;0]u8 }
Init :: struct { io io.Io, args i32 }
`,
},
@@ -4030,7 +4053,7 @@ main func(init process.Init) void { _ = init }
}
@(test)
process_init_supplies_native_arguments_and_helpers_borrow_explicit_storage :: proc(t: ^testing.T) {
process_init_supplies_native_argument_slices :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-process-args"
main_path := "/tmp/brolang-test-process-args/main.bro"
output := "/tmp/brolang-test-process-args/app"
@@ -4038,43 +4061,23 @@ process_init_supplies_native_arguments_and_helpers_borrow_explicit_storage :: pr
io :: import "@std/io"
mem :: import "@std/mem"
supplied_args_work func() bool {
text [3;0]mut u8 := ['r', 'u', 'n']
pointers [1][*;0]u8 :: [(&text).ptr]
args :: process.Args { values = pointers[..] }
if process.argument_count(args) != 1 { return false }
if process.argument(args, 0) |value| {
text[0] = 'f'
if !mem.eql(value, "fun") { return false }
} else { return false }
if process.argument(args, 1) |_| { return false }
empty :: process.Args { values = pointers[0..0] }
if process.argument_count(empty) != 0 { return false }
if process.argument(empty, 0) |_| { return false }
return true
}
main func(init process.Init) i32 {
count :: process.argument_count(init.args)
if count != 1 and count != 4 { return 1 }
if process.argument(init.args, 0) |executable| {
if !mem.eql(executable, "/tmp/brolang-test-process-args/app") { return 2 }
} else { return 3 }
if process.argument(init.args, count) |_| { return 4 }
if process.argument(init.args, maxval!(usize)) |_| { return 5 }
count :: init.args.values.len
if count != 1 and count != 4 and count != 16385 { return 1 }
if !mem.eql(init.args.values[0], "/tmp/brolang-test-process-args/app") { return 2 }
if count == 4 {
if process.argument(init.args, 1) |operation| {
if !mem.eql(operation, "run") { return 6 }
} else { return 7 }
if process.argument(init.args, 2) |empty| {
if empty.len != 0 { return 8 }
} else { return 9 }
if process.argument(init.args, 3) |spaced| {
if !mem.eql(spaced, "two words") { return 10 }
} else { return 11 }
if !mem.eql(init.args.values[1], "run") { return 3 }
if init.args.values[2].len != 0 { return 4 }
if !mem.eql(init.args.values[3], "two words") { return 5 }
}
if !supplied_args_work() { return 12 }
io.write_all(io.stdout(init.io), "args-ok\n") catch |_| { return 13 }
if count == 16385 {
index := usize(1)
while index < count {
if init.args.values[index].len != 0 { return 6 }
index += 1
}
}
io.write_all(io.stdout(init.io), "args-ok\n") catch |_| { return 7 }
return 0
}
`
@@ -4085,9 +4088,16 @@ main func(init process.Init) i32 {
testing.expect_value(t, compiler_core.compile_package(
directory, output, nil, target.DEFAULT, cimport.Options{}, ".",
), 0)
large_command := make([]string, 16385)
defer delete(large_command)
large_command[0] = output
for index in 1..<len(large_command) {
large_command[index] = ""
}
commands := [][]string{
{output},
{output, "run", "", "two words"},
large_command,
}
for command in commands {
state, stdout, stderr, error := os2.process_exec(
@@ -4126,16 +4136,16 @@ process_init_startup_respects_reordered_fields :: proc(t: ^testing.T) {
@hide system func() Io { return Io {value = 37} }
`
process_source := `io :: import "@std/io"
Args :: struct { values [][*;0]u8 }
Args :: struct { values [][;0]u8 }
Init :: struct { args Args, io io.Io }
`
main_source := `process :: import "@std/process"
main func(init process.Init) i32 {
if init.io.value != 37 or init.args.values.len != 3 { return 1 }
empty :: init.args.values[1]
if empty[0] != 0 { return 2 }
if empty.len != 0 { return 2 }
spaced :: init.args.values[2]
if spaced[0] != 'a' or spaced[1] != ' ' or spaced[2] != 'b' or spaced[3] != 0 { return 3 }
if spaced.len != 3 or spaced[0] != 'a' or spaced[1] != ' ' or spaced[2] != 'b' { return 3 }
return 0
}
`