cleanup: restructured platform layer code
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 5 Mar 2023 04:23:50 +0000 (22:23 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 5 Mar 2023 04:23:50 +0000 (22:23 -0600)
Platform layer code was scattered and undocumented what was expected.
This has been remedied, and a good baseline has been defined.

30 files changed:
core/io/stdio.onyx
core/net/net.onyx
core/net/tcp.onyx
core/onyx/fs.onyx [deleted file]
core/os/dir.onyx
core/os/file.onyx
core/os/os.onyx
core/os/process.onyx
core/runtime/common.onyx
core/runtime/js.onyx [deleted file]
core/runtime/onyx_run.onyx [deleted file]
core/runtime/platform/js/platform.onyx [new file with mode: 0644]
core/runtime/platform/onyx/fs.onyx [new file with mode: 0644]
core/runtime/platform/onyx/platform.onyx [new file with mode: 0644]
core/runtime/platform/wasi/clock.onyx [new file with mode: 0644]
core/runtime/platform/wasi/env.onyx [new file with mode: 0644]
core/runtime/platform/wasi/platform.onyx [new file with mode: 0644]
core/runtime/platform/wasi/wasi_defs.onyx [new file with mode: 0644]
core/runtime/platform/wasi/wasi_fs.onyx [new file with mode: 0644]
core/runtime/wasi.onyx [deleted file]
core/std.onyx
core/threads/thread.onyx
core/time/date.onyx
core/time/time.onyx
core/wasi/README.md [deleted file]
core/wasi/clock.onyx [deleted file]
core/wasi/env.onyx [deleted file]
core/wasi/wasi.onyx [deleted file]
core/wasi/wasi_fs.onyx [deleted file]
docs/ideas/platform_layer.md [new file with mode: 0644]

index 42355ee3ec38f8bebcc51388cf1689fcb94384cc..4aeefbc9066cb03c01458f6c38d66e11dfce976c 100644 (file)
@@ -53,7 +53,7 @@ __flush_stdio :: () {
 
     ^stdio.print_stream
         |> io.buffer_stream_to_str()
-        |> runtime.__output_string();
+        |> runtime.platform.__output_string();
 
     ^stdio.print_stream |> io.stream_flush();
 }
@@ -92,17 +92,17 @@ printf :: (format: str, va: ..any) {
     print(conv.format_va(buffer, format, va, .{null, flush}));
 }
 
-#if #defined(runtime.__output_error) {
+#if #defined(runtime.platform.__output_error) {
     //
     // Prints to standard error, if available.
     eprintf :: (format: str, va: ..any) -> str {
         flush :: (_, to_output) => {
-            runtime.__output_error(to_output);
+            runtime.platform.__output_error(to_output);
             return true;
         }
 
         buffer: [1024] u8;
-        runtime.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
+        runtime.platform.__output_error(conv.format_va(buffer, format, va, .{null, flush}));
     }
 }
 
@@ -141,9 +141,9 @@ __byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
         temp[1] = map_to_ascii(val & 15);
         temp[2] = #char " ";
 
-        runtime.__output_string(~~temp);
+        runtime.platform.__output_string(~~temp);
 
-        if i % bytes_per_line == (bytes_per_line - 1) do runtime.__output_string("\n");
+        if i % bytes_per_line == (bytes_per_line - 1) do runtime.platform.__output_string("\n");
     }
 
 
@@ -180,7 +180,7 @@ __byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
 #local stdio_vtable := io.Stream_Vtable.{
     read = (_: ^io.Stream, buffer: [] u8) -> (io.Error, u32) {
         __flush_stdio();
-        bytes_read := runtime.__read_from_input(buffer);
+        bytes_read := runtime.platform.__read_from_input(buffer);
         if bytes_read == 0 do return .ReadPending, 0;
         if bytes_read <  0 do return .EOF, 0;
 
@@ -190,7 +190,7 @@ __byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) {
     read_byte = (_: ^io.Stream) -> (io.Error, u8) {
         __flush_stdio();
         buf: [1] u8;
-        bytes_read := runtime.__read_from_input(buf);
+        bytes_read := runtime.platform.__read_from_input(buf);
         if bytes_read <= 0 do return .EOF, 0;
 
         return .None, buf[0];
index 51d4d71978975d3d98ba05a6e243fa2ccca2e0f9..1b2569b4bf90a6dedccccf6ac48f8635f01b5d20 100644 (file)
@@ -1,5 +1,9 @@
 package core.net
 
+#if !runtime.platform.Supports_Networking {
+    #error "Cannot include this file. Platform not supported.";
+}
+
 use core
 
 Socket :: struct {
@@ -281,6 +285,11 @@ network_to_host :: #match #local {}
     }
 };
 
+//
+// This will be moved into the platform layer later.
+// I think this is most everything you would need, but
+// I need to see how WASI does sockets to see if this
+// makes sense as an abstraction.
 #foreign "onyx_runtime" {
     #package __net_create_socket :: (out_handle: ^Socket.Handle, domain: SocketDomain, type: SocketType) -> SocketError ---
     #package __net_close_socket  :: (handle: Socket.Handle)               -> void ---
index 696501b92edfb60149a3a79f6819e5528d8e6f66..98479904758ae0ce378b62e770080a34ac4090fb 100644 (file)
@@ -1,5 +1,9 @@
 package core.net
 
+#if !runtime.platform.Supports_Networking {
+    #error "Cannot include this file. Platform not supported.";
+}
+
 use core {sync, thread, array, memory, alloc, os, iter}
 
 #if !runtime.Multi_Threading_Enabled {
diff --git a/core/onyx/fs.onyx b/core/onyx/fs.onyx
deleted file mode 100644 (file)
index 7e43179..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-package runtime.fs
-
-use package core
-
-FileData :: #distinct i64
-DirectoryData :: #distinct u64
-
-__file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError) {
-    handle: FileData;
-    error := __file_open_impl(path, mode, ^handle);
-
-    return handle, error;
-}
-
-#foreign "onyx_runtime" {
-    __file_open_impl :: (path: str, mode: os.OpenMode, out_handle: ^FileData) -> os.FileError ---
-
-    __file_close :: (fd: FileData) -> os.FileError ---
-    __file_stat   :: (path: str, stat: ^os.FileStat) -> bool ---
-    __file_exists :: (path: str) -> bool ---
-    __file_remove :: (path: str) -> bool ---
-    __file_rename :: (old_path: str, new_path: str) -> bool ---
-
-    __file_seek  :: (handle: FileData, to: i32, whence: io.SeekFrom) -> i32 ---
-    __file_tell  :: (handle: FileData) -> u32 ---
-    __file_read  :: (handle: FileData, output_buffer: [] u8, bytes_read: ^u64) -> io.Error ---
-    __file_write :: (handle: FileData, input_buffer: [] u8, bytes_wrote: ^u64) -> io.Error ---
-    __file_flush :: (handle: FileData) -> io.Error ---
-    __file_size  :: (handle: FileData) -> u32 ---
-
-    __dir_open   :: (path: str, dir: ^DirectoryData) -> bool ---
-    __dir_close  :: (dir: DirectoryData) -> void ---
-    __dir_read   :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool ---
-    __dir_create :: (path: str) -> bool ---
-    __dir_remove :: (path: str) -> bool ---
-
-
-    __enable_non_blocking_stdin :: () -> void ---
-}
-
-__file_stream_vtable := io.Stream_Vtable.{
-    seek = (use fs: ^os.File, to: i32, whence: io.SeekFrom) -> io.Error {
-        now := __file_seek(data, to, whence);
-        return (.None) if now >= 0 else .BadFile;
-    },
-
-    tell = (use fs: ^os.File) -> (io.Error, u32) {
-        return .None, __file_tell(data);
-    },
-
-    read = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
-        bytes_read: u64;
-        error := __file_read(data, buffer, ^bytes_read);
-        return error, ~~bytes_read;
-    },
-
-    read_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
-        __file_seek(data, at, .Start);
-        bytes_read: u64;
-        error := __file_read(data, buffer, ^bytes_read);
-        return error, ~~bytes_read;
-    },
-
-    read_byte = (use fs: ^os.File) -> (io.Error, u8) {
-        byte: u8;
-        error := __file_read(data, ~~ cast([1] u8) ^byte, null);
-        return error, byte;
-    },
-
-    write = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
-        bytes_wrote: u64;
-        error := __file_write(data, buffer, ^bytes_wrote);
-        return error, ~~bytes_wrote;
-    },
-
-    write_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
-        __file_seek(data, at, .Start);
-        bytes_wrote: u64;
-        error := __file_write(data, buffer, ^bytes_wrote);
-        return error, ~~bytes_wrote;
-    },
-
-    write_byte = (use fs: ^os.File, byte: u8) -> io.Error {
-        b := byte;
-        bytes_wrote: u64;
-        error := __file_write(data, .{ ^b, 1 }, ^bytes_wrote);
-        return error;
-    },
-
-    close = (use fs: ^os.File) -> io.Error {
-        __file_close(data);
-        return .None;
-    },
-
-    flush = (use fs: ^os.File) -> io.Error {
-        return __file_flush(data);
-    },
-
-    size = (use fs: ^os.File) -> i32 {
-        return __file_size(data);
-    },
-};
-
index 4ec089424cb2772db1e06c31c95b37faae1c5c20..a8aaeeac0c01960467253ea4f55a19f4fbe9ad5f 100644 (file)
@@ -1,6 +1,11 @@
 package core.os
 
-use runtime {fs}
+#if !runtime.platform.Supports_Directories {
+    #error "Cannot include this file. Platform not supported.";
+}
+
+use core {string}
+#local fs :: runtime.platform
 
 Directory :: fs.DirectoryData;
 
@@ -31,3 +36,52 @@ dir_create :: fs.__dir_create
 dir_exists :: fs.__file_exists
 dir_remove :: fs.__dir_remove
 dir_rename :: fs.__file_rename
+
+list_directory :: (path: str) -> Iterator(DirectoryEntry) {
+    Context :: struct {
+        dir: Directory;
+        opened := false;
+    }
+
+    next :: (use c: ^Context) -> (DirectoryEntry, bool) {
+        if !opened do return .{}, false;
+
+        entry: DirectoryEntry;
+        if !dir_read(dir, ^entry) {
+            return .{}, false;
+        }
+
+        return entry, true;
+    }
+
+    close :: (use c: ^Context) {
+        dir_close(dir);
+        cfree(c);
+    }
+
+    c := new(Context);
+    if dir, success := dir_open(path); success {
+        c.dir = dir;
+        c.opened = true;
+    }
+
+    return .{ c, next, close };
+}
+
+remove_directory :: (path: str) -> bool {
+    // This is quite a bit of space, and could result in a stack overrun
+    // if the directory being deleted has a lot of descendents.
+    full_path_buffer: [512] u8;
+
+    for list_directory(path) {
+        full_path := string.concat(full_path_buffer, path, "/", it->name());
+
+        if it.type == .Directory {
+            if !remove_directory(full_path) do return false;
+        } else {
+            remove_file(full_path);
+        }
+    }
+
+    return dir_remove(path);    
+}
index 277f99df5e92e0291db410339651d49a18e6c91e..8d663c748456ac1b312e48149eb5336e69058b40 100644 (file)
@@ -1,7 +1,12 @@
 package core.os
 
+#if !runtime.platform.Supports_Files {
+    #error "Cannot include this file. Platform not supported.";
+}
+
 use core
-use runtime {fs}
+#local fs :: package runtime.platform
+
 
 FileError :: enum {
     None       :: 0x00;
index 171c193e227ff0904efd16851e10f0c065380f8a..03c183e1894c7c7edb2ca525237d717f357a7f4d 100644 (file)
@@ -1,69 +1,21 @@
 package core.os
 
-#if runtime.runtime != .Wasi 
-    && runtime.runtime != .Onyx {
-    #error "The os library is currently only available on the WASI runtime, and should only be included if that is the chosen runtime."
-}
-
 use core {string}
 
-list_directory :: (path: str) -> Iterator(DirectoryEntry) {
-    Context :: struct {
-        dir: Directory;
-        opened := false;
-    }
-
-    next :: (use c: ^Context) -> (DirectoryEntry, bool) {
-        if !opened do return .{}, false;
-
-        entry: DirectoryEntry;
-        if !dir_read(dir, ^entry) {
-            return .{}, false;
-        }
-
-        return entry, true;
-    }
-
-    close :: (use c: ^Context) {
-        dir_close(dir);
-        cfree(c);
-    }
-
-    c := new(Context);
-    if dir, success := dir_open(path); success {
-        c.dir = dir;
-        c.opened = true;
-    }
-
-    return .{ c, next, close };
-}
-
-remove_directory :: (path: str) -> bool {
-    // This is quite a bit of space, and could result in a stack overrun
-    // if the directory being deleted has a lot of descendents.
-    full_path_buffer: [512] u8;
-
-    for list_directory(path) {
-        full_path := string.concat(full_path_buffer, path, "/", it->name());
-
-        if it.type == .Directory {
-            if !remove_directory(full_path) do return false;
-        } else {
-            remove_file(full_path);
-        }
-    }
-
-    return dir_remove(path);    
+#if !runtime.platform.Supports_Os {
+    #error "Cannot include this file. Platform not supported.";
 }
 
 exit :: (exitcode: i32) {
-    runtime.__exit(exitcode);
+    runtime.platform.__exit(exitcode);
 }
 
-#if #defined(runtime.__sleep) {
-    sleep :: runtime.__sleep
+#if #defined(runtime.platform.__sleep) {
+    sleep :: runtime.platform.__sleep
 }
 
