return runtime.__process_wait(process_handle);
}
+process_destroy :: (use p: ^Process) => {
+ runtime.__process_destroy(process_handle);
+}
+
#local process_stream_vtable := Stream_Vtable.{
read = (use p: ^Process, buffer: [] u8) -> (Error, u32) {
// Read from the process stdout
reader: Reader;
reader.stream = s;
+ reader.error = .None;
memory.alloc_slice(^reader.buffer, buffer_size, allocator);
reader.buffer_allocator = allocator;
return done && reader_get_buffered(reader) == 0;
}
+read_all :: (use reader: ^Reader, allocator := context.allocator) -> [] u8 {
+ array :: package core.array
+ output := array.make(u8, 128, allocator=allocator);
+
+ while !reader_empty(reader) {
+ if err := reader_read_next_chunk(reader); err != .None do break;
+ if error != .None {
+ reader_consume_error(reader);
+ break;
+ }
+
+ buffered := reader_get_buffered(reader);
+ if buffered > 0 {
+ array.ensure_capacity(^output, output.count + buffered);
+ memory.copy(output.data + output.count, buffer.data, buffered);
+ output.count += buffered;
+ start = end;
+ }
+ }
+
+ return output;
+}
+
read_byte :: (use reader: ^Reader) -> u8 {
while start == end {
if error != .None {
Error :: 0x02;
}
-__process_spawn :: (path: str, args: [] str, non_blocking_io: bool) -> io.Process.Handle #foreign "env" "process_spawn" ---
-__process_read :: (handle: io.Process.Handle, buffer: [] u8) -> u32 #foreign "env" "process_read" ---
-__process_write :: (handle: io.Process.Handle, buffer: [] u8) -> u32 #foreign "env" "process_write" ---
-__process_kill :: (handle: io.Process.Handle) -> bool #foreign "env" "process_kill" ---
-__process_wait :: (handle: io.Process.Handle) -> ProcessResult #foreign "env" "process_wait" ---
\ No newline at end of file
+__process_spawn :: (path: str, args: [] str, non_blocking_io: bool) -> io.Process.Handle #foreign "env" "process_spawn" ---
+__process_read :: (handle: io.Process.Handle, buffer: [] u8) -> u32 #foreign "env" "process_read" ---
+__process_write :: (handle: io.Process.Handle, buffer: [] u8) -> u32 #foreign "env" "process_write" ---
+__process_kill :: (handle: io.Process.Handle) -> bool #foreign "env" "process_kill" ---
+__process_wait :: (handle: io.Process.Handle) -> ProcessResult #foreign "env" "process_wait" ---
+__process_destroy :: (handle: io.Process.Handle) -> void #foreign "env" "process_destroy" ---
\ No newline at end of file
return str.{ data, total_length };
},
+ (buffer: [] u8, strings: ..str) -> str {
+ total_copied := 0;
+ for s: strings {
+ // Should never greater than, but better safe than sorry.
+ if total_copied >= buffer.count do break;
+
+ bytes_to_copy := math.min(s.count, buffer.count - total_copied);
+ memory.copy(buffer.data + total_copied, s.data, bytes_to_copy);
+ total_copied += bytes_to_copy;
+ }
+
+ return buffer[0 .. total_copied];
+ },
+
(into: ^[..] u8, s: str) -> str {
array.ensure_capacity(into, into.count + s.count);
memory.copy(into.data, s.data, into.count);
NetReset :: 0x27;
NetUnreach :: 0x28;
NFile :: 0x29;
- NoBufs :: 0x2a;
+ NoBufs :: 0x2a;
NoDev :: 0x2b;
NoEntry :: 0x2c;
NoExec :: 0x2d;
Directory :: 0x03;
RegularFile :: 0x04;
SocketDgram :: 0x05;
- SocketStream :: 0x06;
+ SocketStream :: 0x06;
SymLink :: 0x07;
}
}
Advice :: enum (u8) {
- Normal :: 0x00;
+ Normal :: 0x00;
Sequential :: 0x01;
Random :: 0x02;
WillNeed :: 0x03;
- match: '\b(i8x16|i16x8|i32x4|i64x2|f32x4|f64x2|v128)\b'
scope: storage.type
- - match: '\b(true|false|null|null_proc|context)\b'
+ - match: '\b(true|false|null|null_proc|context|it)\b'
scope: constant.numeric.onyx
# Numbers
--- /dev/null
+#load "core/std"
+
+use package core
+#local wasi :: package wasi
+
+Directory_Entry :: struct {
+ dirent : wasi.DirEnt;
+ name : str;
+}
+
+list_directory :: (path: str) -> Iterator(Directory_Entry) {
+ Context :: struct {
+ dir_fd: wasi.FileDescriptor;
+ opened := false;
+ reached_end := false;
+
+ entry_walker: ^u8;
+
+ buffer_used: u32 = 0;
+ buffer: [1024] u8;
+
+ last_cookie: wasi.DirCookie = 0;
+ }
+
+ next :: (use c: ^Context) -> (Directory_Entry, bool) {
+ use package core.intrinsics.onyx {__zero_value}
+ if !opened do return __zero_value(Directory_Entry), false;
+
+ read_more :: macro () {
+ if !reached_end {
+ err := wasi.fd_readdir(dir_fd, ~~buffer, sizeof typeof buffer, last_cookie, ^buffer_used);
+ reached_end = buffer_used != sizeof typeof buffer;
+
+ entry_walker = ~~buffer;
+ } else {
+ return __zero_value(Directory_Entry), false;
+ }
+ }
+
+ if buffer_used < sizeof(wasi.DirEnt) do read_more();
+
+ dirent := cast(^wasi.DirEnt) entry_walker;
+ if buffer_used < sizeof(wasi.DirEnt) + dirent.d_namlen {
+ read_more();
+ dirent = ~~entry_walker;
+ }
+
+ name := str.{~~(dirent + 1), dirent.d_namlen};
+
+ entry_size := sizeof(wasi.DirEnt) + dirent.d_namlen;
+ entry_walker += entry_size;
+ buffer_used -= entry_size;
+ last_cookie = dirent.d_next;
+
+ return .{*dirent, name}, true;
+ }
+
+ close :: (use c: ^Context) {
+ wasi.fd_close(dir_fd);
+ cfree(c);
+ }
+
+ c := new(Context);
+ if err := wasi.path_open(4, 0, path, .Directory, ~~0xffffffff, ~~0xffffffff, .Sync, ^c.dir_fd); err == .Success {
+ c.opened = true;
+ }
+
+ return .{ c, next, close };
+}
+
+@Temporary
+find_onyx_files :: (root: str, cases: ^[..] str) {
+ for list_directory(root) {
+ path_buffer: [512] u8;
+ if string.ends_with(it.name, ".onyx") {
+ test_case := string.concat(path_buffer, root, "/", it.name) |> string.alloc_copy();
+ array.push(cases, test_case);
+ }
+
+ if it.dirent.d_type == .Directory {
+ find_onyx_files(string.concat(path_buffer, root, "/", it.name), cases);
+ }
+ }
+
+ return;
+}
+
+main :: (args) => {
+ test_folder := "./tests";
+
+ cases := array.make(str);
+ find_onyx_files(test_folder, ^cases);
+ for cases {
+ printf("Running test {}...\n", it);
+
+ proc := io.process_spawn("./bin/onyx", .["run", it]);
+ defer io.process_destroy(^proc);
+
+ proc_reader := io.reader_make(^proc);
+ output := io.read_all(^proc_reader);
+ defer memory.free_slice(^output);
+
+ if exit := io.process_wait(^proc); exit != .Success {
+ // Error running the test case
+ println(exit);
+ println(output);
+ }
+ }
+
+ // Ensure the corresponding correct output file present
+ // Run tests, checking their output
+}
\ No newline at end of file
process_path[process_len] = '\0';
OnyxProcess *process = bh_alloc_item(global_heap_allocator, OnyxProcess);
+ memset(process, 0, sizeof(*process));
process->magic_number = ONYX_PROCESS_MAGIC_NUMBER;
#ifdef _BH_LINUX
switch (pid = fork()) {
case -1: // Bad fork
wasm_val_init_ptr(&results->data[0], NULL); // Failed to run
+
+ close(process->proc_to_host[0]);
+ close(process->proc_to_host[1]);
+ close(process->host_to_proc[0]);
+ close(process->host_to_proc[1]);
break;
case 0: // Child process
close(process->proc_to_host[0]);
close(process->host_to_proc[1]);
- dup2(process->proc_to_host[1], 1); // Map the output to the pipe
dup2(process->host_to_proc[0], 0); // Map the output to the pipe
+ dup2(process->proc_to_host[1], 1); // Map the output to the pipe
+ dup2(process->proc_to_host[1], 2); // Stderr to stdout
if (!blocking_io) {
fcntl(0, F_SETFL, O_NONBLOCK);
}
execv(process_path, process_args);
- wasm_val_init_ptr(&results->data[0], NULL);
+ exit(-1);
break;
-
+
default: {
process->pid = pid;
close(process->host_to_proc[0]);
char cmdLine[2048];
memset(cmdLine, 0, 2048);
strncat(cmdLine, process_path, 2047);
-
+
byte_t* data = wasm_memory_data(wasm_memory);
byte_t* array_loc = data + args_ptr;
fori (i, 0, args_len) {
#ifdef _BH_LINUX
i32 status;
waitpid(process->pid, &status, 0);
+ close(process->proc_to_host[0]);
+ close(process->host_to_proc[1]);
i32 exit_code = WEXITSTATUS(status);
results->data[0] = WASM_I32_VAL(exit_code != 0 ? 2 : 0);
return NULL;
}
+WASM_INTEROP(onyx_process_destroy_impl) {
+ OnyxProcess *process = (OnyxProcess *) params->data[0].of.i64;
+ if (process == NULL || process->magic_number != ONYX_PROCESS_MAGIC_NUMBER) {
+ return NULL;
+ }
+
+ #ifdef _BH_LINUX
+ kill(process->pid, SIGKILL);
+ close(process->proc_to_host[0]);
+ close(process->host_to_proc[1]);
+ bh_free(global_heap_allocator, process);
+ #endif
+
+ return NULL;
+}
+
void onyx_run_wasm(bh_buffer wasm_bytes) {
wasm_instance_t* instance = NULL;
wasmer_features_t* features = NULL;
import = wasm_func_as_extern(wasm_func);
goto import_found;
}
+
+ if (wasm_name_equals_string(import_name, "process_destroy")) {
+ wasm_functype_t* func_type = wasm_functype_new_1_0(wasm_valtype_new_i64());
+
+ wasm_func_t* wasm_func = wasm_func_new(wasm_store, func_type, onyx_process_destroy_impl);
+ import = wasm_func_as_extern(wasm_func);
+ goto import_found;
+ }
}
goto bad_import;