diff --git a/emcc.py b/emcc.py index 5b0e113214bde..ba102cd9125da 100755 --- a/emcc.py +++ b/emcc.py @@ -1775,6 +1775,12 @@ def default_setting(name, new_default): settings.FETCH = 1 settings.JS_LIBRARIES.append((0, 'library_asmfs.js')) + if settings.WASMFS: + state.forced_stdlibs.append('libwasmfs') + settings.FILESYSTEM = 0 + settings.SYSCALLS_REQUIRE_FILESYSTEM = 0 + settings.JS_LIBRARIES.append((0, 'library_wasmfs.js')) + # Explicitly drop linking in a malloc implementation if program is not using any dynamic allocation calls. if not settings.USES_DYNAMIC_ALLOC: settings.MALLOC = 'none' diff --git a/src/library_wasmfs.js b/src/library_wasmfs.js new file mode 100644 index 0000000000000..0ae01069a8a47 --- /dev/null +++ b/src/library_wasmfs.js @@ -0,0 +1,17 @@ +var WasmfsLibrary = { + $wasmfsBuffers: [null, [], []], + emscripten_wasmfs_printbuffer__deps: ['$wasmfsBuffers'], + emscripten_wasmfs_printbuffer: function(stream, ptr, len) { + for (var j = 0; j < len; j++) { + if (HEAPU8[ptr+j] === 0 || HEAPU8[ptr+j] === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(wasmfsBuffers[stream], 0)); + wasmfsBuffers[stream].length = 0; + } else { + wasmfsBuffers[stream].push(HEAPU8[ptr+j]); + } + } + + } +} + +mergeInto(LibraryManager.library, WasmfsLibrary); diff --git a/src/modules.js b/src/modules.js index a1167ba9bf44d..1d4433c71b0cf 100644 --- a/src/modules.js +++ b/src/modules.js @@ -110,6 +110,8 @@ var LibraryManager = { } libraries.push('library_noderawfs.js'); } + } else { + libraries.push('library_wasmfs.js'); } // Additional JS libraries (without AUTO_JS_LIBRARIES, link to these explicitly via -lxxx.js) diff --git a/src/preamble.js b/src/preamble.js index e8401ca48e82d..f2bfbf9d838dd 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -624,7 +624,7 @@ function abort(what) { #include "memoryprofiler.js" -#if ASSERTIONS && !('$FS' in addedLibraryItems) && !ASMFS +#if ASSERTIONS && !('$FS' in addedLibraryItems) && !ASMFS && !WASMFS // show errors on likely calls to FS when it was not included var FS = { error: function() { diff --git a/src/settings.js b/src/settings.js index 8b961b9532171..af7aee12731a2 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1635,6 +1635,12 @@ var FETCH = 0; // [link] var ASMFS = 0; +// ATTENTION [WIP]: Experimental feature. Please use at your own risk. +// This will eventually replace the current JS file system implementation. +// If set to 1, uses new filesystem implementation. +// [link] +var WASMFS = 0; + // If set to 1, embeds all subresources in the emitted file as base64 string // literals. Embedded subresources may include (but aren't limited to) wasm, // asm.js, and static memory initialization code. diff --git a/system/lib/wasmfs/wasmfs.cpp b/system/lib/wasmfs/wasmfs.cpp new file mode 100644 index 0000000000000..dc4e8e75e1bee --- /dev/null +++ b/system/lib/wasmfs/wasmfs.cpp @@ -0,0 +1,54 @@ +// Copyright 2021 The Emscripten Authors. All rights reserved. +// Emscripten is available under two separate licenses, the MIT license and the +// University of Illinois/NCSA Open Source License. Both these licenses can be +// found in the LICENSE file. +// wasmfs.cpp will implement a new file system that replaces the existing JS filesystem. +// Current Status: Work in Progress. +// See https://github.com/emscripten-core/emscripten/issues/15041. + +#include +#include +#include +#include + +extern "C" { + +int emscripten_wasmfs_printbuffer(__wasi_fd_t fd, const uint8_t* ptr, __wasi_size_t len); + +__wasi_errno_t __wasi_fd_write( + __wasi_fd_t fd, const __wasi_ciovec_t* iovs, size_t iovs_len, __wasi_size_t* nwritten) { + // FD 1 = STDOUT and FD 2 = STDERR. + // Temporary hardcoding of filedescriptor values. + // TODO: May not want to proxy stderr (fd == 2) to the main thread. + // This will not not show in HTML, a console.warn in a worker is suffficient. + // This would be a change from the current FS. + if (fd == 1 || fd == 2) { + __wasi_size_t num = 0; + for (size_t i = 0; i < iovs_len; i++) { + const uint8_t* ptr = iovs[i].buf; + __wasi_size_t len = iovs[i].buf_len; + emscripten_wasmfs_printbuffer(fd, ptr, len); + num += len; + } + *nwritten = num; + } + return 0; +} + +__wasi_errno_t __wasi_fd_seek( + __wasi_fd_t fd, __wasi_filedelta_t offset, __wasi_whence_t whence, __wasi_filesize_t* newoffset) { + emscripten_console_log("__wasi_fd_seek has been temporarily stubbed and is inert"); + abort(); +} + +__wasi_errno_t __wasi_fd_close(__wasi_fd_t fd) { + emscripten_console_log("__wasi_fd_close has been temporarily stubbed and is inert"); + abort(); +} + +__wasi_errno_t __wasi_fd_read( + __wasi_fd_t fd, const __wasi_iovec_t* iovs, size_t iovs_len, __wasi_size_t* nread) { + emscripten_console_log("__wasi_fd_read has been temporarily stubbed and is inert"); + abort(); +} +} diff --git a/tests/common.py b/tests/common.py index b60ba67085b1b..b153a7bc9d5f3 100644 --- a/tests/common.py +++ b/tests/common.py @@ -541,6 +541,7 @@ def verify_es5(self, filename): es_check_env = os.environ.copy() es_check_env['PATH'] = os.path.dirname(config.NODE_JS[0]) + os.pathsep + es_check_env['PATH'] try: + # Comment out --quiet if more detailed error logging is needed shared.run_process(es_check + ['es5', os.path.abspath(filename), '--quiet'], stderr=PIPE, env=es_check_env) except subprocess.CalledProcessError as e: print(e.stderr) diff --git a/tests/test_core.py b/tests/test_core.py index d940f662de9ba..d99e02c18f1e4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -139,6 +139,17 @@ def can_do_standalone(self): '-fsanitize=address' not in self.emcc_args +def also_with_wasmfs(func): + def decorated(self): + func(self) + print('wasmfs') + if self.get_setting('STANDALONE_WASM'): + self.skipTest("test currently cannot run both with WASMFS and STANDALONE_WASM") + self.set_setting('WASMFS') + func(self) + return decorated + + # Impure means a test that cannot run in a wasm VM yet, as it is not 100% # standalone. We can still run them with the JS code though. def also_with_standalone_wasm(wasm2c=False, impure=False): @@ -308,6 +319,7 @@ def get_bullet_library(self, use_cmake): cache_name_extra=configure_commands[0]) @also_with_standalone_wasm() + @also_with_wasmfs def test_hello_world(self): self.do_core_test('test_hello_world.c') # must not emit this unneeded internal thing diff --git a/tools/system_libs.py b/tools/system_libs.py index e05df653023c1..e354283f881a1 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -1285,6 +1285,16 @@ def can_build(self): return True +class libwasmfs(MTLibrary): + name = 'libwasmfs' + + def get_files(self): + return [utils.path_from_root('system/lib/wasmfs/wasmfs.cpp')] + + def can_build(self): + return settings.WASMFS + + class libhtml5(Library): name = 'libhtml5'