-#if #defined(runtime.__time) {
-    time :: runtime.__time
+#if #defined(runtime.platform.__time) {
+    time :: runtime.platform.__time
+} else {
+    time :: () => u64.{0};
 }
index 08ed47852e299fd42a67639d640cdae054b50086..bc511ee8dce9c85276f96636fb4d4d337c5078ff 100644 (file)
@@ -1,16 +1,23 @@
 package core.os
 
-#if runtime.runtime != .Onyx {
-    #error "This file can only be included in the 'onyx' runtime, because Wasi has not defined how to spawn and manage processes.";
+#if !runtime.platform.Supports_Processes {
+    #error "Cannot include this file. Platform not supported.";
 }
 
 use core {io}
+use runtime.platform {
+    __process_spawn,
+    __process_destroy,
+    __process_kill,
+    __process_read,
+    __process_write,
+    __process_wait,
+    ProcessData
+}
 
 Process :: struct {
-    Handle :: #distinct i64;
-
     use stream: io.Stream;
-    process_handle: Handle;
+    process_handle: ProcessData;
 }
 
 process_spawn :: (path: str, args: [] str, non_blocking_io := false, starting_directory := "") -> Process {
@@ -70,19 +77,10 @@ process_destroy :: (use p: ^Process) => {
     }
 }
 
-#local ProcessResult :: enum {
+ProcessResult :: enum {
     Success     :: 0x00;
     FailedToRun :: 0x01;
     Error       :: 0x02;
     InternalErr :: 0x03;
 }
 
-#foreign "onyx_runtime" {
-    __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> Process.Handle ---
-    __process_read    :: (handle: Process.Handle, buffer: [] u8) -> i32 ---
-    __process_write   :: (handle: Process.Handle, buffer: [] u8) -> i32 ---
-    __process_kill    :: (handle: Process.Handle) -> bool ---
-    __process_wait    :: (handle: Process.Handle) -> ProcessResult ---
-    __process_destroy :: (handle: Process.Handle) -> void ---
-}
-
index bee7072ed67b84ea903ec6c6227145512ab48b5e..759973b15424739095a37dc9cc82b1e11ac0e241 100644 (file)
@@ -2,6 +2,13 @@ package runtime
 
 use core
 use core.intrinsics.onyx { __initialize }
+use platform { __output_string }
+
+//
+// Export the __start function from the platform layer.
+// Every platform should define this, even if is it just '() {}'.
+#export "_start" platform.__start
+
 
 // The default assert handler. This assumes that __output_string
 // and __exit are defined in the 'runtime' package.
@@ -83,4 +90,4 @@ __thread_initialize :: () {
         raw_free(alloc.heap_allocator, __tls_base);
         core.thread.__exited(id);
     }
-}
+}
\ No newline at end of file
diff --git a/core/runtime/js.onyx b/core/runtime/js.onyx
deleted file mode 100644 (file)
index f13a5e7..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package runtime
-
-#load "core/runtime/common"
-
-use core
-
-__output_string   :: (s: str)      -> u32  #foreign "host" "print_str" ---
-__exit            :: (status: i32) -> void #foreign "host" "exit" ---
-__read_from_input :: (buf: [] u8)  -> u32 do return 0;
-
-// The builtin _start proc.
-// Sets up everything needed for execution.
-#export "_start" () {
-    __runtime_initialize();
-    context.thread_id = 0;
-
-    #if (typeof (package main).main) == #type () -> void {
-        (package main).main();
-
-    } else {
-        args: [] cstr = .{ null, 0 };
-        (package main).main(args);
-    }
-
-    __flush_stdio();
-}
-
-#if Multi_Threading_Enabled {
-    __spawn_thread :: (id: i32, tls_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "host" "spawn_thread" ---
-    __kill_thread  :: (id: i32) -> i32 #foreign "host" "kill_thread" ---
-
-    #export "_thread_start" _thread_start
-    #export "_thread_exit"  _thread_exit
-}
diff --git a/core/runtime/onyx_run.onyx b/core/runtime/onyx_run.onyx
deleted file mode 100644 (file)
index 3a827aa..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-package runtime
-
-#load "core/runtime/common"
-
-use core
-
-#local {
-    __stdout: os.File;
-    __stderr: os.File;
-    __stdin:  os.File;
-}
-
-__output_string :: (s: str) -> u32 {
-    err, wrote := io.stream_write(^__stdout, s);
-    return wrote;
-}
-
-__output_error :: (s: str) -> u32 {
-    err, wrote := io.stream_write(^__stderr, s);
-    return wrote;
-}
-
-__read_from_input :: (buffer: [] u8) -> i32 {
-    err, read := io.stream_read(^__stdin, buffer);
-    if err == .ReadPending do return 0;
-    if err != .None do return -1;
-    return read;
-}
-
-#library "onyx_runtime"
-
-#foreign "onyx_runtime" {
-    __file_get_standard :: (fd: i32, out: ^fs.FileData) -> bool ---
-
-    __args_get       :: (argv: ^^u8, arg_buf: ^u8) -> void ---
-    __args_sizes_get :: (argc: ^i32, arg_buf_size: ^i32) -> void ---
-
-    __exit :: (status: i32) -> void ---
-
-    __sleep :: (milliseconds: i32) -> void ---
-
-    __time :: () -> u64 ---
-}
-
-#export "_start" () {
-    fd: fs.FileData;
-    __file_get_standard(1, ^fd);
-    __stdout = .{
-        .{ ^fs.__file_stream_vtable },
-        fd
-    };
-    
-    __file_get_standard(2, ^fd);
-    __stderr = .{
-        .{ ^fs.__file_stream_vtable },
-        fd
-    };
-
-    __file_get_standard(0, ^fd);
-    __stdin = .{
-        .{ ^fs.__file_stream_vtable, .Block_On_Read },
-        fd
-    };
-
-    __runtime_initialize();
-    context.thread_id = 0;
-
-    #if (typeof (package main).main) == #type () -> void {
-        (package main).main();
-
-    } else {
-        args : [] cstr;
-        argv_buf_size : i32;
-        __args_sizes_get(^args.count, ^argv_buf_size);
-
-        args = memory.make_slice(cstr, args.count);
-        argv_buf := cast(cstr) calloc(argv_buf_size);
-        __args_get(args.data, argv_buf);
-
-        (package main).main(args);
-    }
-
-    __flush_stdio();
-}
-
-#if Multi_Threading_Enabled {
-    __spawn_thread :: (id: i32, tls_base: rawptr, stack_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "onyx_runtime" "__spawn_thread" ---
-    __kill_thread  :: (id: i32) -> i32 #foreign "onyx_runtime" "__kill_thread" ---
-
-    #export "_thread_start" _thread_start
-    #export "_thread_exit"  _thread_exit
-}
diff --git a/core/runtime/platform/js/platform.onyx b/core/runtime/platform/js/platform.onyx
new file mode 100644 (file)
index 0000000..aa304e2
--- /dev/null
@@ -0,0 +1,48 @@
+package runtime.platform
+
+use core
+use runtime {
+    __runtime_initialize,
+    Multi_Threading_Enabled,
+    _thread_start,
+    _thread_exit
+}
+
+// Platform supports
+Supports_Files :: false
+Supports_Directories :: false
+Supports_Os :: false
+Supports_Processes :: false
+Supports_Time :: false
+Supports_Networking :: false
+Supports_Type_Info :: true
+Supports_Threads :: true
+
+__output_string   :: (s: str)      -> u32  #foreign "host" "print_str" ---
+__output_error    :: (s: str)      -> u32  #foreign "host" "print_str" ---
+__exit            :: (status: i32) -> void #foreign "host" "exit" ---
+__read_from_input :: (buf: [] u8)  -> u32 do return 0;
+
+// Sets up everything needed for execution.
+__start :: () {
+    __runtime_initialize();
+    context.thread_id = 0;
+
+    #if (typeof (package main).main) == #type () -> void {
+        (package main).main();
+
+    } else {
+        args: [] cstr = .{ null, 0 };
+        (package main).main(args);
+    }
+
+    __flush_stdio();
+}
+
+#if Multi_Threading_Enabled {
+    __spawn_thread :: (id: i32, tls_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "host" "spawn_thread" ---
+    __kill_thread  :: (id: i32) -> i32 #foreign "host" "kill_thread" ---
+
+    #export "_thread_start" _thread_start
+    #export "_thread_exit"  _thread_exit
+}
diff --git a/core/runtime/platform/onyx/fs.onyx b/core/runtime/platform/onyx/fs.onyx
new file mode 100644 (file)
index 0000000..f122de6
--- /dev/null
@@ -0,0 +1,117 @@
+package runtime.platform
+
+use core
+
+FileData :: #distinct i64
+DirectoryData :: #distinct u64
+
+#local {
+    #foreign "onyx_runtime" {
+        __file_open_impl :: (path: str, mode: os.OpenMode, out_handle: ^FileData) -> os.FileError ---
+
+        __file_close :: (fd: FileData) -> os.FileError ---
+        __file_stat   :: (path: str, stat: ^os.FileStat) -> bool ---
+        __file_exists :: (path: str) -> bool ---
+        __file_remove :: (path: str) -> bool ---
+        __file_rename :: (old_path: str, new_path: str) -> bool ---
+
+        __file_seek  :: (handle: FileData, to: i32, whence: io.SeekFrom) -> i32 ---
+        __file_tell  :: (handle: FileData) -> u32 ---
+        __file_read  :: (handle: FileData, output_buffer: [] u8, bytes_read: ^u64) -> io.Error ---
+        __file_write :: (handle: FileData, input_buffer: [] u8, bytes_wrote: ^u64) -> io.Error ---
+        __file_flush :: (handle: FileData) -> io.Error ---
+        __file_size  :: (handle: FileData) -> u32 ---
+
+        __dir_open   :: (path: str, dir: ^DirectoryData) -> bool ---
+        __dir_close  :: (dir: DirectoryData) -> void ---
+        __dir_read   :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool ---
+        __dir_create :: (path: str) -> bool ---
+        __dir_remove :: (path: str) -> bool ---
+
+
+        __enable_non_blocking_stdin :: () -> void ---
+    }
+}
+
+__file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError) {
+    handle: FileData;
+    error := __file_open_impl(path, mode, ^handle);
+
+    return handle, error;
+}
+
+// Export symbols from #foreign table
+__file_close   :: __file_close
+__file_stat    :: __file_stat
+__file_exists  :: __file_exists
+__file_remove  :: __file_remove
+__file_rename  :: __file_rename
+__dir_open     :: __dir_open
+__dir_close    :: __dir_close
+__dir_read     :: __dir_read
+__dir_create   :: __dir_create
+__dir_remove   :: __dir_remove
+
+__file_stream_vtable := io.Stream_Vtable.{
+    seek = (use fs: ^os.File, to: i32, whence: io.SeekFrom) -> io.Error {
+        now := __file_seek(data, to, whence);
+        return (.None) if now >= 0 else .BadFile;
+    },
+
+    tell = (use fs: ^os.File) -> (io.Error, u32) {
+        return .None, __file_tell(data);
+    },
+
+    read = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
+        bytes_read: u64;
+        error := __file_read(data, buffer, ^bytes_read);
+        return error, ~~bytes_read;
+    },
+
+    read_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
+        __file_seek(data, at, .Start);
+        bytes_read: u64;
+        error := __file_read(data, buffer, ^bytes_read);
+        return error, ~~bytes_read;
+    },
+
+    read_byte = (use fs: ^os.File) -> (io.Error, u8) {
+        byte: u8;
+        error := __file_read(data, ~~ cast([1] u8) ^byte, null);
+        return error, byte;
+    },
+
+    write = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
+        bytes_wrote: u64;
+        error := __file_write(data, buffer, ^bytes_wrote);
+        return error, ~~bytes_wrote;
+    },
+
+    write_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
+        __file_seek(data, at, .Start);
+        bytes_wrote: u64;
+        error := __file_write(data, buffer, ^bytes_wrote);
+        return error, ~~bytes_wrote;
+    },
+
+    write_byte = (use fs: ^os.File, byte: u8) -> io.Error {
+        b := byte;
+        bytes_wrote: u64;
+        error := __file_write(data, .{ ^b, 1 }, ^bytes_wrote);
+        return error;
+    },
+
+    close = (use fs: ^os.File) -> io.Error {
+        __file_close(data);
+        return .None;
+    },
+
+    flush = (use fs: ^os.File) -> io.Error {
+        return __file_flush(data);
+    },
+
+    size = (use fs: ^os.File) -> i32 {
+        return __file_size(data);
+    },
+};
+
diff --git a/core/runtime/platform/onyx/platform.onyx b/core/runtime/platform/onyx/platform.onyx
new file mode 100644 (file)
index 0000000..f30c47b
--- /dev/null
@@ -0,0 +1,128 @@
+package runtime.platform
+
+use core
+use runtime {
+    __runtime_initialize,
+    Multi_Threading_Enabled,
+    _thread_start,
+    _thread_exit
+}
+
+#load "./fs"
+
+
+// Platform supports
+Supports_Files :: true
+Supports_Directories :: true
+Supports_Os :: true
+Supports_Processes :: true
+Supports_Time :: true
+Supports_Networking :: true
+Supports_Type_Info :: true
+Supports_Threads :: true
+
+
+#library "onyx_runtime"
+
+#local {
+    __stdout: os.File;
+    __stderr: os.File;
+    __stdin:  os.File;
+}
+
+__output_string :: (s: str) -> u32 {
+    err, wrote := io.stream_write(^__stdout, s);
+    return wrote;
+}
+
+__output_error :: (s: str) -> u32 {
+    err, wrote := io.stream_write(^__stderr, s);
+    return wrote;
+}
+
+__read_from_input :: (buffer: [] u8) -> i32 {
+    err, read := io.stream_read(^__stdin, buffer);
+    if err == .ReadPending do return 0;
+    if err != .None do return -1;
+    return read;
+}
+
+
+ProcessData :: #distinct u64
+
+#foreign "onyx_runtime" {
+    // Arguments
+    __args_get       :: (argv: ^^u8, arg_buf: ^u8) -> void ---
+    __args_sizes_get :: (argc: ^i32, arg_buf_size: ^i32) -> void ---
+
+    // OS
+    __exit :: (status: i32) -> void ---
+    __sleep :: (milliseconds: i32) -> void ---
+
+    // Time and sleep
+    __time :: () -> u64 ---
+    __time_localtime :: (time: u64, tm: ^core.time.Timestamp) -> void ---
+    __time_gmtime    :: (time: u64, tm: ^core.time.Timestamp) -> void ---
+    __time_mktime    :: (tm: ^time.Timestamp) -> i64 ---
+    __time_strftime  :: (buf: [] u8, format: cstr, tm: ^time.Timestamp) -> u32 ---
+
+    // Processes
+    __process_spawn   :: (path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> ProcessData ---
+    __process_read    :: (handle: ProcessData, buffer: [] u8) -> i32 ---
+    __process_write   :: (handle: ProcessData, buffer: [] u8) -> i32 ---
+    __process_kill    :: (handle: ProcessData) -> bool ---
+    __process_wait    :: (handle: ProcessData) -> os.ProcessResult ---
+    __process_destroy :: (handle: ProcessData) -> void ---
+
+    // Misc
+    __file_get_standard :: (fd: i32, out: ^FileData) -> bool ---
+}
+
+__start :: () {
+    fd: FileData;
+    __file_get_standard(1, ^fd);
+    __stdout = .{
+        .{ ^__file_stream_vtable },
+        fd
+    };
+    
+    __file_get_standard(2, ^fd);
+    __stderr = .{
+        .{ ^__file_stream_vtable },
+        fd
+    };
+
+    __file_get_standard(0, ^fd);
+    __stdin = .{
+        .{ ^__file_stream_vtable, .Block_On_Read },
+        fd
+    };
+
+    __runtime_initialize();
+    context.thread_id = 0;
+
+    #if (typeof (package main).main) == #type () -> void {
+        (package main).main();
+
+    } else {
+        args : [] cstr;
+        argv_buf_size : i32;
+        __args_sizes_get(^args.count, ^argv_buf_size);
+
+        args = memory.make_slice(cstr, args.count);
+        argv_buf := cast(cstr) calloc(argv_buf_size);
+        __args_get(args.data, argv_buf);
+
+        (package main).main(args);
+    }
+
+    __flush_stdio();
+}
+
+#if Multi_Threading_Enabled {
+    __spawn_thread :: (id: i32, tls_base: rawptr, stack_base: rawptr, func: (data: rawptr) -> void, data: rawptr) -> bool #foreign "onyx_runtime" "__spawn_thread" ---
+    __kill_thread  :: (id: i32) -> i32 #foreign "onyx_runtime" "__kill_thread" ---
+
+    #export "_thread_start" _thread_start
+    #export "_thread_exit"  _thread_exit
+}
diff --git a/core/runtime/platform/wasi/clock.onyx b/core/runtime/platform/wasi/clock.onyx
new file mode 100644 (file)
index 0000000..468327f
--- /dev/null
@@ -0,0 +1,47 @@
+package core.clock
+
+#local runtime :: package runtime
+#if runtime.runtime != .Wasi 
+    && runtime.runtime != .Onyx {
+    #error "'core.clock' is only available with the 'wasi' or 'onyx' runtimes.";
+}
+
+use package wasi
+
+time :: () -> u64 {
+    output_time: Timestamp;
+    clock_time_get(.Realtime, 500000, ^output_time);
+    return ~~(output_time / 1000000);
+}
+
+time_ns :: () -> u64 {
+    output_time: Timestamp;
+    clock_time_get(.Realtime, 1, ^output_time);
+    return ~~output_time;
+}
+
+sleep :: #match {
+    (seconds: u64)      { sleep(nanoseconds=seconds * 1000000000); },
+    (milliseconds: u64) { sleep(nanoseconds=milliseconds * 1000000); },
+
+    (nanoseconds: u64) {
+        tagged: SubscriptionTagged;
+        tagged.tag = .Clock;
+        tagged.clock = .{
+            id = .Realtime,
+            timeout = cast(u64) nanoseconds,
+            precision = 1,
+            flags = ~~0,
+        };
+
+        subscription := Subscription.{
+            userdata = 0,
+            u = tagged,
+        };
+
+        event: Event;
+        number_of_events: u32;
+
+        error_code := poll_oneoff(^subscription, ^event, 1, ^number_of_events);
+    }
+}
diff --git a/core/runtime/platform/wasi/env.onyx b/core/runtime/platform/wasi/env.onyx
new file mode 100644 (file)
index 0000000..7cd5b3b
--- /dev/null
@@ -0,0 +1,57 @@
+package core.env
+
+#local runtime :: package runtime
+#if runtime.runtime != .Wasi 
+    && runtime.runtime != .Onyx {
+    #error "'core.env' is only available with the 'wasi' and 'onyx' runtimes.";
+}
+
+
+use package wasi { environ_get, environ_sizes_get, Size }
+#package map    :: package core.map
+#package memory :: package core.memory
+#package string :: package core.string
+
+Environment :: struct {
+    vars             : Map(str, str);
+
+    buffer           : [] u8;
+    buffer_allocator : Allocator;
+}
+
+get_env :: (allocator := context.allocator) -> Environment {
+    env_count, env_buf_size : Size;
+    environ_sizes_get(^env_count, ^env_buf_size);
+
+    env_var := memory.make_slice(cstr, env_count, allocator=allocator);
+    env_buf := memory.make_slice(u8, env_buf_size, allocator=allocator);
+
+    environ_get(env_var.data, env_buf.data);
+
+    while i := cast(i32) (env_var.count - 1); i >= 0 {
+        defer i -= 1;
+
+        env_var[i] = cast(cstr) (cast(^u32) env_var.data)[i];
+    }
+
+    env_map := map.make(str, str, "");
+    for env: env_var {
+        s := string.from_cstr(env);
+        var := string.read_until(^s, #char "=");
+        map.put(^env_map, var, string.advance(s, 1));
+    }
+
+    raw_free(allocator, env_var.data);
+
+    return .{
+        vars             = env_map,
+        buffer           = env_buf,
+        buffer_allocator = allocator,
+    };
+}
+
+free_env :: (use env: ^Environment) {
+    map.free(^vars);
+
+    raw_free(buffer_allocator, buffer.data);
+}
diff --git a/core/runtime/platform/wasi/platform.onyx b/core/runtime/platform/wasi/platform.onyx
new file mode 100644 (file)
index 0000000..4e01eb0
--- /dev/null
@@ -0,0 +1,112 @@
+package runtime.platform
+
+#load "./wasi_defs"
+#load "./wasi_fs"
+
+use core
+use wasi {
+    IOVec, SubscriptionTagged, Subscription, Event, Size,
+    poll_oneoff, fd_write, fd_datasync, fd_read,
+    args_get, args_sizes_get,
+    proc_exit
+}
+use runtime {
+    __runtime_initialize,
+    Multi_Threading_Enabled,
+}
+
+
+// Platform supports
+Supports_Files :: true
+Supports_Directories :: true
+Supports_Os :: true
+Supports_Processes :: false
+Supports_Time :: false
+Supports_Networking :: false
+Supports_Type_Info :: true
+Supports_Threads :: false
+
+
+
+__output_string :: (s: str) -> u32 {
+    STDOUT_FILENO :: 1
+
+    vec := IOVec.{ buf = cast(u32) s.data, len = s.count };
+    tmp : Size;
+    fd_write(STDOUT_FILENO, ^vec, 1, ^tmp);
+    fd_datasync(STDOUT_FILENO);
+    return tmp;
+}
+
+__exit :: (status: i32) do proc_exit(status);
+
+__read_from_input :: (buffer: [] u8) -> i32 {
+    STDIN_FILENO :: 0
+
+    vec := IOVec.{ buf = cast(i32) buffer.data, len = buffer.count };
+    read: Size;
+    error := fd_read(STDIN_FILENO, ^vec, 1, ^read);
+    if error != .Success do return -1;
+
+    return read;
+}
+
+__sleep :: (milliseconds: u32) {
+    tagged: SubscriptionTagged;
+    tagged.tag = .Clock;
+    tagged.clock = .{
+        id = .Realtime,
+        timeout = cast(u64) milliseconds * 1000000,
+        precision = 1,
+        flags = ~~0,
+    };
+
+    subscription := Subscription.{
+        userdata = 0,
+        u = tagged,
+    };
+
+    event: Event;
+    number_of_events: u32;
+
+    error_code := poll_oneoff(^subscription, ^event, 1, ^number_of_events);
+}
+
+
+// Sets up everything needed for execution.
+__start :: () {
+    __runtime_initialize();
+    context.thread_id = 0;
+
+    #if (typeof (package main).main) == #type () -> void {
+        (package main).main();
+
+    } else {
+        args : [] cstr;
+        argv_buf_size : Size;
+        args_sizes_get(^args.count, ^argv_buf_size);
+
+        args = memory.make_slice(cstr, args.count);
+        argv_buf := cast(cstr) calloc(argv_buf_size);
+        args_get(args.data, argv_buf);
+
+
+        // This post processing of the argv array needs to happen if the target is using
+        // 32-bit pointers, instead of 64-bits. Right now, Onyx pointers take up 64-bits,
+        // but in most circumstances, only the lower 32-bits are used. When webassembly
+        // standardizes the 64-bit address space, it will be an easy conversion over.
+        // But for right now, WASI will give the argv array 32-bit pointers, instead of
+        // 64-bit pointers. This loops expands the 32-bit pointers into 64-bit pointers
+        // while not clobbering any of them.
+        while i := cast(i32) (args.count - 1); i >= 0 {
+            defer i -= 1;
+
+            args[i] = cast(cstr) (cast(^u32) args.data)[i];
+        }
+
+        (package main).main(args);
+    }
+
+    __flush_stdio();
+}
+
diff --git a/core/runtime/platform/wasi/wasi_defs.onyx b/core/runtime/platform/wasi/wasi_defs.onyx
new file mode 100644 (file)
index 0000000..7350563
--- /dev/null
@@ -0,0 +1,435 @@
+package wasi
+
+Size      :: #type u32;
+Filesize  :: #type u64;
+Timestamp :: #type u64;
+
+ClockID   :: enum (u32) {
+    Realtime         :: 0x00;
+    Monotonic        :: 0x01;
+    ProcessCPUTimeID :: 0x02;
+    ThreadCPUTimeID  :: 0x03;
+}
+
+Errno :: enum (u16) {
+    Success      :: 0x00;
+    TooBig       :: 0x01;
+    Access       :: 0x02;
+    AddrInUse    :: 0x03;
+    AddrNotAvail :: 0x04;
+    AFNoSupport  :: 0x05;
+    Again        :: 0x06;
+    Already      :: 0x07;
+    BadFile      :: 0x08;
+    BadMsg       :: 0x09;
+    Busy         :: 0x0a;
+    Canceled     :: 0x0b;
+    Child        :: 0x0c;
+    ConnAborted  :: 0x0d;
+    ConnRefused  :: 0x0e;
+    ConnReset    :: 0x0f;
+    DeadLock     :: 0x10;
+    DestAddrReq  :: 0x11;
+    Domain       :: 0x12;
+    DQUOT        :: 0x13;
+    Exist        :: 0x14;
+    Fault        :: 0x15;
+    FileTooBig   :: 0x16;
+    HostUnreach  :: 0x17;
+    IdentRemoved :: 0x18;
+    IllegalSeq   :: 0x19;
+    InProgress   :: 0x1a;
+    Interrupt    :: 0x1b;
+    Invalid      :: 0x1c;
+    IO           :: 0x1d;
+    IsConnection :: 0x1e;
+    IsDirectory  :: 0x1f;
+    Loop         :: 0x20;
+    MFile        :: 0x21;
+    MLink        :: 0x22;
+    MsgSize      :: 0x23;
+    MultiHop     :: 0x24;
+    NameTooLong  :: 0x25;
+    NetDown      :: 0x26;
+    NetReset     :: 0x27;
+    NetUnreach   :: 0x28;
+    NFile        :: 0x29;
+    NoBufs       :: 0x2a;
+    NoDev        :: 0x2b;
+    NoEntry      :: 0x2c;
+    NoExec       :: 0x2d;
+    NoLock       :: 0x2e;
+    NoLink       :: 0x2f;
+    NoMemory     :: 0x30;
+    NoMsg        :: 0x31;
+    NoProtoOpt   :: 0x32;
+    NoSpace      :: 0x33;
+    NoSys        :: 0x34;
+    NotConn      :: 0x35;
+    NotDir       :: 0x36;
+    NotEmpty     :: 0x37;
+    NotRecover   :: 0x38;
+    NotSock      :: 0x39;
+    NotSupported :: 0x3a;
+    NoTTY        :: 0x3b;
+    NXIO         :: 0x3c;
+    Overflow     :: 0x3d;
+    OwnerDead    :: 0x3e;
+    Permission   :: 0x3f;
+    Pipe         :: 0x40;
+    Protocol     :: 0x41;
+    ProtoNoSup   :: 0x42;
+    Prototype    :: 0x43;
+    Range        :: 0x44;
+    ReadonlyFS   :: 0x45;
+    SeekPipe     :: 0x46;
+    Search       :: 0x47;
+    Stale        :: 0x48;
+    Timedout     :: 0x49;
+    TextBusy     :: 0x4a;
+    XDev         :: 0x4b;
+
+    NotCapable   :: 0x4c;
+}
+
+Rights :: enum #flags (u64) {
+    DataSync;
+    Read;
+    Seek;
+    FdStatSetFlags;
+    Sync;
+    Tell;
+    Write;
+    Advise;
+    Allocate;
+    PathCreateDirectory;
+    PathCreateFile;
+    PathLinkSource;
+    PathLinkTarget;
+    PathOpen;
+    ReadDir;
+    PathReadlink;
+    PathRenameSource;
+    PathRenameTarget;
+    PathFilestatGet;
+    PathFilestateSetSize;
+    PathFilestateSetTimes;
+    FilestatGet;
+    FilestatSetSize;
+    FilestatSetTimes;
+    PathSymlink;
+    PathRemoveDirectory;
+    PathUnlinkFile;
+    PollFDReadWrite;
+    SockShutdown;
+}
+
+FileDescriptor :: #type i32;
+
+IOVec :: struct {
+    buf : u32; // actually a ^u8, but WASM is 32-bit at the moment;
+    len : u32;
+}
+
+CIOVec :: #type IOVec; // Constant IOVec
+
+FileDelta :: #type i64;
+
+Whence :: enum (u8) {
+    Set :: 0x00;
+    Cur :: 0x01;
+    End :: 0x02;
+}
+
+DirCookie  :: #type u64;
+DirNameLen :: #type u32;
+INode      :: #type u64;
+
+Filetype :: enum (u8) {
+    Unknown      :: 0x00;
+    BlockDevice  :: 0x01;
+    CharDevice   :: 0x02;
+    Directory    :: 0x03;
+    RegularFile  :: 0x04;
+    SocketDgram  :: 0x05;
+    SocketStream :: 0x06;
+    SymLink      :: 0x07;
+}
+
+DirEnt :: struct {
+    d_next   : DirCookie;
+    d_ino    : INode;
+    d_namlen : DirNameLen;
+    d_type   : Filetype;
+}
+
+Advice :: enum (u8) {
+    Normal     :: 0x00;
+    Sequential :: 0x01;
+    Random     :: 0x02;
+    WillNeed   :: 0x03;
+    DontNeed   :: 0x04;
+    NoReuse    :: 0x05;
+}
+
+FDFlags :: enum #flags (u16) {
+    Append;
+    DSync;
+    NonBlock;
+    RSync;
+    Sync;
+}
+
+FDStat :: struct {
+    fs_filetype          : Filetype;
+    fs_flags             : FDFlags;
+    fs_rights_base       : Rights;
+    fs_rights_inheriting : Rights;
+}
+
+Device :: #type u64;
+
+FSTFlags :: enum #flags (u16) {
+    ATIM;
+    ATIM_NOW;
+    MTIM;
+    MTIM_NOW;
+}
+
+LookupFlags :: enum #flags (u32) {
+    SymLinkFollow;
+}
+
+OFlags :: enum #flags (u16) {
+    Creat;
+    Directory;
+    FailIfExists;
+    Trunc;
+}
+
+LinkCount :: #type u64;
+
+FileStat :: struct {
+    dev      : Device;
+    ino      : INode;
+    filetype : Filetype;
+
+    // ANGER(Brendan Hansen): I give the worst documentation in
+    // the entire world award to WASI, whose best documentation
+    // is a C header file, that is itself incorrect. nlink does
+    // not exist in the current version of wasmtime, and should
+    // not be here, but maybe in the future? Ugh.
+    //
+    // STILL ANGERY(Brendan Hansen): Actually, nlink does exist
+    // in the current version of wasmtime; however its actually
+    // a 32-bit value, not 64-bit, which means the offsets were
+    // incorrect. I fixed it for now, but when wasmtime updates
+    // the size of 'LinkCount' needs to be changed.
+    //
+    // EVEN ANGRIER(Brendan Hansen): ACTUALLY, I realized today
+    // that it has changed (at least for Wasmer). There was not
+    // any mention of this on any release documents, but hey, I
+    // guess this can actually be 64-bits now.
+    //                                   - brendanfh 2020/12/05
+    //                                   - brendanfh 2020/12/07
+    //                                   - brendanfh 2021/07/10
+    nlink    : LinkCount;
+
+    size     : Filesize;
+    atim     : Timestamp;
+    mtim     : Timestamp;
+    ctim     : Timestamp;
+}
+
+Userdata :: #type u64;
+
+EventType :: enum (u8) {
+    Clock;
+    FDRead;
+    FDWrite;
+}
+
+EventRWFlags :: enum #flags (u16) {
+    ReadWriteHangUp;
+}
+
+EventFDReadWrite :: struct {
+    nbytes : Filesize;
+    flags  : EventRWFlags;
+}
+
+Event :: struct {
+    userdata     : Userdata;
+    error        : Errno;
+    type         : EventType;
+    fd_readwrite : EventFDReadWrite;
+}
+
+SubClockFlags :: enum #flags (u16) {
+    ClockAbsTime;
+}
+
+SubscriptionClock :: struct {
+    id        : ClockID;
+    timeout   : Timestamp;
+    precision : Timestamp;
+    flags     : SubClockFlags;
+}
+
+SubscriptionFDReadWrite :: struct {
+    file_descriptor : FileDescriptor;
+}
+
+SubscriptionTagged :: struct {
+    tag : EventType;
+
+    use u : struct #union {
+        clock    : SubscriptionClock;
+        fd_read  : SubscriptionFDReadWrite;
+        fd_write : SubscriptionFDReadWrite;
+    };
+}
+
+Subscription :: struct {
+    userdata : Userdata;
+
+    u : SubscriptionTagged;
+}
+
+ExitCode :: #type u32;
+Signal :: enum (u8) {
+    None;
+    Hup;
+    Int;
+    Quit;
+    Ill;
+    Trap;
+    Abrt;
+    Bus;
+    Fpe;
+    Kill;
+    USR1;
+    Segv;
+    USR2;
+    Pipe;
+    Alrm;
+    Term;
+    Chld;
+    Stop;
+    Tstp;
+    Ttin;
+    Urg;
+    Xcpu;
+    Xfsz;
+    Vtalrm;
+    Prof;
+    Winch;
+    Poll;
+    Pwr;
+    Sys;
+}
+
+RIFlags :: enum #flags (u16) {
+    RecvPeek;
+    RecvWaitAll;
+}
+
+ROFlags :: enum #flags (u16) {
+    RecvDataTruncated :: 1;
+}
+
+SIFlags :: enum #flags (u16) {
+    None;
+}
+
+SDFlags :: enum #flags (u16) {
+    RD;
+    WR;
+}
+
+PreopenType :: enum (u8) {
+    Dir :: 0x00;
+}
+
+PrestatDir :: struct {
+    pr_name_len : Size;
+}
+
+PrestatTagged :: struct {
+    tag : PreopenType;
+
+    u : struct #union {
+        dir : PrestatDir;
+    };
+}
+
+
+// FUNCTIONS
+args_get       :: (argv: ^^u8, argv_buf: ^u8) -> Errno #foreign "wasi_snapshot_preview1" "args_get"---
+args_sizes_get :: (argc: ^Size, argv_buf_size: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "args_sizes_get" ---
+
+environ_get       :: (environ: ^^u8, environ_buf: ^u8) -> Errno #foreign "wasi_snapshot_preview1" "environ_get" ---
+environ_sizes_get :: (environc: ^Size, environ_buf_size: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "environ_sizes_get" ---
+
+clock_res_get  :: (id: ClockID, resolution: ^Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_res_get" ---
+clock_time_get :: (id: ClockID, precision: Timestamp, time: ^Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_time_get" ---
+
+fd_advise             :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign "wasi_snapshot_preview1" "fd_advise" ---
+fd_allocate           :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_allocate" ---
+fd_close              :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_close" ---
+fd_datasync           :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_datasync" ---
+fd_fdstat_get         :: (fd: FileDescriptor, stat: ^FDStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_get" ---
+fd_fdstat_set_flags   :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_flags" ---
+fd_fdstat_set_rights  :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_rights" ---
+fd_filestat_get       :: (fd: FileDescriptor, buf: ^FileStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_get" ---
+fd_filestat_set_size  :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_size" ---
+fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_times" ---
+fd_pread              :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, offset: Filesize, nread: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pread" ---
+fd_prestat_get        :: (fd: FileDescriptor, buf: ^PrestatTagged) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_get" ---
+fd_prestat_dir_name   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_dir_name" ---
+fd_pwrite             :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, offset: Filesize, nwritten: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pwrite" ---
+fd_read               :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, nread: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_read" ---
+fd_readdir            :: (fd: FileDescriptor, buf: ^u8, buf_len: Size, cookie: DirCookie, bufused: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_readdir" ---
+fd_renumber           :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_renumber" ---
+fd_seek               :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: ^Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_seek" ---
+fd_sync               :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_sync" ---
+fd_tell               :: (fd: FileDescriptor, offset: ^Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_tell" ---
+fd_write              :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, nwritten: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_write" ---
+
+path_create_directory   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_create_directory" ---
+path_filestat_get       :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: ^FileStat) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_get" ---
+path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_set_times" ---
+
+path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_link" ---
+path_open :: (fd: FileDescriptor
+    , dirflags: LookupFlags
+    , path: str
+    , oflags: OFlags
+    , fs_rights_base: Rights
+    , fs_rights_inherting: Rights
+    , fdflags: FDFlags
+    , opened_fd: ^FileDescriptor
+    ) -> Errno
+     #foreign "wasi_snapshot_preview1" "path_open" ---
+
+path_readlink :: (fd: FileDescriptor, path: str, buf: ^u8, buf_len: Size, bufused: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "path_readlink" ---
+path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_remove_directory" ---
+path_rename  :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_rename" ---
+path_symlink :: (old_path: ^u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_symlink" ---
+path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_unlink_file" ---
+
+poll_oneoff :: (in: ^Subscription, out: ^Event, nsubscriptions: Size, nevents: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "poll_oneoff" ---
+
+proc_exit  :: (rval: ExitCode) -> void #foreign "wasi_snapshot_preview1" "proc_exit" ---
+proc_raise :: (sig: Signal) -> Errno #foreign "wasi_snapshot_preview1" "proc_raise" ---
+
+sched_yield :: () -> Errno #foreign "wasi_snapshot_preview1" "sched_yield" ---
+
+random_get :: (buf: ^u8, buf_len: Size) -> Errno #foreign "wasi_snapshot_preview1" "random_get" ---
+
+sock_recv     :: (fd: FileDescriptor, ri_data: ^IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: ^Size, ro_flags: ^ROFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_recv" ---
+sock_send     :: (fd: FileDescriptor, si_data: ^IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "sock_send" ---
+sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_shutdown" ---
+
+
+
diff --git a/core/runtime/platform/wasi/wasi_fs.onyx b/core/runtime/platform/wasi/wasi_fs.onyx
new file mode 100644 (file)
index 0000000..8e42728
--- /dev/null
@@ -0,0 +1,336 @@
+package runtime.platform
+
+#local runtime :: package runtime
+#if runtime.runtime != .Wasi {
+    #error "The file system library is currently only available on the WASI runtime, and should only be included if that is the chosen runtime."
+}
+
+use package core
+
+#local wasi :: package wasi
+use package wasi {
+    FileDescriptor,
+    FDFlags, OFlags, Rights,
+    LookupFlags, Errno,
+    IOVec, Size,
+    FileStat, Whence
+}
+
+FileData :: struct {
+    fd     : FileDescriptor = -1;
+
+    mode   : os.OpenMode = .Invalid;
+    rights : Rights   = ~~ 0;
+    flags  : FDFlags  = ~~ 0;
+}
+
+__file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError) {
+    // Requesting all of the rights because why not.
+    rights :=
+        Rights.DataSync
+        | Rights.Read
+        | Rights.Seek
+        | Rights.FdStatSetFlags
+        | Rights.Sync
+        | Rights.Tell
+        | Rights.Write
+        | Rights.Advise
+        | Rights.Allocate
+        | Rights.PathCreateDirectory
+        | Rights.PathCreateFile
+        | Rights.PathLinkSource
+        | Rights.PathLinkTarget
+        | Rights.PathOpen
+        | Rights.ReadDir
+        | Rights.PathReadlink
+        | Rights.PathRenameSource
+        | Rights.PathRenameTarget
+        | Rights.PathFilestatGet
+        | Rights.PathFilestateSetSize
+        | Rights.PathFilestateSetTimes
+        | Rights.FilestatGet
+        | Rights.FilestatSetSize
+        | Rights.FilestatSetTimes
+        | Rights.PathSymlink
+        | Rights.PathRemoveDirectory
+        | Rights.PathUnlinkFile
+        | Rights.PollFDReadWrite;
+
+    open_flags := cast(OFlags) 0;
+    fd_flags := FDFlags.Sync;
+
+    switch mode {
+        case .Write {
+            open_flags |= OFlags.Creat | OFlags.Trunc;
+            rights     |= Rights.Write | Rights.Seek | Rights.Tell;
+        }
+
+        case .Append {
+            open_flags |= OFlags.Creat;
+            rights     |= Rights.Write | Rights.Seek | Rights.Tell;
+            fd_flags   |= FDFlags.Append;
+        }
+
+        case .Read {
+            rights |= Rights.Read | Rights.Seek | Rights.Tell;
+        }
+    }
+
+    file := FileData.{ fd = -1 };
+    file.mode   = mode;
+    file.rights = rights;
+    file.flags  = fd_flags;
+
+    // Currently the directory's file descriptor appears to always be 3
+    // However, this is not necessarily correct, so also try a preopened directory
+    for DIR_FD: .[ 3, 4 ] {
+        if err := wasi.path_open(
+            DIR_FD,
+            .SymLinkFollow,
+            path,
+            open_flags,
+            rights,
+            rights,
+            fd_flags,
+            ^file.fd);
+            err == .Success {
+                return file, .None;
+        }
+    }
+
+    // @TODO // provide a better error code.
+    return file, .NotFound;
+}
+
+__file_close :: (file: FileData) -> os.FileError {
+    if wasi.fd_close(file.fd) != .Success {
+        return .BadFile;
+    }
+
+    return .None;
+}
+
+__file_stat :: (path: str, out: ^os.FileStat) -> bool {
+    fs: wasi.FileStat;
+
+    exists := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
+        if err == .Success {
+            exists = true;
+            out.size = ~~ fs.size;
+
+            switch fs.filetype {
+                case .RegularFile do out.type = .RegularFile;
+                case .Directory do out.type = .Directory;
+                case .SymLink do out.type = .SymLink;
+                case #default do out.type = .Unknown;
+            }
+        }
+    }
+
+    return exists;
+}
+
+__file_exists :: (path: str) -> bool {
+    fs: wasi.FileStat;
+
+    exists := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
+        if err == .Success do exists = true;
+    }
+
+    return exists;
+}
+
+__file_remove :: (path: str) -> bool {
+    removed := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_unlink_file(it, path);
+        if err == .Success do removed = true;
+    }
+
+    return removed;
+}
+
+__file_rename :: (old_path: str, new_path: str) -> bool {
+    renamed := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_rename(it, old_path, it, new_path);
+        if err == .Success do renamed = true;
+    }
+
+    return renamed;
+}
+
+__file_stream_vtable := io.Stream_Vtable.{
+    seek = (use fs: ^os.File, to: i32, whence: io.SeekFrom) -> io.Error {
+        // Currently, the new offset is just ignored.
+        newoffset : wasi.Filesize;
+        error := wasi.fd_seek(data.fd, ~~ to, ~~ whence, ^newoffset);
+        if error != .Success do return .BadFile;
+
+        return .None;
+    },
+
+    tell = (use fs: ^os.File) -> (io.Error, u32) {
+        location : wasi.Filesize;
+        error := wasi.fd_tell(data.fd, ^location);
+        if error != .Success do return .BadFile, 0;
+
+        return .None, ~~location;
+    },
+
+    read = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
+        bytes_read : wasi.Size;
+        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+        error := wasi.fd_read(data.fd, ^vec, 1, ^bytes_read);
+        if error != .Success do return .BadFile, 0;
+
+        return .None, bytes_read;
+    },
+
+    read_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
+        bytes_read : wasi.Size;
+        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+        error := wasi.fd_pread(data.fd, ^vec, 1, ~~at, ^bytes_read);
+
+        // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
+        if error != .Success do return .BadFile, 0;
+
+        return .None, bytes_read;
+    },
+
+    read_byte = (use fs: ^os.File) -> (io.Error, u8) {
+        bytes_read : wasi.Size;
+        byte  : u8;
+        vec   := IOVec.{ buf = cast(u32) ^byte, len = 1};
+        error := wasi.fd_read(data.fd, ^vec, 1, ^bytes_read);
+        if error != .Success do return .BadFile, 0;
+
+        return .None, byte;
+    },
+
+    write = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
+        bytes_written : wasi.Size;
+        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+        error := wasi.fd_write(data.fd, ^vec, 1, ^bytes_written);
+        if error != .Success do return .BadFile, 0;
+
+        return .None, bytes_written;
+    },
+
+    write_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
+        bytes_written : wasi.Size;
+        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
+        error := wasi.fd_pwrite(data.fd, ^vec, 1, ~~at, ^bytes_written);
+
+        // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
+        if error != .Success do return .BadFile, 0;
+
+        return .None, bytes_written;
+    },
+
+    write_byte = (use fs: ^os.File, byte: u8) -> io.Error {
+        bytes_written : wasi.Size;
+        byte_to_write := byte;
+        vec   := IOVec.{ buf = cast(u32) ^byte_to_write, len = 1 };
+        error := wasi.fd_write(data.fd, ^vec, 1, ^bytes_written);
+        if error != .Success do return .BadFile;
+
+        return .None;
+    },
+
+    close = (use fs: ^os.File) -> io.Error {
+        __file_close(data);
+        return .None;
+    },
+
+    flush = (use fs: ^os.File) -> io.Error {
+        wasi.fd_datasync(data.fd);
+        return .None;
+    },
+
+    size = (use fs: ^os.File) -> i32 {
+        file_stat: FileStat;
+        if wasi.fd_filestat_get(data.fd, ^file_stat) != .Success do return 0;
+
+        return ~~ file_stat.size;
+    },
+}
+
+
+WasiDirectory :: struct {
+    dir_fd: FileDescriptor;
+    last_cookie: wasi.DirCookie;
+}
+
+DirectoryData :: ^WasiDirectory;
+
+__dir_open :: (path: str, dir: ^DirectoryData) -> bool {
+    dir_fd: FileDescriptor;
+    err := wasi.path_open(4, .SymLinkFollow, path, .Directory, ~~0xffffffff, ~~0xffffffff, .Sync, ^dir_fd);
+    if err != .Success {
+        return false;
+    }
+
+    d := new(WasiDirectory);
+    d.dir_fd = dir_fd;
+    d.last_cookie = 0;
+
+    *dir = d;
+    return true;
+}
+
+__dir_close :: (dir: DirectoryData) {
+    wasi.fd_close(dir.dir_fd);
+    cfree(dir);
+}
+
+__dir_read :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool {
+    buffer: [512] u8;
+    bufused: u32;
+
+    err := wasi.fd_readdir(dir.dir_fd, ~~buffer, 512, dir.last_cookie, ^bufused);
+    if err != .Success || bufused == 0 do return false;
+
+    dirent := cast(^wasi.DirEnt) buffer;
+    switch dirent.d_type {
+        case .Unknown do out_entry.type = .Unknown;
+        case .BlockDevice do out_entry.type = .Block;
+        case .CharDevice do out_entry.type = .Char;
+        case .Directory do out_entry.type = .Directory;
+        case .RegularFile do out_entry.type = .RegularFile;
+        case .SymLink do out_entry.type = .SymLink;
+        case #default do out_entry.type = .Other;
+    }
+
+    out_entry.identifier = ~~dirent.d_ino;
+    out_entry.name_length = dirent.d_namlen;
+    memory.set(~~^out_entry.name_data, 0, 256);
+    memory.copy(~~^out_entry.name_data, ~~(dirent + 1), math.min(dirent.d_namlen, sizeof typeof out_entry.name_data));
+
+    dir.last_cookie = dirent.d_next;
+    return true;
+}
+
+__dir_create :: (path: str) -> bool {
+    created := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_create_directory(it, path);
+        if err == .Success do created = true;
+    }
+
+    return created;
+}
+
+__dir_remove :: (path: str) -> bool {
+    removed := false;
+    for .[3, 4] { // Trying both preopened directories
+        err := wasi.path_remove_directory(it, path);
+        if err == .Success do removed = true;
+    }
+
+    return removed;
+}
diff --git a/core/runtime/wasi.onyx b/core/runtime/wasi.onyx
deleted file mode 100644 (file)
index f94874d..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-package runtime
-
-#load "core/wasi/wasi"
-#load "core/runtime/common"
-
-use wasi
-use core
-
-__output_string :: (s: str) -> u32 {
-    STDOUT_FILENO :: 1
-
-    vec := IOVec.{ buf = cast(u32) s.data, len = s.count };
-    tmp : Size;
-    fd_write(STDOUT_FILENO, ^vec, 1, ^tmp);
-    fd_datasync(STDOUT_FILENO);
-    return tmp;
-}
-
-__exit :: (status: i32) do proc_exit(status);
-
-__read_from_input :: (buffer: [] u8) -> i32 {
-    STDIN_FILENO :: 0
-
-    vec := IOVec.{ buf = cast(i32) buffer.data, len = buffer.count };
-    read: Size;
-    error := fd_read(STDIN_FILENO, ^vec, 1, ^read);
-    if error != .Success do return -1;
-
-    return read;
-}
-
-__sleep :: (milliseconds: u32) {
-    tagged: SubscriptionTagged;
-    tagged.tag = .Clock;
-    tagged.clock = .{
-        id = .Realtime,
-        timeout = cast(u64) milliseconds * 1000000,
-        precision = 1,
-        flags = ~~0,
-    };
-
-    subscription := Subscription.{
-        userdata = 0,
-        u = tagged,
-    };
-
-    event: Event;
-    number_of_events: u32;
-
-    error_code := poll_oneoff(^subscription, ^event, 1, ^number_of_events);
-}
-
-
-// The builtin _start proc.
-// Sets up everything needed for execution.
-#export "_start" () {
-    __runtime_initialize();
-    context.thread_id = 0;
-
-    #if (typeof (package main).main) == #type () -> void {
-        (package main).main();
-
-    } else {
-        args : [] cstr;
-        argv_buf_size : Size;
-        args_sizes_get(^args.count, ^argv_buf_size);
-
-        args = memory.make_slice(cstr, args.count);
-        argv_buf := cast(cstr) calloc(argv_buf_size);
-        args_get(args.data, argv_buf);
-
-
-        // This post processing of the argv array needs to happen if the target is using
-        // 32-bit pointers, instead of 64-bits. Right now, Onyx pointers take up 64-bits,
-        // but in most circumstances, only the lower 32-bits are used. When webassembly
-        // standardizes the 64-bit address space, it will be an easy conversion over.
-        // But for right now, WASI will give the argv array 32-bit pointers, instead of
-        // 64-bit pointers. This loops expands the 32-bit pointers into 64-bit pointers
-        // while not clobbering any of them.
-        while i := cast(i32) (args.count - 1); i >= 0 {
-            defer i -= 1;
-
-            args[i] = cast(cstr) (cast(^u32) args.data)[i];
-        }
-
-        (package main).main(args);
-    }
-
-    __flush_stdio();
-}
-
index 2df668c5243fb63db16d2581ed8358ec50356d7c..fc9386650e0474188df99e75d84c35571b37afdd 100644 (file)
@@ -49,42 +49,45 @@ package core
 #load "./misc/arg_parse"
 #load "./misc/method_ops"
 
