From 530aca76a22509eb65e8cbeedaa5a2463eb2f673 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Sat, 31 Oct 2020 19:48:14 -0700 Subject: [PATCH] Implement some more of the wasi API In order to test our WASI API implementation I've added `wasi-test-suite` as submodule. This submodule is only needed for testing. --- .gitmodules | 3 + emcc.py | 14 ++- emscripten.py | 3 +- src/generated_struct_info32.json | 61 ++++++++++- src/generated_struct_info64.json | 61 ++++++++++- src/library_sigs.js | 2 + src/library_syscall.js | 14 ++- src/library_wasi.js | 169 ++++++++++++++++++++++++++++--- src/shell.js | 2 +- src/struct_info.json | 58 ++++++++++- test/test_other.py | 79 +++++++++++++++ test/third_party/wasi-test-suite | 1 + tools/building.py | 1 + tools/gen_sig_info.py | 1 + 14 files changed, 445 insertions(+), 24 deletions(-) create mode 160000 test/third_party/wasi-test-suite diff --git a/.gitmodules b/.gitmodules index 391eaedc48e05..896f670b3caad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "test/third_party/googletest"] path = test/third_party/googletest url = https://github.com/google/googletest +[submodule "test/third_party/wasi-test-suite"] + path = test/third_party/wasi-test-suite + url = https://github.com/khronosproject/wasi-test-suite diff --git a/emcc.py b/emcc.py index ac1091db62a3f..17c869565cbd6 100755 --- a/emcc.py +++ b/emcc.py @@ -1137,7 +1137,7 @@ def package_files(options, target): if options.preload_files: # Preloading files uses --pre-js code that runs before the module is loaded. file_code = shared.check_call(cmd, stdout=PIPE).stdout - js_manipulation.add_files_pre_js(options.pre_js, file_code) + js_manipulation.add_files_pre_js(settings.PRE_JS_FILES, file_code) else: # Otherwise, we are embedding files, which does not require --pre-js code, # and instead relies on a static constrcutor to populate the filesystem. @@ -1311,9 +1311,6 @@ def run(args): if len(options.preload_files) or len(options.embed_files): linker_arguments += package_files(options, target) - settings.PRE_JS_FILES = [os.path.abspath(f) for f in options.pre_js] - settings.POST_JS_FILES = [os.path.abspath(f) for f in options.post_js] - if options.oformat == OFormat.OBJECT: logger.debug(f'link_to_object: {linker_arguments} -> {target}') building.link_to_object(linker_arguments, target) @@ -1891,7 +1888,7 @@ def phase_linker_setup(options, state, newargs): # Requesting both Wasm and Wasm2JS support settings.WASM2JS = 1 - if (options.oformat == OFormat.WASM or settings.PURE_WASI) and not settings.SIDE_MODULE: + if options.oformat == OFormat.WASM and not settings.SIDE_MODULE: # if the output is just a wasm file, it will normally be a standalone one, # as there is no JS. an exception are side modules, as we can't tell at # compile time whether JS will be involved or not - the main module may @@ -1902,6 +1899,10 @@ def phase_linker_setup(options, state, newargs): if settings.LZ4: settings.EXPORTED_RUNTIME_METHODS += ['LZ4'] + if settings.PURE_WASI: + settings.STANDALONE_WASM = 1 + settings.WASM_BIGINT = 1 + if settings.WASM2C: # wasm2c only makes sense with standalone wasm - there will be no JS, # just wasm and then C @@ -2906,6 +2907,9 @@ def get_full_import_name(name): # in standalone mode, crt1 will call the constructors from inside the wasm settings.REQUIRED_EXPORTS.append('__wasm_call_ctors') + settings.PRE_JS_FILES = [os.path.abspath(f) for f in options.pre_js] + settings.POST_JS_FILES = [os.path.abspath(f) for f in options.post_js] + return target, wasm_target diff --git a/emscripten.py b/emscripten.py index d4109061e77f0..691da8e591c70 100644 --- a/emscripten.py +++ b/emscripten.py @@ -77,8 +77,9 @@ def optimize_syscalls(declares): # without filesystem support, it doesn't matter what syscalls need settings.SYSCALLS_REQUIRE_FILESYSTEM = 0 else: + # TODO(sbc): Find a better way to identify wasi syscalls syscall_prefixes = ('__syscall_', 'fd_') - syscalls = {d for d in declares if d.startswith(syscall_prefixes)} + syscalls = {d for d in declares if d.startswith(syscall_prefixes) or d in ['path_open']} # check if the only filesystem syscalls are in: close, ioctl, llseek, write # (without open, etc.. nothing substantial can be done, so we can disable # extra filesystem support in that case) diff --git a/src/generated_struct_info32.json b/src/generated_struct_info32.json index a2dbe9c3ee50d..96aedcd63bd59 100644 --- a/src/generated_struct_info32.json +++ b/src/generated_struct_info32.json @@ -394,10 +394,49 @@ "__WASI_CLOCKID_PROCESS_CPUTIME_ID": 2, "__WASI_CLOCKID_REALTIME": 0, "__WASI_CLOCKID_THREAD_CPUTIME_ID": 3, + "__WASI_FDFLAGS_APPEND": 1, + "__WASI_FDFLAGS_DSYNC": 2, + "__WASI_FDFLAGS_NONBLOCK": 4, + "__WASI_FDFLAGS_RSYNC": 8, + "__WASI_FDFLAGS_SYNC": 16, "__WASI_FILETYPE_CHARACTER_DEVICE": 2, "__WASI_FILETYPE_DIRECTORY": 3, "__WASI_FILETYPE_REGULAR_FILE": 4, - "__WASI_FILETYPE_SYMBOLIC_LINK": 7 + "__WASI_FILETYPE_SYMBOLIC_LINK": 7, + "__WASI_OFLAGS_CREAT": 1, + "__WASI_OFLAGS_DIRECTORY": 2, + "__WASI_OFLAGS_EXCL": 4, + "__WASI_OFLAGS_TRUNC": 8, + "__WASI_PREOPENTYPE_DIR": 0, + "__WASI_RIGHTS_FD_ADVISE": 128, + "__WASI_RIGHTS_FD_ALLOCATE": 256, + "__WASI_RIGHTS_FD_DATASYNC": 1, + "__WASI_RIGHTS_FD_FDSTAT_SET_FLAGS": 8, + "__WASI_RIGHTS_FD_FILESTAT_GET": 2097152, + "__WASI_RIGHTS_FD_FILESTAT_SET_SIZE": 4194304, + "__WASI_RIGHTS_FD_FILESTAT_SET_TIMES": 8388608, + "__WASI_RIGHTS_FD_READ": 2, + "__WASI_RIGHTS_FD_READDIR": 16384, + "__WASI_RIGHTS_FD_SEEK": 4, + "__WASI_RIGHTS_FD_SYNC": 16, + "__WASI_RIGHTS_FD_TELL": 32, + "__WASI_RIGHTS_FD_WRITE": 64, + "__WASI_RIGHTS_PATH_CREATE_DIRECTORY": 512, + "__WASI_RIGHTS_PATH_CREATE_FILE": 1024, + "__WASI_RIGHTS_PATH_FILESTAT_GET": 262144, + "__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE": 524288, + "__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES": 1048576, + "__WASI_RIGHTS_PATH_LINK_SOURCE": 2048, + "__WASI_RIGHTS_PATH_LINK_TARGET": 4096, + "__WASI_RIGHTS_PATH_OPEN": 8192, + "__WASI_RIGHTS_PATH_READLINK": 32768, + "__WASI_RIGHTS_PATH_REMOVE_DIRECTORY": 33554432, + "__WASI_RIGHTS_PATH_RENAME_SOURCE": 65536, + "__WASI_RIGHTS_PATH_RENAME_TARGET": 131072, + "__WASI_RIGHTS_PATH_SYMLINK": 16777216, + "__WASI_RIGHTS_PATH_UNLINK_FILE": 67108864, + "__WASI_RIGHTS_POLL_FD_READWRITE": 134217728, + "__WASI_RIGHTS_SOCK_SHUTDOWN": 268435456 }, "structs": { "EmscriptenBatteryEvent": { @@ -1236,6 +1275,26 @@ "fs_rights_base": 8, "fs_rights_inheriting": 16 }, + "__wasi_filestat_t": { + "__size__": 64, + "atim": 40, + "ctim": 56, + "dev": 0, + "filetype": 16, + "ino": 8, + "mtim": 48, + "nlink": 24, + "size": 32 + }, + "__wasi_prestat_dir_t": { + "__size__": 4, + "pr_name_len": 0 + }, + "__wasi_prestat_t": { + "__size__": 8, + "pr_type": 0, + "u": 4 + }, "addrinfo": { "__size__": 32, "ai_addr": 20, diff --git a/src/generated_struct_info64.json b/src/generated_struct_info64.json index d2265582696af..5946eabc8ff11 100644 --- a/src/generated_struct_info64.json +++ b/src/generated_struct_info64.json @@ -394,10 +394,49 @@ "__WASI_CLOCKID_PROCESS_CPUTIME_ID": 2, "__WASI_CLOCKID_REALTIME": 0, "__WASI_CLOCKID_THREAD_CPUTIME_ID": 3, + "__WASI_FDFLAGS_APPEND": 1, + "__WASI_FDFLAGS_DSYNC": 2, + "__WASI_FDFLAGS_NONBLOCK": 4, + "__WASI_FDFLAGS_RSYNC": 8, + "__WASI_FDFLAGS_SYNC": 16, "__WASI_FILETYPE_CHARACTER_DEVICE": 2, "__WASI_FILETYPE_DIRECTORY": 3, "__WASI_FILETYPE_REGULAR_FILE": 4, - "__WASI_FILETYPE_SYMBOLIC_LINK": 7 + "__WASI_FILETYPE_SYMBOLIC_LINK": 7, + "__WASI_OFLAGS_CREAT": 1, + "__WASI_OFLAGS_DIRECTORY": 2, + "__WASI_OFLAGS_EXCL": 4, + "__WASI_OFLAGS_TRUNC": 8, + "__WASI_PREOPENTYPE_DIR": 0, + "__WASI_RIGHTS_FD_ADVISE": 128, + "__WASI_RIGHTS_FD_ALLOCATE": 256, + "__WASI_RIGHTS_FD_DATASYNC": 1, + "__WASI_RIGHTS_FD_FDSTAT_SET_FLAGS": 8, + "__WASI_RIGHTS_FD_FILESTAT_GET": 2097152, + "__WASI_RIGHTS_FD_FILESTAT_SET_SIZE": 4194304, + "__WASI_RIGHTS_FD_FILESTAT_SET_TIMES": 8388608, + "__WASI_RIGHTS_FD_READ": 2, + "__WASI_RIGHTS_FD_READDIR": 16384, + "__WASI_RIGHTS_FD_SEEK": 4, + "__WASI_RIGHTS_FD_SYNC": 16, + "__WASI_RIGHTS_FD_TELL": 32, + "__WASI_RIGHTS_FD_WRITE": 64, + "__WASI_RIGHTS_PATH_CREATE_DIRECTORY": 512, + "__WASI_RIGHTS_PATH_CREATE_FILE": 1024, + "__WASI_RIGHTS_PATH_FILESTAT_GET": 262144, + "__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE": 524288, + "__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES": 1048576, + "__WASI_RIGHTS_PATH_LINK_SOURCE": 2048, + "__WASI_RIGHTS_PATH_LINK_TARGET": 4096, + "__WASI_RIGHTS_PATH_OPEN": 8192, + "__WASI_RIGHTS_PATH_READLINK": 32768, + "__WASI_RIGHTS_PATH_REMOVE_DIRECTORY": 33554432, + "__WASI_RIGHTS_PATH_RENAME_SOURCE": 65536, + "__WASI_RIGHTS_PATH_RENAME_TARGET": 131072, + "__WASI_RIGHTS_PATH_SYMLINK": 16777216, + "__WASI_RIGHTS_PATH_UNLINK_FILE": 67108864, + "__WASI_RIGHTS_POLL_FD_READWRITE": 134217728, + "__WASI_RIGHTS_SOCK_SHUTDOWN": 268435456 }, "structs": { "EmscriptenBatteryEvent": { @@ -1236,6 +1275,26 @@ "fs_rights_base": 8, "fs_rights_inheriting": 16 }, + "__wasi_filestat_t": { + "__size__": 64, + "atim": 40, + "ctim": 56, + "dev": 0, + "filetype": 16, + "ino": 8, + "mtim": 48, + "nlink": 24, + "size": 32 + }, + "__wasi_prestat_dir_t": { + "__size__": 8, + "pr_name_len": 0 + }, + "__wasi_prestat_t": { + "__size__": 16, + "pr_type": 0, + "u": 8 + }, "addrinfo": { "__size__": 48, "ai_addr": 24, diff --git a/src/library_sigs.js b/src/library_sigs.js index 3308b2a84cd14..dc99c670dc9f5 100644 --- a/src/library_sigs.js +++ b/src/library_sigs.js @@ -1001,9 +1001,11 @@ sigs = { lineRGBA__sig: 'ipiiiiiiii', pixelRGBA__sig: 'ipiiiiii', proc_exit__sig: 'vi', + random_get__sig: 'ipp', rectangleColor__sig: 'ipiiiii', rectangleRGBA__sig: 'ipiiiiiiii', rotozoomSurface__sig: 'ppddi', + sched_yield__sig: 'i', setprotoent__sig: 'vi', strftime__sig: 'ppppp', strftime_l__sig: 'pppppp', diff --git a/src/library_syscall.js b/src/library_syscall.js index 45fb3ca65dead..bc276d08c0a9f 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -994,7 +994,18 @@ function wrapSyscallFunction(x, library, isWasi) { var canThrow = library[x + '__nothrow'] !== true; #endif - var pre = '', post = ''; + if (!library[x + '__deps']) library[x + '__deps'] = []; + +#if PURE_WASI + // In PURE_WASI mode we can't assume the wasm binary was built by emscripten + // and politely notify us on memory growth. Instead we have to check for + // possible memory growth on each syscall. + var pre = '\nif (!HEAPU8.byteLength) _emscripten_notify_memory_growth(0);\n' + library[x + '__deps'].push('emscripten_notify_memory_growth'); +#else + var pre = ''; +#endif + var post = ''; if (isVariadic) { pre += 'SYSCALLS.varargs = varargs;\n'; } @@ -1047,7 +1058,6 @@ function wrapSyscallFunction(x, library, isWasi) { } library[x] = eval('(' + t + ')'); - if (!library[x + '__deps']) library[x + '__deps'] = []; library[x + '__deps'].push('$SYSCALLS'); #if PTHREADS // Most syscalls need to happen on the main JS thread (e.g. because the diff --git a/src/library_wasi.js b/src/library_wasi.js index 1dbb9dbf692c3..c506cbb289817 100644 --- a/src/library_wasi.js +++ b/src/library_wasi.js @@ -37,6 +37,17 @@ var WasiLibrary = { #endif // MINIMAL_RUNTIME }, + sched_yield__nothrow: true, + sched_yield: function() { + return 0; + }, + + random_get__deps: ['getentropy'], + random_get: function(buf, buf_len) { + _getentropy(buf, buf_len); + return 0; + }, + $getEnvStrings__deps: ['$ENV', '$getExecutableName'], $getEnvStrings: function() { if (!getEnvStrings.strings) { @@ -49,6 +60,7 @@ var WasiLibrary = { var lang = 'C.UTF-8'; #endif var env = { +#if !PURE_WASI 'USER': 'web_user', 'LOGNAME': 'web_user', 'PATH': '/', @@ -56,6 +68,7 @@ var WasiLibrary = { 'HOME': '/home/web_user', 'LANG': lang, '_': getExecutableName() +#endif }; // Apply the user-provided values, if any. for (var x in ENV) { @@ -368,23 +381,155 @@ var WasiLibrary = { #endif }, + $wasiRightsToMuslOFlags: function(rights) { +#if SYSCALL_DEBUG + err('wasiRightsToMuslOFlags: ' + rights); +#endif + if ((rights & {{{ cDefs.__WASI_RIGHTS_FD_READ }}}) && (rights & {{{ cDefs.__WASI_RIGHTS_FD_WRITE }}})) { + return {{{ cDefs.O_RDWR }}}; + } + if (rights & {{{ cDefs.__WASI_RIGHTS_FD_READ }}}) { + return {{{ cDefs.O_RDONLY }}}; + } + if (rights & {{{ cDefs.__WASI_RIGHTS_FD_WRITE }}}) { + return {{{ cDefs.O_WRONLY }}}; + } + throw new FS.ErrnoError({{{ cDefs.EINVAL }}}); + }, + + $wasiOFlagsToMuslOFlags: function(oflags) { + var musl_oflags = 0; + if (oflags & {{{ cDefs.__WASI_OFLAGS_CREAT }}}) { + musl_oflags |= {{{ cDefs.O_CREAT }}}; + } + if (oflags & {{{ cDefs.__WASI_OFLAGS_TRUNC }}}) { + musl_oflags |= {{{ cDefs.O_TRUNC }}}; + } + if (oflags & {{{ cDefs.__WASI_OFLAGS_DIRECTORY }}}) { + musl_oflags |= {{{ cDefs.O_DIRECTORY }}}; + } + if (oflags & {{{ cDefs.__WASI_OFLAGS_EXCL }}}) { + musl_oflags |= {{{ cDefs.O_EXCL }}}; + } + return musl_oflags; + }, + +#if PURE_WASI + // preopen maps open file descriptors to pathname. + // In emscripten we already have a VFS layer so (for now) we expose the entire + // VFS to the wasi API. + $preopens: "{3: '/'}", + + path_open__sig: 'iiiiiiiiii', + path_open__deps: ['$wasiRightsToMuslOFlags', '$wasiOFlagsToMuslOFlags', '$preopens'], + path_open: function(fd, dirflags, path, path_len, oflags, + fs_rights_base, fs_rights_inherting, + fdflags, opened_fd) { + if (!(fd in preopens)) { + return {{{ cDefs.EBADF }}}; + } + var pathname = UTF8ToString(path, path_len); + var musl_oflags = wasiRightsToMuslOFlags(Number(fs_rights_base)); +#if SYSCALL_DEBUG + err("oflags1: 0x" + musl_oflags.toString(16)); +#endif + musl_oflags |= wasiOFlagsToMuslOFlags(Number(oflags)); +#if SYSCALL_DEBUG + err("oflags2: 0x" + musl_oflags.toString(16)); +#endif + var stream = FS.open(pathname, musl_oflags); + {{{ makeSetValue('opened_fd', '0', 'stream.fd', 'i32') }}}; + return 0; + }, + + fd_prestat_dir_name__deps: ['$preopens'], + fd_prestat_dir_name__sig: 'iiii', + fd_prestat_dir_name__nothrow: true, + fd_prestat_dir_name: function(fd, path, path_len) { + if (!(fd in preopens)) { + return {{{ cDefs.EBADF }}}; + } + var preopen = preopens[fd]; + stringToUTF8Array(preopens, HEAP8, path, path_len) + return 0; + }, + + fd_prestat_get__deps: ['$preopens'], + fd_prestat_get__sig: 'iii', + fd_prestat_get__nothrow: true, + fd_prestat_get: function(fd, stat_buf) { + if (!(fd in preopens)) { + return {{{ cDefs.EBADF }}}; + } + var preopen = preopens[fd]; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_prestat_t.pr_type, cDefs.__WASI_PREOPENTYPE_DIR, 'i8') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_prestat_t.u + C_STRUCTS.__wasi_prestat_dir_t.pr_name_len, 'preopen.length', 'i64') }}}; + return 0; + }, + + fd_fdstat_set_flags__sig: 'iii', + fd_fdstat_set_flags: function(fd, flags) { + // TODO(sbc): implement + var stream = SYSCALLS.getStreamFromFD(fd); + return 0; + }, + + fd_filestat_get__sig: 'iii', + fd_filestat_get: function(fd, stat_buf) { + // TODO(sbc): implement + var stream = SYSCALLS.getStreamFromFD(fd); + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.dev, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.ino, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.filetype, '0', 'i8') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.nlink, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.size, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.atim, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.mtim, '0', 'i64') }}}; + {{{ makeSetValue('stat_buf', C_STRUCTS.__wasi_filestat_t.ctim, '0', 'i64') }}}; + return 0; + }, +#endif + +#if PURE_WASI + fd_fdstat_get__deps: ['$preopens'], +#endif fd_fdstat_get: function(fd, pbuf) { + var rightsBase = 0; + var rightsInheriting = 0; + var flags = 0; +#if PURE_WASI + if (fd in preopens) { + var type = {{{ cDefs.__WASI_FILETYPE_DIRECTORY }}}; + rightsBase = {{{ cDefs.__WASI_RIGHTS_PATH_CREATE_FILE | + cDefs.__WASI_RIGHTS_PATH_OPEN }}}; + rightsInheriting = {{{ cDefs.__WASI_RIGHTS_FD_READ | + cDefs.__WASI_RIGHTS_FD_WRITE }}} + } else +#endif + { #if SYSCALLS_REQUIRE_FILESYSTEM - var stream = SYSCALLS.getStreamFromFD(fd); - // All character devices are terminals (other things a Linux system would - // assume is a character device, like the mouse, we have special APIs for). - var type = stream.tty ? {{{ cDefs.__WASI_FILETYPE_CHARACTER_DEVICE }}} : - FS.isDir(stream.mode) ? {{{ cDefs.__WASI_FILETYPE_DIRECTORY }}} : - FS.isLink(stream.mode) ? {{{ cDefs.__WASI_FILETYPE_SYMBOLIC_LINK }}} : - {{{ cDefs.__WASI_FILETYPE_REGULAR_FILE }}}; + var stream = SYSCALLS.getStreamFromFD(fd); + // All character devices are terminals (other things a Linux system would + // assume is a character device, like the mouse, we have special APIs for). + var type = stream.tty ? {{{ cDefs.__WASI_FILETYPE_CHARACTER_DEVICE }}} : + FS.isDir(stream.mode) ? {{{ cDefs.__WASI_FILETYPE_DIRECTORY }}} : + FS.isLink(stream.mode) ? {{{ cDefs.__WASI_FILETYPE_SYMBOLIC_LINK }}} : + {{{ cDefs.__WASI_FILETYPE_REGULAR_FILE }}}; #else - // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 - var type = fd == 1 || fd == 2 ? {{{ cDefs.__WASI_FILETYPE_CHARACTER_DEVICE }}} : abort(); + // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 + var type = fd == 0 || fd == 1 || fd == 2 ? {{{ cDefs.__WASI_FILETYPE_CHARACTER_DEVICE }}} : abort(); + if (fd == 0) { + rightsBase = {{{ cDefs.__WASI_RIGHTS_FD_READ }}}; + } else if (fd == 1 || fd == 2) { + rightsBase = {{{ cDefs.__WASI_RIGHTS_FD_WRITE }}}; + } + flags = {{{ cDefs.__WASI_FDFLAGS_APPEND }}}; #endif + } {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_filetype, 'type', 'i8') }}}; - // TODO {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_flags, '?', 'i16') }}}; - // TODO {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_rights_base, '?', 'i64') }}}; - // TODO {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_rights_inheriting, '?', 'i64') }}}; + {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_flags, 'flags', 'i16') }}}; + {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_rights_base, 'rightsBase', 'i64') }}}; + {{{ makeSetValue('pbuf', C_STRUCTS.__wasi_fdstat_t.fs_rights_inheriting, 'rightsInheriting', 'i64') }}}; return 0; }, diff --git a/src/shell.js b/src/shell.js index 0c21ed2b068dd..b007cce288047 100644 --- a/src/shell.js +++ b/src/shell.js @@ -218,7 +218,7 @@ if (ENVIRONMENT_IS_NODE) { #include "node_shell_read.js" - if (process.argv.length > 1) { + if (!Module['thisProgram'] && process.argv.length > 1) { thisProgram = process.argv[1].replace(/\\/g, '/'); } diff --git a/src/struct_info.json b/src/struct_info.json index 0466d2d93ee8d..f4c556e890c50 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -954,6 +954,23 @@ "fs_flags", "fs_rights_base", "fs_rights_inheriting" + ], + "__wasi_prestat_t": [ + "pr_type", + "u" + ], + "__wasi_prestat_dir_t": [ + "pr_name_len" + ], + "__wasi_filestat_t": [ + "dev", + "ino", + "filetype", + "nlink", + "size", + "atim", + "mtim", + "ctim" ] }, "defines": [ @@ -964,7 +981,46 @@ "__WASI_CLOCKID_REALTIME", "__WASI_CLOCKID_MONOTONIC", "__WASI_CLOCKID_PROCESS_CPUTIME_ID", - "__WASI_CLOCKID_THREAD_CPUTIME_ID" + "__WASI_CLOCKID_THREAD_CPUTIME_ID", + "__WASI_FDFLAGS_APPEND", + "__WASI_FDFLAGS_DSYNC", + "__WASI_FDFLAGS_NONBLOCK", + "__WASI_FDFLAGS_RSYNC", + "__WASI_FDFLAGS_SYNC", + "__WASI_OFLAGS_CREAT", + "__WASI_OFLAGS_DIRECTORY", + "__WASI_OFLAGS_EXCL", + "__WASI_OFLAGS_TRUNC", + "__WASI_RIGHTS_FD_DATASYNC", + "__WASI_RIGHTS_FD_READ", + "__WASI_RIGHTS_FD_SEEK", + "__WASI_RIGHTS_FD_FDSTAT_SET_FLAGS", + "__WASI_RIGHTS_FD_SYNC", + "__WASI_RIGHTS_FD_TELL", + "__WASI_RIGHTS_FD_WRITE", + "__WASI_RIGHTS_FD_ADVISE", + "__WASI_RIGHTS_FD_ALLOCATE", + "__WASI_RIGHTS_PATH_CREATE_DIRECTORY", + "__WASI_RIGHTS_PATH_CREATE_FILE", + "__WASI_RIGHTS_PATH_LINK_SOURCE", + "__WASI_RIGHTS_PATH_LINK_TARGET", + "__WASI_RIGHTS_PATH_OPEN", + "__WASI_RIGHTS_FD_READDIR", + "__WASI_RIGHTS_PATH_READLINK", + "__WASI_RIGHTS_PATH_RENAME_SOURCE", + "__WASI_RIGHTS_PATH_RENAME_TARGET", + "__WASI_RIGHTS_PATH_FILESTAT_GET", + "__WASI_RIGHTS_PATH_FILESTAT_SET_SIZE", + "__WASI_RIGHTS_PATH_FILESTAT_SET_TIMES", + "__WASI_RIGHTS_FD_FILESTAT_GET", + "__WASI_RIGHTS_FD_FILESTAT_SET_SIZE", + "__WASI_RIGHTS_FD_FILESTAT_SET_TIMES", + "__WASI_RIGHTS_PATH_SYMLINK", + "__WASI_RIGHTS_PATH_REMOVE_DIRECTORY", + "__WASI_RIGHTS_PATH_UNLINK_FILE", + "__WASI_RIGHTS_POLL_FD_READWRITE", + "__WASI_RIGHTS_SOCK_SHUTDOWN", + "__WASI_PREOPENTYPE_DIR" ] }, { diff --git a/test/test_other.py b/test/test_other.py index d2ed532e1470b..fb88902dcd359 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -13333,3 +13333,82 @@ def test_missing_struct_info(self): ''') err = self.expect_fail([EMCC, test_file('hello_world.c'), '--js-library=lib.js']) self.assertContained('Error: Missing C define Foo! If you just added it to struct_info.json, you need to run ./tools/gen_struct_info.py', err) + + def run_wasi_test_suite_test(self, name): + if not os.path.exists(path_from_root('test/third_party/wasi-test-suite')): + self.fail('wasi-testsuite not found; run `git submodule update --init`') + self.node_args += shared.node_bigint_flags() + wasm = path_from_root('test', 'third_party', 'wasi-test-suite', name + '.wasm') + with open(path_from_root('test', 'third_party', 'wasi-test-suite', name + '.json')) as f: + config = json.load(f) + exit_code = config.get('exitCode', 0) + args = config.get('args', []) + env = config.get('env', []) + if env: + env = [f'ENV["{key}"] = "{value}";' for key, value in env.items()] + env = '\n'.join(env) + create_file('env.js', 'Module.preRun = () => { %s };' % env) + self.emcc_args.append('--pre-js=env.js') + self.run_process([EMCC, '-Wno-experimental', '--post-link', '-g', + '-sPURE_WASI', '-lnodefs.js', '-lnoderawfs.js', + wasm, '-o', name + '.js'] + self.get_emcc_args(main_file=True)) + + output = self.run_js(name + '.js', args=args, assert_returncode=exit_code) + if 'stdout' in config: + self.assertContained(config['stdout'], output) + + @requires_node + def test_wasi_std_env_args(self): + create_file('pre.js', 'Module["thisProgram"] = "std_env_args.wasm"') + self.emcc_args += ['--pre-js', 'pre.js'] + self.run_wasi_test_suite_test('std_env_args') + + @requires_node + def test_wasi_std_env_vars(self): + self.run_wasi_test_suite_test('std_env_vars') + + @requires_node + def test_wasi_std_io_stdout(self): + self.run_wasi_test_suite_test('std_io_stdout') + + @requires_node + def test_wasi_std_io_stderr(self): + self.run_wasi_test_suite_test('std_io_stderr') + + @requires_node + def test_wasi_clock_res_get(self): + self.run_wasi_test_suite_test('wasi_clock_res_get') + + @requires_node + def test_wasi_clock_time_get(self): + self.run_wasi_test_suite_test('wasi_clock_time_get') + + @requires_node + def test_wasi_fd_fdstat_get(self): + self.run_wasi_test_suite_test('wasi_fd_fdstat_get') + + @requires_node + def test_wasi_wasi_fd_write_file(self): + self.run_wasi_test_suite_test('wasi_fd_write_file') + with open('new_file') as f: + self.assertEqual(f.read(), 'new_file') + + @requires_node + def test_wasi_wasi_fd_write_stdout(self): + self.run_wasi_test_suite_test('wasi_fd_write_stdout') + + @requires_node + def test_wasi_wasi_fd_write_stderr(self): + self.run_wasi_test_suite_test('wasi_fd_write_stderr') + + @requires_node + def test_wasi_proc_exit(self): + self.run_wasi_test_suite_test('wasi_proc_exit') + + @requires_node + def test_wasi_random_get(self): + self.run_wasi_test_suite_test('wasi_random_get') + + @requires_node + def test_wasi_sched_yield(self): + self.run_wasi_test_suite_test('wasi_sched_yield') diff --git a/test/third_party/wasi-test-suite b/test/third_party/wasi-test-suite new file mode 160000 index 0000000000000..ac9371207fc61 --- /dev/null +++ b/test/third_party/wasi-test-suite @@ -0,0 +1 @@ +Subproject commit ac9371207fc61972a21d7d086074ca690a99d14d diff --git a/tools/building.py b/tools/building.py index 8996f2cc9a9de..9125335950d0f 100644 --- a/tools/building.py +++ b/tools/building.py @@ -812,6 +812,7 @@ def metadce(js_file, wasm_file, minify_whitespace, debug_info): 'proc_exit', 'clock_res_get', 'clock_time_get', + 'path_open', } for item in graph: if 'import' in item and item['import'][1][1:] in WASI_IMPORTS: diff --git a/tools/gen_sig_info.py b/tools/gen_sig_info.py index 172ffc7d3f48b..b0a9e85c2da45 100755 --- a/tools/gen_sig_info.py +++ b/tools/gen_sig_info.py @@ -110,6 +110,7 @@ 'fd_fdstat_get', 'args_get', 'args_sizes_get', + 'random_get', }