Platform layer code was scattered and undocumented what was expected.
This has been remedied, and a good baseline has been defined.
^stdio.print_stream
|> io.buffer_stream_to_str()
- |> runtime.__output_string();
+ |> runtime.platform.__output_string();
^stdio.print_stream |> io.stream_flush();
}
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}));
}
}
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");
}
#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;
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];
package core.net
+#if !runtime.platform.Supports_Networking {
+ #error "Cannot include this file. Platform not supported.";
+}
+
use core
Socket :: struct {
}
};
+//
+// 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 ---
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 {
+++ /dev/null
-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);
- },
-};
-
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;
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);
+}
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;
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};
}
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 {
}
}
-#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 ---
-}
-
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.
raw_free(alloc.heap_allocator, __tls_base);
core.thread.__exited(id);
}
-}
+}
\ No newline at end of file
+++ /dev/null
-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
-}
+++ /dev/null
-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
-}
--- /dev/null
+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
+}
--- /dev/null
+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);
+ },
+};
+
--- /dev/null
+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
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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);
+}
--- /dev/null
+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();
+}
+
--- /dev/null
+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" ---
+
+
+
--- /dev/null
+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;
+}
+++ /dev/null
-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();
-}
-
#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 {
#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
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);
}
//
__atomic_wait(^t.id, t.id);
} else {
// To not completely kill the CPU.
- runtime.__sleep(1);
+ runtime.platform.__sleep(1);
}
}
}
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;
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 {
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.";
}
}
}
-#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 ---
- }
-}
-
+++ /dev/null
-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
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-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);
-}
+++ /dev/null
-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" ---
-
-
-
+++ /dev/null
-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;
-}
--- /dev/null
+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`