-#if runtime.runtime == .Wasi || runtime.runtime == .Onyx {
+#load "./time/date"
+#load "./encoding/base64"
+
+#load "./runtime/common"
+
+#if runtime.platform.Supports_Files {
     #load "./os/file"
-    #load "./os/os"
+}
+
+#if runtime.platform.Supports_Directories {
     #load "./os/dir"
 }
 
-#if runtime.runtime == .Onyx   {
-    #load "./runtime/onyx_run"
+#if runtime.platform.Supports_Os {
+    #load "./os/os"
+}
 
+#if runtime.platform.Supports_Processes {
     #load "./os/process"
+}
+
+#if runtime.platform.Supports_Time {
     #load "./time/time"
-    #load "./time/date"
+}
 
+#if runtime.platform.Supports_Networking {
     #load "./net/net"
     #load "./net/tcp"
-
-    #load "./onyx/fs"
-    #load "./onyx/cptr"
-    #load "./onyx/cbindgen"
-    #load "./onyx/fault_handling"
-}
-#if runtime.runtime == .Wasi   {
-    #load "./wasi/wasi"
-    #load "./runtime/wasi"
-    #load "./wasi/clock"
-    #load "./wasi/env"
-    #load "./wasi/wasi_fs"
 }
-#if runtime.runtime == .Js     { #load "./runtime/js" }
-#if runtime.runtime != .Custom {
+
+#if runtime.platform.Supports_Type_Info {
     #load "./runtime/info/helper"
     #load "./io/stdio"
