From 6f1cc8071c3ff49c5431cc8ad078d12883f91545 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 15:28:08 +0200 Subject: [PATCH 1/5] wasm: add foreign import and linking of wasm object files --- src/build_settings.cpp | 9 --------- src/check_decl.cpp | 7 +++++-- src/checker.cpp | 3 +-- src/linker.cpp | 14 ++++++++++++++ src/llvm_backend_utility.cpp | 6 +++++- src/parser.cpp | 3 +-- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c3b4f2506..9c93a5b69 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -860,15 +860,6 @@ gb_internal bool is_arch_x86(void) { return false; } -gb_internal bool allow_check_foreign_filepath(void) { - switch (build_context.metrics.arch) { - case TargetArch_wasm32: - case TargetArch_wasm64p32: - return false; - } - return true; -} - // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 883cfcba9..3c4a4b3de 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1178,9 +1178,12 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (foreign_library->LibraryName.paths.count >= 1) { module_name = foreign_library->LibraryName.paths[0]; } - name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + + if (!string_ends_with(module_name, str_lit(".o"))) { + name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + } } - + e->Procedure.is_foreign = true; e->Procedure.link_name = name; diff --git a/src/checker.cpp b/src/checker.cpp index 734659510..c3d2ae5eb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5000,9 +5000,8 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) { String file_str = op.value.value_string; file_str = string_trim_whitespace(file_str); - String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); if (ok) { diff --git a/src/linker.cpp b/src/linker.cpp index 371736743..34c0af7e5 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -85,6 +85,20 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (extra_linker_flags.len != 0) { lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags)); } + + for_array(i, e->LibraryName.paths) { + String lib = e->LibraryName.paths[i]; + + if (lib.len == 0) { + continue; + } + + if (!string_ends_with(lib, str_lit(".o"))) { + continue; + } + + inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib)); + } } if (build_context.metrics.os == TargetOs_orca) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 98ed0c57e..1165476be 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2029,7 +2029,11 @@ gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Ent GB_ASSERT(foreign_library->LibraryName.paths.count == 1); module_name = foreign_library->LibraryName.paths[0]; - + + if (string_ends_with(module_name, str_lit(".o"))) { + return; + } + if (string_starts_with(import_name, module_name)) { import_name = substring(import_name, module_name.len+WASM_MODULE_NAME_SEPARATOR.len, import_name.len); } diff --git a/src/parser.cpp b/src/parser.cpp index 583f4a57d..997d32fa5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5813,7 +5813,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node return false; } - if (collection_name.len > 0) { // NOTE(bill): `base:runtime` == `core:runtime` if (collection_name == "core") { @@ -5968,7 +5967,7 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas Token fp_token = fp->BasicLit.token; String file_str = string_trim_whitespace(string_value_from_token(f, fp_token)); String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); if (!ok) { From 4e18e1b19192ceaffcda33ddb3ddfcc1e041f4f3 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 15:29:24 +0200 Subject: [PATCH 2/5] wasi: make `os.open` work with absolute paths --- core/os/os_wasi.odin | 114 ++++++++++++++++++++++++++++++- core/sys/wasm/wasi/wasi_api.odin | 4 +- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index 8a1acb194..d38e73c79 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -38,6 +38,112 @@ _alloc_command_line_arguments :: proc() -> (args: []string) { return } +// WASI works with "preopened" directories, the environment retrieves directories +// (for example with `wasmtime --dir=. module.wasm`) and those given directories +// are the only ones accessible by the application. +// +// So in order to facilitate the `os` API (absolute paths etc.) we keep a list +// of the given directories and match them when needed (notably `os.open`). + +@(private) +Preopen :: struct { + fd: wasi.fd_t, + prefix: string, +} +@(private) +preopens: [dynamic]Preopen + +@(private, init) +init_preopens :: proc() { + + strip_prefixes :: proc(path: string) -> string { + path := path + loop: for len(path) > 0 { + switch { + case path[0] == '/': + path = path[1:] + case len(path) > 2 && path[0] == '.' && path[1] == '/': + path = path[2:] + case len(path) == 1 && path[0] == '.': + path = path[1:] + case: + break loop + } + } + return path + } + + loop: for fd := wasi.fd_t(3); ; fd += 1 { + desc, err := wasi.fd_prestat_get(fd) + #partial switch err { + case .BADF: break loop + case: panic("fd_prestat_get returned an unexpected error") + case .SUCCESS: + } + + switch desc.tag { + case .DIR: + buf := make([]byte, desc.dir.pr_name_len) or_else panic("could not allocate memory for filesystem preopens") + if err = wasi.fd_prestat_dir_name(fd, buf); err != .SUCCESS { + panic("could not get filesystem preopen dir name") + } + append(&preopens, Preopen{fd, strip_prefixes(string(buf))}) + } + } +} + +wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { + + prefix_matches :: proc(prefix, path: string) -> bool { + // Empty is valid for any relative path. + if len(prefix) == 0 && len(path) > 0 && path[0] == '/' { + return true + } + + if len(path) < len(prefix) { + return false + } + + if path[:len(prefix)] != prefix { + return false + } + + // Only match on full components. + i := len(prefix) + for i > 0 && prefix[i-1] == '/' { + i -= 1 + } + return path[i] == '/' + } + + path := path + for len(path) > 0 && path[0] == '/' { + path = path[1:] + } + + match: Preopen + #reverse for preopen in preopens { + if (match.fd == 0 || len(preopen.prefix) > len(match.prefix)) && prefix_matches(preopen.prefix, path) { + match = preopen + } + } + + if match.fd == 0 { + return 0, "", false + } + + relative := path[len(match.prefix):] + for len(relative) > 0 && relative[0] == '/' { + relative = relative[1:] + } + + if len(relative) == 0 { + relative = "." + } + + return match.fd, relative, true +} + write :: proc(fd: Handle, data: []byte) -> (int, Errno) { iovs := wasi.ciovec_t(data) n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) @@ -87,7 +193,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn if mode & O_SYNC == O_SYNC { fdflags += {.SYNC} } - fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags) + + dir_fd, relative, ok := wasi_match_preopen(path) + if !ok { + return INVALID_HANDLE, Errno(wasi.errno_t.BADF) + } + + fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags) return Handle(fd), Errno(err) } close :: proc(fd: Handle) -> Errno { diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index e9ceb4667..22abd8dc4 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -962,7 +962,7 @@ prestat_dir_t :: struct { } prestat_t :: struct { - tag: u8, + tag: preopentype_t, using u: struct { dir: prestat_dir_t, }, @@ -1158,7 +1158,7 @@ foreign wasi { /** * A buffer into which to write the preopened directory name. */ - path: string, + path: []byte, ) -> errno_t --- /** * Create a directory. From 10c68a89510c64c2693be8864dcae6bc54929811 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 15:41:48 +0200 Subject: [PATCH 3/5] wasm: support `vendor:stb/truetype` and `vendor:fontstash` --- core/os/os_wasi.odin | 2 +- vendor/fontstash/fontstash.odin | 20 +---- vendor/fontstash/fontstash_os.odin | 20 +++++ vendor/stb/lib/stb_truetype_wasm.o | Bin 0 -> 41425 bytes vendor/stb/src/Makefile | 4 + vendor/stb/src/stb_truetype_wasm.c | 46 +++++++++++ vendor/stb/truetype/stb_truetype.odin | 2 + vendor/stb/truetype/stb_truetype_wasm.odin | 92 +++++++++++++++++++++ 8 files changed, 168 insertions(+), 18 deletions(-) create mode 100644 vendor/fontstash/fontstash_os.odin create mode 100644 vendor/stb/lib/stb_truetype_wasm.o create mode 100644 vendor/stb/src/stb_truetype_wasm.c create mode 100644 vendor/stb/truetype/stb_truetype_wasm.odin diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index d38e73c79..edcf979ad 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -96,7 +96,7 @@ wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { prefix_matches :: proc(prefix, path: string) -> bool { // Empty is valid for any relative path. - if len(prefix) == 0 && len(path) > 0 && path[0] == '/' { + if len(prefix) == 0 && len(path) > 0 && path[0] != '/' { return true } diff --git a/vendor/fontstash/fontstash.odin b/vendor/fontstash/fontstash.odin index 9b556a04a..2c692db06 100644 --- a/vendor/fontstash/fontstash.odin +++ b/vendor/fontstash/fontstash.odin @@ -1,14 +1,14 @@ -//+build windows, linux, darwin //+vet !using-param package fontstash import "base:runtime" + import "core:log" -import "core:os" import "core:mem" import "core:math" import "core:strings" import "core:slice" + import stbtt "vendor:stb/truetype" // This is a port from Fontstash into odin - specialized for nanovg @@ -321,20 +321,6 @@ __AtlasAddWhiteRect :: proc(ctx: ^FontContext, w, h: int) -> bool { return true } -AddFontPath :: proc( - ctx: ^FontContext, - name: string, - path: string, -) -> int { - data, ok := os.read_entire_file(path) - - if !ok { - log.panicf("FONT: failed to read font at %s", path) - } - - return AddFontMem(ctx, name, data, true) -} - // push a font to the font stack // optionally init with ascii characters at a wanted size AddFontMem :: proc( @@ -1192,4 +1178,4 @@ EndState :: proc(using ctx: ^FontContext) { } __dirtyRectReset(ctx) } -} \ No newline at end of file +} diff --git a/vendor/fontstash/fontstash_os.odin b/vendor/fontstash/fontstash_os.odin new file mode 100644 index 000000000..6182573bd --- /dev/null +++ b/vendor/fontstash/fontstash_os.odin @@ -0,0 +1,20 @@ +//+build !js +package fontstash + +import "core:log" +import "core:os" + +AddFontPath :: proc( + ctx: ^FontContext, + name: string, + path: string, +) -> int { + data, ok := os.read_entire_file(path) + + if !ok { + log.panicf("FONT: failed to read font at %s", path) + } + + return AddFontMem(ctx, name, data, true) +} + diff --git a/vendor/stb/lib/stb_truetype_wasm.o b/vendor/stb/lib/stb_truetype_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..15c4fa0d5204ed2642368175e70ac3a9fc077d69 GIT binary patch literal 41425 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 literal 0 HcmV?d00001 diff --git a/vendor/stb/src/Makefile b/vendor/stb/src/Makefile index 1027cf8d2..6123a95fa 100644 --- a/vendor/stb/src/Makefile +++ b/vendor/stb/src/Makefile @@ -6,6 +6,10 @@ else all: unix endif +wasm: + mkdir -p ../lib + clang -c -Os --target=wasm32 -nostdlib stb_truetype_wasm.c -o ../lib/stb_truetype_wasm.o + unix: mkdir -p ../lib $(CC) -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c diff --git a/vendor/stb/src/stb_truetype_wasm.c b/vendor/stb/src/stb_truetype_wasm.c new file mode 100644 index 000000000..e0b1fdc77 --- /dev/null +++ b/vendor/stb/src/stb_truetype_wasm.c @@ -0,0 +1,46 @@ +#include + +void *stbtt_malloc(size_t size); +void stbtt_free(void *ptr); + +void stbtt_qsort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*)); + +double stbtt_floor(double x); +double stbtt_ceil(double x); +double stbtt_sqrt(double x); +double stbtt_pow(double x, double y); +double stbtt_fmod(double x, double y); +double stbtt_cos(double x); +double stbtt_acos(double x); +double stbtt_fabs(double x); + +unsigned long stbtt_strlen(const char *str); + +void *memcpy(void *dst, const void *src, size_t count); +void *memset(void *dst, int x, size_t count); + +#define STBRP_SORT stbtt_qsort +#define STBRP_ASSERT(condition) ((void)0) + +#define STBTT_malloc(x,u) ((void)(u),stbtt_malloc(x)) +#define STBTT_free(x,u) ((void)(u),stbtt_free(x)) + +#define STBTT_assert(condition) ((void)0) + +#define STBTT_ifloor(x) ((int) stbtt_floor(x)) +#define STBTT_iceil(x) ((int) stbtt_ceil(x)) +#define STBTT_sqrt(x) stbtt_sqrt(x) +#define STBTT_pow(x,y) stbtt_pow(x,y) +#define STBTT_fmod(x,y) stbtt_fmod(x,y) +#define STBTT_cos(x) stbtt_cos(x) +#define STBTT_acos(x) stbtt_acos(x) +#define STBTT_fabs(x) stbtt_fabs(x) +#define STBTT_strlen(x) stbtt_strlen(x) +#define STBTT_memcpy memcpy +#define STBTT_memset memset + +#define STB_RECT_PACK_IMPLEMENTATION +#include "stb_rect_pack.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index 876138c3a..6993cd2b7 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -17,6 +17,8 @@ when LIB != "" { } foreign import stbtt { LIB } +} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbtt "../lib/stb_truetype_wasm.o" } else { foreign import stbtt "system:stb_truetype" } diff --git a/vendor/stb/truetype/stb_truetype_wasm.odin b/vendor/stb/truetype/stb_truetype_wasm.odin new file mode 100644 index 000000000..2c22d45dc --- /dev/null +++ b/vendor/stb/truetype/stb_truetype_wasm.odin @@ -0,0 +1,92 @@ +//+build wasm32, wasm64p32 +package stb_truetype + +import "base:builtin" +import "base:intrinsics" +import "base:runtime" + +import "core:c" +import "core:math" +import "core:mem" +import "core:sort" + +@(require, linkage="strong", link_name="stbtt_malloc") +malloc :: proc "c" (size: uint) -> rawptr { + context = runtime.default_context() + ptr, _ := runtime.mem_alloc_non_zeroed(int(size)) + return raw_data(ptr) +} + +@(require, linkage="strong", link_name="stbtt_free") +free :: proc "c" (ptr: rawptr) { + context = runtime.default_context() + builtin.free(ptr) +} + +@(require, linkage="strong", link_name="stbtt_qsort") +qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: rawptr) -> i32) { + context = runtime.default_context() + + Inputs :: struct { + base: rawptr, + num: uint, + size: uint, + cmp: proc "c" (a, b: rawptr) -> i32, + } + inputs := Inputs{base, num, size, cmp} + + sort.sort({ + collection = &inputs, + len = proc(it: sort.Interface) -> int { + inputs := (^Inputs)(it.collection) + return int(inputs.num) + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + inputs := (^Inputs)(it.collection) + a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) + b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) + return inputs.cmp(a, b) < 0 + }, + swap = proc(it: sort.Interface, i, j: int) { + inputs := (^Inputs)(it.collection) + + a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) + b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) + + tmp := intrinsics.alloca(inputs.size, runtime.DEFAULT_ALIGNMENT) + intrinsics.mem_copy_non_overlapping(tmp, a, inputs.size) + intrinsics.mem_copy_non_overlapping(a, b, inputs.size) + intrinsics.mem_copy_non_overlapping(b, tmp, inputs.size) + }, + }) +} + +@(require, linkage="strong", link_name="stbtt_floor") +floor :: proc "c" (x: f64) -> f64 { return math.floor(x) } +@(require, linkage="strong", link_name="stbtt_ceil") +ceil :: proc "c" (x: f64) -> f64 { return math.ceil(x) } +@(require, linkage="strong", link_name="stbtt_sqrt") +sqrt :: proc "c" (x: f64) -> f64 { return math.sqrt(x) } +@(require, linkage="strong", link_name="stbtt_pow") +pow :: proc "c" (x, y: f64) -> f64 { return math.pow(x, y) } + +@(require, linkage="strong", link_name="stbtt_fmod") +fmod :: proc "c" (x, y: f64) -> f64 { + context = runtime.default_context(); + // NOTE: only called in the `stbtt_GetGlyphSDF` code path. + panic("`math.round` is broken on 32 bit targets, see #3856") +} + +@(require, linkage="strong", link_name="stbtt_cos") +cos :: proc "c" (x: f64) -> f64 { return math.cos(x) } +@(require, linkage="strong", link_name="stbtt_acos") +acos :: proc "c" (x: f64) -> f64 { return math.acos(x) } +@(require, linkage="strong", link_name="stbtt_fabs") +fabs :: proc "c" (x: f64) -> f64 { return math.abs(x) } + +@(require, linkage="strong", link_name="stbtt_strlen") +strlen :: proc "c" (str: cstring) -> c.ulong { return c.ulong(len(str)) } + +// NOTE: defined in runtime. +// void *memcpy(void *dst, const void *src, size_t count); +// void *memset(void *dst, int x, size_t count); From 0ef5191540cb750b512a1aea47732090b6a711ae Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 20:10:21 +0200 Subject: [PATCH 4/5] use slice.ptr_swap instead of alloca --- vendor/stb/truetype/stb_truetype_wasm.odin | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/vendor/stb/truetype/stb_truetype_wasm.odin b/vendor/stb/truetype/stb_truetype_wasm.odin index 2c22d45dc..cb516bc1d 100644 --- a/vendor/stb/truetype/stb_truetype_wasm.odin +++ b/vendor/stb/truetype/stb_truetype_wasm.odin @@ -8,6 +8,7 @@ import "base:runtime" import "core:c" import "core:math" import "core:mem" +import "core:slice" import "core:sort" @(require, linkage="strong", link_name="stbtt_malloc") @@ -33,10 +34,9 @@ qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: raw size: uint, cmp: proc "c" (a, b: rawptr) -> i32, } - inputs := Inputs{base, num, size, cmp} sort.sort({ - collection = &inputs, + collection = &Inputs{base, num, size, cmp}, len = proc(it: sort.Interface) -> int { inputs := (^Inputs)(it.collection) return int(inputs.num) @@ -53,10 +53,7 @@ qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: raw a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) - tmp := intrinsics.alloca(inputs.size, runtime.DEFAULT_ALIGNMENT) - intrinsics.mem_copy_non_overlapping(tmp, a, inputs.size) - intrinsics.mem_copy_non_overlapping(a, b, inputs.size) - intrinsics.mem_copy_non_overlapping(b, tmp, inputs.size) + slice.ptr_swap_non_overlapping(a, b, int(inputs.size)) }, }) } From 5399093050f192c13ea494e69455be8e1052e06c Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 20:17:24 +0200 Subject: [PATCH 5/5] make preopens a slice and remove bad current_dir --- core/os/os_wasi.odin | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin index edcf979ad..7b7fb4686 100644 --- a/core/os/os_wasi.odin +++ b/core/os/os_wasi.odin @@ -26,7 +26,6 @@ O_CLOEXEC :: 0x80000 stdin: Handle = 0 stdout: Handle = 1 stderr: Handle = 2 -current_dir: Handle = 3 args := _alloc_command_line_arguments() @@ -51,9 +50,9 @@ Preopen :: struct { prefix: string, } @(private) -preopens: [dynamic]Preopen +preopens: []Preopen -@(private, init) +@(init, private) init_preopens :: proc() { strip_prefixes :: proc(path: string) -> string { @@ -73,6 +72,7 @@ init_preopens :: proc() { return path } + dyn_preopens: [dynamic]Preopen loop: for fd := wasi.fd_t(3); ; fd += 1 { desc, err := wasi.fd_prestat_get(fd) #partial switch err { @@ -87,9 +87,10 @@ init_preopens :: proc() { if err = wasi.fd_prestat_dir_name(fd, buf); err != .SUCCESS { panic("could not get filesystem preopen dir name") } - append(&preopens, Preopen{fd, strip_prefixes(string(buf))}) + append(&dyn_preopens, Preopen{fd, strip_prefixes(string(buf))}) } } + preopens = dyn_preopens[:] } wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {