Skip to content

Commit f55f018

Browse files
authored
WasmFS: Implement FS.cwd (#19452)
This both adds JS API support for it, and internal support in JS libraries that use it via PATH_FS, such as SDL1. Also switch the JS API methods to hold on to their allocations rather than return them to JS and expect JS to call free. This may use more memory in some cases, but in others will be more efficient, and it is simpler.
1 parent c5e53c4 commit f55f018

File tree

8 files changed

+40
-12
lines changed

8 files changed

+40
-12
lines changed

emcc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,7 @@ def phase_linker_setup(options, state, newargs):
23342334
'_wasmfs_readdir_start',
23352335
'_wasmfs_readdir_get',
23362336
'_wasmfs_readdir_finish',
2337+
'_wasmfs_get_cwd',
23372338
]
23382339

23392340
if settings.FETCH and final_suffix in EXECUTABLE_ENDINGS:

src/library_path.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,15 @@ mergeInto(LibraryManager.library, {
8282
},
8383
// The FS-using parts are split out into a separate object, so simple path
8484
// usage does not require the FS.
85-
$PATH_FS__deps: ['$PATH', '$FS'],
85+
$PATH_FS__deps: [
86+
'$PATH',
87+
'$FS',
88+
#if WASMFS
89+
// In WasmFS, FS.cwd() is implemented via a call into wasm, so we need to
90+
// add a dependency on that.
91+
'_wasmfs_get_cwd',
92+
#endif
93+
],
8694
$PATH_FS: {
8795
resolve: function() {
8896
var resolvedPath = '',

src/library_wasmfs.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ FS.createPreloadedFile = FS_createPreloadedFile;
2626
#if FORCE_FILESYSTEM
2727
'$FS_modeStringToFlags',
2828
'malloc',
29+
'free',
2930
#endif
3031
],
3132
$FS : {
@@ -90,18 +91,15 @@ FS.createPreloadedFile = FS_createPreloadedFile;
9091
ret = UTF8ArrayToString(ret, 0);
9192
}
9293

93-
_free(buf);
9494
return ret;
9595
},
96-
cwd: () => {
97-
// TODO: Remove dependency on FS.cwd().
98-
// User code should not be using FS.cwd().
99-
// For file preloading, cwd should be '/' to begin with.
100-
return '/';
101-
},
10296

10397
#if FORCE_FILESYSTEM
10498
// Full JS API support
99+
100+
cwd: () => {
101+
return UTF8ToString(__wasmfs_get_cwd());
102+
},
105103
mkdir: (path, mode) => {
106104
return withStackSave(() => {
107105
mode = mode !== undefined ? mode : 511 /* 0777 */;

system/lib/wasmfs/js_api.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
#include "file.h"
1212
#include "paths.h"
1313

14+
// Some APIs return data using a thread-local allocation that is never freed.
15+
// This is simpler and more efficient as it avoids the JS caller needing to free
16+
// the allocation (which would have both the overhead of free, and also of a
17+
// call back into wasm), but on the other hand it does mean more memory may be
18+
// used. This seems a reasonable tradeoff as heavy workloads should ideally
19+
// avoid the JS API anyhow.
20+
1421
using namespace wasmfs;
1522

1623
extern "C" {
@@ -20,7 +27,6 @@ __wasi_fd_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend);
2027
// Copy the file specified by the pathname into JS.
2128
// Return a pointer to the JS buffer in HEAPU8.
2229
// The buffer will also contain the file length.
23-
// Caller must free the returned pointer.
2430
void* _wasmfs_read_file(char* path) {
2531
static_assert(sizeof(off_t) == 8, "File offset type must be 64-bit");
2632

@@ -36,7 +42,11 @@ void* _wasmfs_read_file(char* path) {
3642
// first 8 bytes. The remaining bytes will contain the buffer contents. This
3743
// allows the caller to use HEAPU8.subarray(buf + 8, buf + 8 + length).
3844
off_t size = file.st_size;
39-
uint8_t* result = (uint8_t*)malloc(size + sizeof(size));
45+
46+
static thread_local void* buffer = nullptr;
47+
buffer = realloc(buffer, size + sizeof(size));
48+
49+
auto* result = (uint8_t*)buffer;
4050
*(off_t*)result = size;
4151

4252
int fd = open(path, O_RDONLY);
@@ -246,4 +256,11 @@ void _wasmfs_readdir_finish(struct wasmfs_readdir_state* state) {
246256
free(state);
247257
}
248258

259+
char* _wasmfs_get_cwd(void) {
260+
// TODO: PATH_MAX is 4K atm, so it might be good to reduce this somehow.
261+
static thread_local void* path = nullptr;
262+
path = realloc(path, PATH_MAX);
263+
return getcwd((char*)path, PATH_MAX);
264+
}
265+
249266
} // extern "C"

test/browser/test_sdl_image.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <stdlib.h>
1515

1616
int testImage(SDL_Surface* screen, const char* fileName) {
17+
printf("testImage: %s\n", fileName);
18+
1719
SDL_Surface *image = IMG_Load(fileName);
1820
if (!image)
1921
{
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
163957
1+
163973

test/test_browser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ def test_sdl_surface_lock_opts(self):
749749
# Test Emscripten-specific extensions to optimize SDL_LockSurface and SDL_UnlockSurface.
750750
self.btest('hello_world_sdl.cpp', reference='htmltest.png', args=['-DTEST_SDL_LOCK_OPTS', '-lSDL', '-lGL'])
751751

752+
@also_with_wasmfs
752753
def test_sdl_image(self):
753754
# load an image file, get pixel data. Also O2 coverage for --preload-file, and memory-init
754755
shutil.copyfile(test_file('screenshot.jpg'), 'screenshot.jpg')
@@ -761,7 +762,6 @@ def test_sdl_image(self):
761762
'-O2', '-lSDL', '-lGL',
762763
'--preload-file', dest, '-DSCREENSHOT_DIRNAME="' + dirname + '"', '-DSCREENSHOT_BASENAME="' + basename + '"', '--use-preload-plugins'
763764
])
764-
return
765765

766766
@also_with_wasmfs
767767
def test_sdl_image_jpeg(self):
@@ -3722,6 +3722,7 @@ def test_dynamic_link_many(self):
37223722
self.run_process([EMCC, 'side2.c', '-sSIDE_MODULE', '-o', 'side2.wasm'])
37233723
self.btest_exit(self.in_dir('main.c'), args=['-sMAIN_MODULE=2', 'side1.wasm', 'side2.wasm'])
37243724

3725+
@requires_threads
37253726
def test_dynamic_link_pthread_many(self):
37263727
# Test asynchronously loading two side modules during startup
37273728
# They should always load in the same order

test/test_core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5680,6 +5680,7 @@ def test_getdents64_special_cases(self):
56805680
def test_getcwd_with_non_ascii_name(self):
56815681
self.do_run_in_out_file_test('fs/test_getcwd_with_non_ascii_name.cpp')
56825682

5683+
@no_wasmfs('no support for /proc/self/fd/, see https://github.com/emscripten-core/emscripten/issues/19430')
56835684
def test_proc_self_fd(self):
56845685
self.do_run_in_out_file_test('fs/test_proc_self_fd.c')
56855686

0 commit comments

Comments
 (0)