-
-    #load "./encoding/base64"
-    #load "./encoding/csv"
     #load "./misc/any_utils"
+    #load "./encoding/csv"
+}
+
+#if runtime.platform.Supports_Threads && runtime.Multi_Threading_Enabled {
+    #load "./threads/thread"
 }
 
 #if runtime.Multi_Threading_Enabled {
@@ -95,6 +98,25 @@ package core
     #load "./sync/semaphore"
     #load "./sync/barrier"
     #load "./sync/once"
+}
 
-    #load "./threads/thread"
+
+//
+// Load platform files
+//
+
+#if runtime.runtime == .Onyx {
+    #load "./runtime/platform/onyx/platform"
+
+    #load "./onyx/cptr"
+    #load "./onyx/cbindgen"
+    #load "./onyx/fault_handling"
 }
+
+#if runtime.runtime == .Wasi   {
+    #load "./runtime/platform/wasi/platform"
+}
+
+#if runtime.runtime == .Js {
+    #load "./runtime/platform/js/platform"
+}
\ No newline at end of file
index c234a834d4e2bf144e157fc45934595cf377492e..c7152f16b2b2879cb454ae743206b6a0f8ce10c5 100644 (file)
@@ -41,7 +41,7 @@ spawn :: (t: ^Thread, data: ^$T, func: (^T) -> void) {
 
     stack_base := raw_alloc(alloc.heap_allocator, 1 << 20);
 
-    runtime.__spawn_thread(t.id, tls_base, stack_base, func, data);
+    runtime.platform.__spawn_thread(t.id, tls_base, stack_base, func, data);
 }
 
 //
@@ -54,7 +54,7 @@ join :: (t: ^Thread) {
             __atomic_wait(^t.id, t.id);
         } else {
             // To not completely kill the CPU.
-            runtime.__sleep(1);
+            runtime.platform.__sleep(1);
         }
     }
 }
@@ -65,7 +65,7 @@ join :: (t: ^Thread) {
 kill :: (t: ^Thread) -> i32 {
     if !t.alive do return -1;
 
-    ret := runtime.__kill_thread(t.id);
+    ret := runtime.platform.__kill_thread(t.id);
     if ret > 0 do __exited(t.id);
 
     return 1;
index 74bc824918d940607b42e562188137b8ed882fba..42944419a9b00a8efec34d3729b773bb0be0888a 100644 (file)
@@ -16,8 +16,11 @@ Date :: struct {
         return .{ year, month - 1, day };
     }
 
-    today :: () -> Date {
-        return now()->as_date();
+
+    #if runtime.platform.Supports_Time {
+        today :: () -> Date {
+            return now()->as_date();
+        }
     }
 
     add_months :: (d: Date, days: i32) -> Date {
index b61975d05e32949283058c9b9863314c8290fa11..5d4ad95f5cf091dee4849b371e394faf79454258 100644 (file)
@@ -1,8 +1,14 @@
 package core.time
 
 use core {os, conv}
+use runtime.platform {
+    __time_gmtime,
+    __time_localtime,
+    __time_mktime,
+    __time_strftime
+}
 
-#if runtime.runtime != .Onyx {
+#if !runtime.platform.Supports_Time {
     #error "'core.time' should only be used with the Onyx runtime, for now.";
 }
 
@@ -328,12 +334,3 @@ strptime :: (buf_: [] u8, format_: [] u8, tm: ^Timestamp) -> bool {
     }
 }
 
-#local {
-    #foreign "onyx_runtime" {
-        __time_localtime :: (time: u64, tm: ^Timestamp) -> void ---
-        __time_gmtime    :: (time: u64, tm: ^Timestamp) -> void ---
-        __time_mktime    :: (tm: ^Timestamp) -> i64 ---
-        __time_strftime  :: (buf: [] u8, format: cstr, tm: ^Timestamp) -> u32 ---
-    }
-}
-
diff --git a/core/wasi/README.md b/core/wasi/README.md
deleted file mode 100644 (file)
index 3c3e6ee..0000000
+++ /dev/null
@@ -1 +0,0 @@
-These files are only available on the 'wasi' runtime. In a C compilation, other files will be included, and the API between them will have to remain the same.
\ No newline at end of file
diff --git a/core/wasi/clock.onyx b/core/wasi/clock.onyx
deleted file mode 100644 (file)
index 468327f..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-package core.clock
-
-#local runtime :: package runtime
-#if runtime.runtime != .Wasi 
-    && runtime.runtime != .Onyx {
-    #error "'core.clock' is only available with the 'wasi' or 'onyx' runtimes.";
-}
-
-use package wasi
-
-time :: () -> u64 {
-    output_time: Timestamp;
-    clock_time_get(.Realtime, 500000, ^output_time);
-    return ~~(output_time / 1000000);
-}
-
-time_ns :: () -> u64 {
-    output_time: Timestamp;
-    clock_time_get(.Realtime, 1, ^output_time);
-    return ~~output_time;
-}
-
-sleep :: #match {
-    (seconds: u64)      { sleep(nanoseconds=seconds * 1000000000); },
-    (milliseconds: u64) { sleep(nanoseconds=milliseconds * 1000000); },
-
-    (nanoseconds: u64) {
-        tagged: SubscriptionTagged;
-        tagged.tag = .Clock;
-        tagged.clock = .{
-            id = .Realtime,
-            timeout = cast(u64) nanoseconds,
-            precision = 1,
-            flags = ~~0,
-        };
-
-        subscription := Subscription.{
-            userdata = 0,
-            u = tagged,
-        };
-
-        event: Event;
-        number_of_events: u32;
-
-        error_code := poll_oneoff(^subscription, ^event, 1, ^number_of_events);
-    }
-}
diff --git a/core/wasi/env.onyx b/core/wasi/env.onyx
deleted file mode 100644 (file)
index 7cd5b3b..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-package core.env
-
-#local runtime :: package runtime
-#if runtime.runtime != .Wasi 
-    && runtime.runtime != .Onyx {
-    #error "'core.env' is only available with the 'wasi' and 'onyx' runtimes.";
-}
-
-
-use package wasi { environ_get, environ_sizes_get, Size }
-#package map    :: package core.map
-#package memory :: package core.memory
-#package string :: package core.string
-
-Environment :: struct {
-    vars             : Map(str, str);
-
-    buffer           : [] u8;
-    buffer_allocator : Allocator;
-}
-
-get_env :: (allocator := context.allocator) -> Environment {
-    env_count, env_buf_size : Size;
-    environ_sizes_get(^env_count, ^env_buf_size);
-
-    env_var := memory.make_slice(cstr, env_count, allocator=allocator);
-    env_buf := memory.make_slice(u8, env_buf_size, allocator=allocator);
-
-    environ_get(env_var.data, env_buf.data);
-
-    while i := cast(i32) (env_var.count - 1); i >= 0 {
-        defer i -= 1;
-
-        env_var[i] = cast(cstr) (cast(^u32) env_var.data)[i];
-    }
-
-    env_map := map.make(str, str, "");
-    for env: env_var {
-        s := string.from_cstr(env);
-        var := string.read_until(^s, #char "=");
-        map.put(^env_map, var, string.advance(s, 1));
-    }
-
-    raw_free(allocator, env_var.data);
-
-    return .{
-        vars             = env_map,
-        buffer           = env_buf,
-        buffer_allocator = allocator,
-    };
-}
-
-free_env :: (use env: ^Environment) {
-    map.free(^vars);
-
-    raw_free(buffer_allocator, buffer.data);
-}
diff --git a/core/wasi/wasi.onyx b/core/wasi/wasi.onyx
deleted file mode 100644 (file)
index 7350563..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-package wasi
-
-Size      :: #type u32;
-Filesize  :: #type u64;
-Timestamp :: #type u64;
-
-ClockID   :: enum (u32) {
-    Realtime         :: 0x00;
-    Monotonic        :: 0x01;
-    ProcessCPUTimeID :: 0x02;
-    ThreadCPUTimeID  :: 0x03;
-}
-
-Errno :: enum (u16) {
-    Success      :: 0x00;
-    TooBig       :: 0x01;
-    Access       :: 0x02;
-    AddrInUse    :: 0x03;
-    AddrNotAvail :: 0x04;
-    AFNoSupport  :: 0x05;
-    Again        :: 0x06;
-    Already      :: 0x07;
-    BadFile      :: 0x08;
-    BadMsg       :: 0x09;
-    Busy         :: 0x0a;
-    Canceled     :: 0x0b;
-    Child        :: 0x0c;
-    ConnAborted  :: 0x0d;
-    ConnRefused  :: 0x0e;
-    ConnReset    :: 0x0f;
-    DeadLock     :: 0x10;
-    DestAddrReq  :: 0x11;
-    Domain       :: 0x12;
-    DQUOT        :: 0x13;
-    Exist        :: 0x14;
-    Fault        :: 0x15;
-    FileTooBig   :: 0x16;
-    HostUnreach  :: 0x17;
-    IdentRemoved :: 0x18;
-    IllegalSeq   :: 0x19;
-    InProgress   :: 0x1a;
-    Interrupt    :: 0x1b;
-    Invalid      :: 0x1c;
-    IO           :: 0x1d;
-    IsConnection :: 0x1e;
-    IsDirectory  :: 0x1f;
-    Loop         :: 0x20;
-    MFile        :: 0x21;
-    MLink        :: 0x22;
-    MsgSize      :: 0x23;
-    MultiHop     :: 0x24;
-    NameTooLong  :: 0x25;
-    NetDown      :: 0x26;
-    NetReset     :: 0x27;
-    NetUnreach   :: 0x28;
-    NFile        :: 0x29;
-    NoBufs       :: 0x2a;
-    NoDev        :: 0x2b;
-    NoEntry      :: 0x2c;
-    NoExec       :: 0x2d;
-    NoLock       :: 0x2e;
-    NoLink       :: 0x2f;
-    NoMemory     :: 0x30;
-    NoMsg        :: 0x31;
-    NoProtoOpt   :: 0x32;
-    NoSpace      :: 0x33;
-    NoSys        :: 0x34;
-    NotConn      :: 0x35;
-    NotDir       :: 0x36;
-    NotEmpty     :: 0x37;
-    NotRecover   :: 0x38;
-    NotSock      :: 0x39;
-    NotSupported :: 0x3a;
-    NoTTY        :: 0x3b;
-    NXIO         :: 0x3c;
-    Overflow     :: 0x3d;
-    OwnerDead    :: 0x3e;
-    Permission   :: 0x3f;
-    Pipe         :: 0x40;
-    Protocol     :: 0x41;
-    ProtoNoSup   :: 0x42;
-    Prototype    :: 0x43;
-    Range        :: 0x44;
-    ReadonlyFS   :: 0x45;
-    SeekPipe     :: 0x46;
-    Search       :: 0x47;
-    Stale        :: 0x48;
-    Timedout     :: 0x49;
-    TextBusy     :: 0x4a;
-    XDev         :: 0x4b;
-
-    NotCapable   :: 0x4c;
-}
-
-Rights :: enum #flags (u64) {
-    DataSync;
-    Read;
-    Seek;
-    FdStatSetFlags;
-    Sync;
-    Tell;
-    Write;
-    Advise;
-    Allocate;
-    PathCreateDirectory;
-    PathCreateFile;
-    PathLinkSource;
-    PathLinkTarget;
-    PathOpen;
-    ReadDir;
-    PathReadlink;
-    PathRenameSource;
-    PathRenameTarget;
-    PathFilestatGet;
-    PathFilestateSetSize;
-    PathFilestateSetTimes;
-    FilestatGet;
-    FilestatSetSize;
-    FilestatSetTimes;
-    PathSymlink;
-    PathRemoveDirectory;
-    PathUnlinkFile;
-    PollFDReadWrite;
-    SockShutdown;
-}
-
-FileDescriptor :: #type i32;
-
-IOVec :: struct {
-    buf : u32; // actually a ^u8, but WASM is 32-bit at the moment;
-    len : u32;
-}
-
-CIOVec :: #type IOVec; // Constant IOVec
-
-FileDelta :: #type i64;
-
-Whence :: enum (u8) {
-    Set :: 0x00;
-    Cur :: 0x01;
-    End :: 0x02;
-}
-
-DirCookie  :: #type u64;
-DirNameLen :: #type u32;
-INode      :: #type u64;
-
-Filetype :: enum (u8) {
-    Unknown      :: 0x00;
-    BlockDevice  :: 0x01;
-    CharDevice   :: 0x02;
-    Directory    :: 0x03;
-    RegularFile  :: 0x04;
-    SocketDgram  :: 0x05;
-    SocketStream :: 0x06;
-    SymLink      :: 0x07;
-}
-
-DirEnt :: struct {
-    d_next   : DirCookie;
-    d_ino    : INode;
-    d_namlen : DirNameLen;
-    d_type   : Filetype;
-}
-
-Advice :: enum (u8) {
-    Normal     :: 0x00;
-    Sequential :: 0x01;
-    Random     :: 0x02;
-    WillNeed   :: 0x03;
-    DontNeed   :: 0x04;
-    NoReuse    :: 0x05;
-}
-
-FDFlags :: enum #flags (u16) {
-    Append;
-    DSync;
-    NonBlock;
-    RSync;
-    Sync;
-}
-
-FDStat :: struct {
-    fs_filetype          : Filetype;
-    fs_flags             : FDFlags;
-    fs_rights_base       : Rights;
-    fs_rights_inheriting : Rights;
-}
-
-Device :: #type u64;
-
-FSTFlags :: enum #flags (u16) {
-    ATIM;
-    ATIM_NOW;
-    MTIM;
-    MTIM_NOW;
-}
-
-LookupFlags :: enum #flags (u32) {
-    SymLinkFollow;
-}
-
-OFlags :: enum #flags (u16) {
-    Creat;
-    Directory;
-    FailIfExists;
-    Trunc;
-}
-
-LinkCount :: #type u64;
-
-FileStat :: struct {
-    dev      : Device;
-    ino      : INode;
-    filetype : Filetype;
-
-    // ANGER(Brendan Hansen): I give the worst documentation in
-    // the entire world award to WASI, whose best documentation
-    // is a C header file, that is itself incorrect. nlink does
-    // not exist in the current version of wasmtime, and should
-    // not be here, but maybe in the future? Ugh.
-    //
-    // STILL ANGERY(Brendan Hansen): Actually, nlink does exist
-    // in the current version of wasmtime; however its actually
-    // a 32-bit value, not 64-bit, which means the offsets were
-    // incorrect. I fixed it for now, but when wasmtime updates
-    // the size of 'LinkCount' needs to be changed.
-    //
-    // EVEN ANGRIER(Brendan Hansen): ACTUALLY, I realized today
-    // that it has changed (at least for Wasmer). There was not
-    // any mention of this on any release documents, but hey, I
-    // guess this can actually be 64-bits now.
-    //                                   - brendanfh 2020/12/05
-    //                                   - brendanfh 2020/12/07
-    //                                   - brendanfh 2021/07/10
-    nlink    : LinkCount;
-
-    size     : Filesize;
-    atim     : Timestamp;
-    mtim     : Timestamp;
-    ctim     : Timestamp;
-}
-
-Userdata :: #type u64;
-
-EventType :: enum (u8) {
-    Clock;
-    FDRead;
-    FDWrite;
-}
-
-EventRWFlags :: enum #flags (u16) {
-    ReadWriteHangUp;
-}
-
-EventFDReadWrite :: struct {
-    nbytes : Filesize;
-    flags  : EventRWFlags;
-}
-
-Event :: struct {
-    userdata     : Userdata;
-    error        : Errno;
-    type         : EventType;
-    fd_readwrite : EventFDReadWrite;
-}
-
-SubClockFlags :: enum #flags (u16) {
-    ClockAbsTime;
-}
-
-SubscriptionClock :: struct {
-    id        : ClockID;
-    timeout   : Timestamp;
-    precision : Timestamp;
-    flags     : SubClockFlags;
-}
-
-SubscriptionFDReadWrite :: struct {
-    file_descriptor : FileDescriptor;
-}
-
-SubscriptionTagged :: struct {
-    tag : EventType;
-
-    use u : struct #union {
-        clock    : SubscriptionClock;
-        fd_read  : SubscriptionFDReadWrite;
-        fd_write : SubscriptionFDReadWrite;
-    };
-}
-
-Subscription :: struct {
-    userdata : Userdata;
-
-    u : SubscriptionTagged;
-}
-
-ExitCode :: #type u32;
-Signal :: enum (u8) {
-    None;
-    Hup;
-    Int;
-    Quit;
-    Ill;
-    Trap;
-    Abrt;
-    Bus;
-    Fpe;
-    Kill;
-    USR1;
-    Segv;
-    USR2;
-    Pipe;
-    Alrm;
-    Term;
-    Chld;
-    Stop;
-    Tstp;
-    Ttin;
-    Urg;
-    Xcpu;
-    Xfsz;
-    Vtalrm;
-    Prof;
-    Winch;
-    Poll;
-    Pwr;
-    Sys;
-}
-
-RIFlags :: enum #flags (u16) {
-    RecvPeek;
-    RecvWaitAll;
-}
-
-ROFlags :: enum #flags (u16) {
-    RecvDataTruncated :: 1;
-}
-
-SIFlags :: enum #flags (u16) {
-    None;
-}
-
-SDFlags :: enum #flags (u16) {
-    RD;
-    WR;
-}
-
-PreopenType :: enum (u8) {
-    Dir :: 0x00;
-}
-
-PrestatDir :: struct {
-    pr_name_len : Size;
-}
-
-PrestatTagged :: struct {
-    tag : PreopenType;
-
-    u : struct #union {
-        dir : PrestatDir;
-    };
-}
-
-
-// FUNCTIONS
-args_get       :: (argv: ^^u8, argv_buf: ^u8) -> Errno #foreign "wasi_snapshot_preview1" "args_get"---
-args_sizes_get :: (argc: ^Size, argv_buf_size: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "args_sizes_get" ---
-
-environ_get       :: (environ: ^^u8, environ_buf: ^u8) -> Errno #foreign "wasi_snapshot_preview1" "environ_get" ---
-environ_sizes_get :: (environc: ^Size, environ_buf_size: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "environ_sizes_get" ---
-
-clock_res_get  :: (id: ClockID, resolution: ^Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_res_get" ---
-clock_time_get :: (id: ClockID, precision: Timestamp, time: ^Timestamp) -> Errno #foreign "wasi_snapshot_preview1" "clock_time_get" ---
-
-fd_advise             :: (fd: FileDescriptor, offset: Filesize, len: Filesize, advice: Advice) -> Errno #foreign "wasi_snapshot_preview1" "fd_advise" ---
-fd_allocate           :: (fd: FileDescriptor, offset: Filesize, len: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_allocate" ---
-fd_close              :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_close" ---
-fd_datasync           :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_datasync" ---
-fd_fdstat_get         :: (fd: FileDescriptor, stat: ^FDStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_get" ---
-fd_fdstat_set_flags   :: (fd: FileDescriptor, flags: FDFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_flags" ---
-fd_fdstat_set_rights  :: (fd: FileDescriptor, rights_base: Rights, rights_inheriting: Rights) -> Errno #foreign "wasi_snapshot_preview1" "fd_fdstat_set_rights" ---
-fd_filestat_get       :: (fd: FileDescriptor, buf: ^FileStat) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_get" ---
-fd_filestat_set_size  :: (fd: FileDescriptor, size: Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_size" ---
-fd_filestat_set_times :: (fd: FileDescriptor, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "fd_filestat_set_times" ---
-fd_pread              :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, offset: Filesize, nread: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pread" ---
-fd_prestat_get        :: (fd: FileDescriptor, buf: ^PrestatTagged) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_get" ---
-fd_prestat_dir_name   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "fd_prestat_dir_name" ---
-fd_pwrite             :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, offset: Filesize, nwritten: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_pwrite" ---
-fd_read               :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, nread: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_read" ---
-fd_readdir            :: (fd: FileDescriptor, buf: ^u8, buf_len: Size, cookie: DirCookie, bufused: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_readdir" ---
-fd_renumber           :: (fd: FileDescriptor, to: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_renumber" ---
-fd_seek               :: (fd: FileDescriptor, offset: FileDelta, whence: Whence, newoffset: ^Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_seek" ---
-fd_sync               :: (fd: FileDescriptor) -> Errno #foreign "wasi_snapshot_preview1" "fd_sync" ---
-fd_tell               :: (fd: FileDescriptor, offset: ^Filesize) -> Errno #foreign "wasi_snapshot_preview1" "fd_tell" ---
-fd_write              :: (fd: FileDescriptor, iovs: ^IOVec, iovs_len: Size, nwritten: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "fd_write" ---
-
-path_create_directory   :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_create_directory" ---
-path_filestat_get       :: (fd: FileDescriptor, flags: LookupFlags, path: str, buf: ^FileStat) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_get" ---
-path_filestat_set_times :: (fd: FileDescriptor, flags: LookupFlags, path: str, atim: Timestamp, mtim: Timestamp, fst_flags: FSTFlags) -> Errno #foreign "wasi_snapshot_preview1" "path_filestat_set_times" ---
-
-path_link :: (fd: FileDescriptor, old_flags: LookupFlags, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_link" ---
-path_open :: (fd: FileDescriptor
-    , dirflags: LookupFlags
-    , path: str
-    , oflags: OFlags
-    , fs_rights_base: Rights
-    , fs_rights_inherting: Rights
-    , fdflags: FDFlags
-    , opened_fd: ^FileDescriptor
-    ) -> Errno
-     #foreign "wasi_snapshot_preview1" "path_open" ---
-
-path_readlink :: (fd: FileDescriptor, path: str, buf: ^u8, buf_len: Size, bufused: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "path_readlink" ---
-path_remove_directory :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_remove_directory" ---
-path_rename  :: (fd: FileDescriptor, old_path: str, new_fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_rename" ---
-path_symlink :: (old_path: ^u8, old_path_len: Size, fd: FileDescriptor, new_path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_symlink" ---
-path_unlink_file :: (fd: FileDescriptor, path: str) -> Errno #foreign "wasi_snapshot_preview1" "path_unlink_file" ---
-
-poll_oneoff :: (in: ^Subscription, out: ^Event, nsubscriptions: Size, nevents: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "poll_oneoff" ---
-
-proc_exit  :: (rval: ExitCode) -> void #foreign "wasi_snapshot_preview1" "proc_exit" ---
-proc_raise :: (sig: Signal) -> Errno #foreign "wasi_snapshot_preview1" "proc_raise" ---
-
-sched_yield :: () -> Errno #foreign "wasi_snapshot_preview1" "sched_yield" ---
-
-random_get :: (buf: ^u8, buf_len: Size) -> Errno #foreign "wasi_snapshot_preview1" "random_get" ---
-
-sock_recv     :: (fd: FileDescriptor, ri_data: ^IOVec, ri_data_len: Size, ri_flags: RIFlags, ro_datalen: ^Size, ro_flags: ^ROFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_recv" ---
-sock_send     :: (fd: FileDescriptor, si_data: ^IOVec, ri_data_len: Size, si_flags: SIFlags, so_datalen: ^Size) -> Errno #foreign "wasi_snapshot_preview1" "sock_send" ---
-sock_shutdown :: (fd: FileDescriptor, how: SDFlags) -> Errno #foreign "wasi_snapshot_preview1" "sock_shutdown" ---
-
-
-
diff --git a/core/wasi/wasi_fs.onyx b/core/wasi/wasi_fs.onyx
deleted file mode 100644 (file)
index 7bc6e7e..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-package runtime.fs
-
-#local runtime :: package runtime
-#if runtime.runtime != .Wasi {
-    #error "The file system library is currently only available on the WASI runtime, and should only be included if that is the chosen runtime."
-}
-
-use package core
-
-#local wasi :: package wasi
-use package wasi {
-    FileDescriptor,
-    FDFlags, OFlags, Rights,
-    LookupFlags, Errno,
-    IOVec, Size,
-    FileStat, Whence
-}
-
-FileData :: struct {
-    fd     : FileDescriptor = -1;
-
-    mode   : os.OpenMode = .Invalid;
-    rights : Rights   = ~~ 0;
-    flags  : FDFlags  = ~~ 0;
-}
-
-__file_open :: (path: str, mode := os.OpenMode.Read) -> (FileData, os.FileError) {
-    // Requesting all of the rights because why not.
-    rights :=
-        Rights.DataSync
-        | Rights.Read
-        | Rights.Seek
-        | Rights.FdStatSetFlags
-        | Rights.Sync
-        | Rights.Tell
-        | Rights.Write
-        | Rights.Advise
-        | Rights.Allocate
-        | Rights.PathCreateDirectory
-        | Rights.PathCreateFile
-        | Rights.PathLinkSource
-        | Rights.PathLinkTarget
-        | Rights.PathOpen
-        | Rights.ReadDir
-        | Rights.PathReadlink
-        | Rights.PathRenameSource
-        | Rights.PathRenameTarget
-        | Rights.PathFilestatGet
-        | Rights.PathFilestateSetSize
-        | Rights.PathFilestateSetTimes
-        | Rights.FilestatGet
-        | Rights.FilestatSetSize
-        | Rights.FilestatSetTimes
-        | Rights.PathSymlink
-        | Rights.PathRemoveDirectory
-        | Rights.PathUnlinkFile
-        | Rights.PollFDReadWrite;
-
-    open_flags := cast(OFlags) 0;
-    fd_flags := FDFlags.Sync;
-
-    switch mode {
-        case .Write {
-            open_flags |= OFlags.Creat | OFlags.Trunc;
-            rights     |= Rights.Write | Rights.Seek | Rights.Tell;
-        }
-
-        case .Append {
-            open_flags |= OFlags.Creat;
-            rights     |= Rights.Write | Rights.Seek | Rights.Tell;
-            fd_flags   |= FDFlags.Append;
-        }
-
-        case .Read {
-            rights |= Rights.Read | Rights.Seek | Rights.Tell;
-        }
-    }
-
-    file := FileData.{ fd = -1 };
-    file.mode   = mode;
-    file.rights = rights;
-    file.flags  = fd_flags;
-
-    // Currently the directory's file descriptor appears to always be 3
-    // However, this is not necessarily correct, so also try a preopened directory
-    for DIR_FD: .[ 3, 4 ] {
-        if err := wasi.path_open(
-            DIR_FD,
-            .SymLinkFollow,
-            path,
-            open_flags,
-            rights,
-            rights,
-            fd_flags,
-            ^file.fd);
-            err == .Success {
-                return file, .None;
-        }
-    }
-
-    // @TODO // provide a better error code.
-    return file, .NotFound;
-}
-
-__file_close :: (file: FileData) -> os.FileError {
-    if wasi.fd_close(file.fd) != .Success {
-        return .BadFile;
-    }
-
-    return .None;
-}
-
-__file_stat :: (path: str, out: ^os.FileStat) -> bool {
-    fs: wasi.FileStat;
-
-    exists := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
-        if err == .Success {
-            exists = true;
-            out.size = ~~ fs.size;
-
-            switch fs.filetype {
-                case .RegularFile do out.type = .RegularFile;
-                case .Directory do out.type = .Directory;
-                case .SymLink do out.type = .SymLink;
-                case #default do out.type = .Unknown;
-            }
-        }
-    }
-
-    return exists;
-}
-
-__file_exists :: (path: str) -> bool {
-    fs: wasi.FileStat;
-
-    exists := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_filestat_get(it, .SymLinkFollow, path, ^fs);
-        if err == .Success do exists = true;
-    }
-
-    return exists;
-}
-
-__file_remove :: (path: str) -> bool {
-    removed := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_unlink_file(it, path);
-        if err == .Success do removed = true;
-    }
-
-    return removed;
-}
-
-__file_rename :: (old_path: str, new_path: str) -> bool {
-    renamed := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_rename(it, old_path, it, new_path);
-        if err == .Success do renamed = true;
-    }
-
-    return renamed;
-}
-
-__file_stream_vtable := io.Stream_Vtable.{
-    seek = (use fs: ^os.File, to: i32, whence: io.SeekFrom) -> io.Error {
-        // Currently, the new offset is just ignored.
-        newoffset : wasi.Filesize;
-        error := wasi.fd_seek(data.fd, ~~ to, ~~ whence, ^newoffset);
-        if error != .Success do return .BadFile;
-
-        return .None;
-    },
-
-    tell = (use fs: ^os.File) -> (io.Error, u32) {
-        location : wasi.Filesize;
-        error := wasi.fd_tell(data.fd, ^location);
-        if error != .Success do return .BadFile, 0;
-
-        return .None, ~~location;
-    },
-
-    read = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
-        bytes_read : wasi.Size;
-        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
-        error := wasi.fd_read(data.fd, ^vec, 1, ^bytes_read);
-        if error != .Success do return .BadFile, 0;
-
-        return .None, bytes_read;
-    },
-
-    read_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
-        bytes_read : wasi.Size;
-        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
-        error := wasi.fd_pread(data.fd, ^vec, 1, ~~at, ^bytes_read);
-
-        // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
-        if error != .Success do return .BadFile, 0;
-
-        return .None, bytes_read;
-    },
-
-    read_byte = (use fs: ^os.File) -> (io.Error, u8) {
-        bytes_read : wasi.Size;
-        byte  : u8;
-        vec   := IOVec.{ buf = cast(u32) ^byte, len = 1};
-        error := wasi.fd_read(data.fd, ^vec, 1, ^bytes_read);
-        if error != .Success do return .BadFile, 0;
-
-        return .None, byte;
-    },
-
-    write = (use fs: ^os.File, buffer: [] u8) -> (io.Error, u32) {
-        bytes_written : wasi.Size;
-        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
-        error := wasi.fd_write(data.fd, ^vec, 1, ^bytes_written);
-        if error != .Success do return .BadFile, 0;
-
-        return .None, bytes_written;
-    },
-
-    write_at = (use fs: ^os.File, at: u32, buffer: [] u8) -> (io.Error, u32) {
-        bytes_written : wasi.Size;
-        vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
-        error := wasi.fd_pwrite(data.fd, ^vec, 1, ~~at, ^bytes_written);
-
-        // FIX: Maybe report io.Error.OutOfBounds if the 'at' was out of bounds?
-        if error != .Success do return .BadFile, 0;
-
-        return .None, bytes_written;
-    },
-
-    write_byte = (use fs: ^os.File, byte: u8) -> io.Error {
-        bytes_written : wasi.Size;
-        byte_to_write := byte;
-        vec   := IOVec.{ buf = cast(u32) ^byte_to_write, len = 1 };
-        error := wasi.fd_write(data.fd, ^vec, 1, ^bytes_written);
-        if error != .Success do return .BadFile;
-
-        return .None;
-    },
-
-    close = (use fs: ^os.File) -> io.Error {
-        __file_close(data);
-        return .None;
-    },
-
-    flush = (use fs: ^os.File) -> io.Error {
-        wasi.fd_datasync(data.fd);
-        return .None;
-    },
-
-    size = (use fs: ^os.File) -> i32 {
-        file_stat: FileStat;
-        if wasi.fd_filestat_get(data.fd, ^file_stat) != .Success do return 0;
-
-        return ~~ file_stat.size;
-    },
-}
-
-
-WasiDirectory :: struct {
-    dir_fd: FileDescriptor;
-    last_cookie: wasi.DirCookie;
-}
-
-DirectoryData :: ^WasiDirectory;
-
-__dir_open :: (path: str, dir: ^DirectoryData) -> bool {
-    dir_fd: FileDescriptor;
-    err := wasi.path_open(4, .SymLinkFollow, path, .Directory, ~~0xffffffff, ~~0xffffffff, .Sync, ^dir_fd);
-    if err != .Success {
-        return false;
-    }
-
-    d := new(WasiDirectory);
-    d.dir_fd = dir_fd;
-    d.last_cookie = 0;
-
-    *dir = d;
-    return true;
-}
-
-__dir_close :: (dir: DirectoryData) {
-    wasi.fd_close(dir.dir_fd);
-    cfree(dir);
-}
-
-__dir_read :: (dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool {
-    buffer: [512] u8;
-    bufused: u32;
-
-    err := wasi.fd_readdir(dir.dir_fd, ~~buffer, 512, dir.last_cookie, ^bufused);
-    if err != .Success || bufused == 0 do return false;
-
-    dirent := cast(^wasi.DirEnt) buffer;
-    switch dirent.d_type {
-        case .Unknown do out_entry.type = .Unknown;
-        case .BlockDevice do out_entry.type = .Block;
-        case .CharDevice do out_entry.type = .Char;
-        case .Directory do out_entry.type = .Directory;
-        case .RegularFile do out_entry.type = .RegularFile;
-        case .SymLink do out_entry.type = .SymLink;
-        case #default do out_entry.type = .Other;
-    }
-
-    out_entry.identifier = ~~dirent.d_ino;
-    out_entry.name_length = dirent.d_namlen;
-    memory.set(~~^out_entry.name_data, 0, 256);
-    memory.copy(~~^out_entry.name_data, ~~(dirent + 1), math.min(dirent.d_namlen, sizeof typeof out_entry.name_data));
-
-    dir.last_cookie = dirent.d_next;
-    return true;
-}
-
-__dir_create :: (path: str) -> bool {
-    created := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_create_directory(it, path);
-        if err == .Success do created = true;
-    }
-
-    return created;
-}
-
-__dir_remove :: (path: str) -> bool {
-    removed := false;
-    for .[3, 4] { // Trying both preopened directories
-        err := wasi.path_remove_directory(it, path);
-        if err == .Success do removed = true;
-    }
-
-    return removed;
-}
diff --git a/docs/ideas/platform_layer.md b/docs/ideas/platform_layer.md
new file mode 100644 (file)
index 0000000..e14cee2
--- /dev/null
@@ -0,0 +1,132 @@
+Onyx Standard Library Platform Layer
+====================================
+
+Onyx has *soft* definition for what is expected from the supporting
+platform within its standard library. This should be solidified into
+a concrete set of types and functions that are expected to be implemented,
+or _explicity not_ implemented for a given platform.
+
+This is where not having header files and having a fluid definition of
+functions does hurt a little bit, because it is not possible to say
+"This set of files should define all of these functions". Nonetheless,
+this document will serve as that "header file"
+
+## Always Expected
+- `__start() -> void`
+
+
+## Files
+
+### Types
+- `FileData`
+
+### Procedures
+- `__file_open(path: str, mode: core.os.OpenMode) -> (FileData, core.os.FileError)`
+- `__file_close(fd: FileData) -> core.os.FileError`
+- `__file_stat(path: str, stat: ^os.FileStat) -> bool`
+- `__file_exists(path: str) -> bool`
+- `__file_remove(path: str) -> bool`
+- `__file_rename(old_path, new_path: str) -> bool`
+
+### Values
+- `__file_stream_vtable: io.Stream_Vtable`
+
+
+## Directories
+
+### Types
+- `DirectoryData`
+
+- `__dir_open(path: str, dir: ^DirectoryData) -> bool`
+- `__dir_close(dir: DirectoryData) -> void`
+- `__dir_read(dir: DirectoryData, out_entry: ^os.DirectoryEntry) -> bool`
+- `__dir_create(path: str) -> bool`
+- `__dir_remove(path: str) -> bool`
+
+### Values
+
+
+
+## Standard I/O
+
+### Types
+
+### Procedures
+- `__output_string(s: str) -> u32`
+- `__output_error(s: str) -> u32`
+- `__read_from_input(buffer: [] u8) -> i32`
+    - `> 0` on success. Returns number of bytes
+    - `0` on no-input, but not error.
+    - `< 0` on error.
+
+### Values
+
+
+## OS
+
+### Types
+
+### Procedures
+- `__exit(code: i32) -> void`
+- `__sleep(milliseconds: i32) -> void`
+
+### Values
+
+
+## Time
+
+### Types
+
+### Procedures
+- `__time() -> u64`
+- `__time_localtime(time: u64, tm: ^core.time.Timestamp) -> void`
+- `__time_gmtime(time: u64, tm: ^core.time.Timestamp) -> void`
+- `__time_gmtime(tm: ^core.time.Timestamp) -> i64`
+- `__time_strftime(buf: [] u8, format: cstr, tm: ^core.time.Timestamp) -> u32`
+
+### Values
+
+
+## Threads
+
+### Types
+
+### Procedures
+- `__spawn_thread(id: i32, tls_base, stack_base: rawptr, func: (rawptr) -> void, data: rawptr) -> bool`
+- `__kill_thread(id: i32) -> i32`
+
+### Values
+
+
+## Processes
+
+### Types
+- `ProcessData`
+
+### Procedures
+- `__process_spawn(path: str, args: [] str, non_blocking_io: bool, starting_directory: str) -> ProcessData`
+- `__process_read(handle: ProcessData, buffer: [] u8) -> i32`
+- `__process_write(handle: ProcessData, buffer: [] u8) -> i32`
+- `__process_kill(handle: ProcessData) -> bool`
+- `__process_wait(handle: ProcessData) -> os.ProcessResult`
+- `__process_destroy(handle: ProcessData) -> void`
+
+### Values
+
+
+## Platform Support Flags
+
+### Types
+
+### Procedures
+
+### Values
+
+- `Supports_Files :: bool`
+- `Supports_Directories :: bool`
+- `Supports_Os :: bool`
+- `Supports_Processes :: bool`
+- `Supports_Time :: bool`
+- `Supports_Networking :: bool`
+- `Supports_Type_Info :: bool`
+- `Supports_Threads :: bool`