diff --git a/crates/wasi-common/js-polyfill/Cargo.lock b/crates/wasi-common/js-polyfill/Cargo.lock new file mode 100644 index 000000000000..144085074ea1 --- /dev/null +++ b/crates/wasi-common/js-polyfill/Cargo.lock @@ -0,0 +1,408 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + +[[package]] +name = "atty" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpu-time" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e393a7668fe1fad3075085b86c781883000b4ede868f43627b34a87c8b7ded" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "cvt" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac344c7efccb80cd25bc61b2170aec26f2f693fd40e765a539a1243db48c71" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "filetime" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff6d4dab0aa0c8e6346d46052e93b13a16cf847b54ed357087c35011048cc7d" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "num" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" +dependencies = [ + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "polyfill" +version = "0.1.0" +dependencies = [ + "wasi-common", +] + +[[package]] +name = "proc-macro2" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" + +[[package]] +name = "wasi-common" +version = "0.7.0" +dependencies = [ + "anyhow", + "cfg-if", + "cpu-time", + "filetime", + "getrandom", + "lazy_static", + "libc", + "log", + "num", + "thiserror", + "wasi-common-cbindgen", + "wig", + "winapi", + "winx", + "yanix", +] + +[[package]] +name = "wasi-common-cbindgen" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wast" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233648f540f07fce9b972436f2fbcae8a750c1121b6d32d949e1a44b4d9fc7b1" +dependencies = [ + "leb128", +] + +[[package]] +name = "wig" +version = "0.7.0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "witx", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winx" +version = "0.7.0" +dependencies = [ + "bitflags", + "cvt", + "winapi", +] + +[[package]] +name = "witx" +version = "0.6.0" +dependencies = [ + "clap", + "thiserror", + "wast", +] + +[[package]] +name = "yanix" +version = "0.1.0" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "log", + "thiserror", +] diff --git a/crates/wasi-common/js-polyfill/Cargo.toml b/crates/wasi-common/js-polyfill/Cargo.toml new file mode 100644 index 000000000000..f07f78a33740 --- /dev/null +++ b/crates/wasi-common/js-polyfill/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "polyfill" +version = "0.1.0" +authors = ["The Wasmtime Project Developers"] +edition = "2018" + +[dependencies] +wasi-common = { path = ".." } + +# This crate is built with the wasm32-unknown-emscripten target, so it's separate +# from the main Wasmtime build, so use this directive to exclude it +# from the parent directory's workspace. +[workspace] diff --git a/crates/wasi-common/js-polyfill/assets/WASI-small.png b/crates/wasi-common/js-polyfill/assets/WASI-small.png new file mode 100644 index 000000000000..ef55a0bf6df4 Binary files /dev/null and b/crates/wasi-common/js-polyfill/assets/WASI-small.png differ diff --git a/crates/wasi-common/js-polyfill/assets/load-files.js b/crates/wasi-common/js-polyfill/assets/load-files.js new file mode 100644 index 000000000000..80c4ee0fc46a --- /dev/null +++ b/crates/wasi-common/js-polyfill/assets/load-files.js @@ -0,0 +1,23 @@ +mergeInto(LibraryManager.library, { + loadFiles: function() { + const imports = { wasi_snapshot_preview1: WASIPolyfill }; + let file = document.getElementById("input").files[0]; + let file_with_mime_type = file.slice(0, file.size, "application/wasm"); + let response = new Response(file_with_mime_type); + wasi_instantiateStreaming(response, imports) + .then(obj => { + setInstance(obj.instance); + try { + obj.instance.exports._start(); + } catch (e) { + if (e instanceof WASIExit) { + handleWASIExit(e); + } else { + } + } + }) + .catch(error => { + console.log("error! " + error); + }); + } +}); diff --git a/crates/wasi-common/js-polyfill/assets/shell.html b/crates/wasi-common/js-polyfill/assets/shell.html new file mode 100644 index 000000000000..f44eda52ca03 --- /dev/null +++ b/crates/wasi-common/js-polyfill/assets/shell.html @@ -0,0 +1,88 @@ + + + + + + + WASI Web Polyfill + + + +
WASI
+
Downloading...
+
+ +
+ WASI logo + +
+ + + {{{ SCRIPT }}} + + diff --git a/crates/wasi-common/js-polyfill/assets/wasi.js b/crates/wasi-common/js-polyfill/assets/wasi.js new file mode 100644 index 000000000000..237cb5bad40c --- /dev/null +++ b/crates/wasi-common/js-polyfill/assets/wasi.js @@ -0,0 +1,758 @@ +// To implement `proc_exit`, we define a custom exception object +// that we can throw to unwind the stack and carry the exit value. +function WASIExit(return_value, message, fileName, lineNumber) { + let instance = new Error(message, fileName, lineNumber); + instance.return_value = return_value; + Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); + if (Error.captureStackTrace) { + Error.captureStackTrace(instance, WASIExit); + } + return instance; +} + +WASIExit.prototype = Object.create(Error.prototype, { + constructor: { + value: Error, + enumerable: false, + writable: true, + configurable: true + } +}); + +if (Object.setPrototypeOf) { + Object.setPrototypeOf(WASIExit, Error); +} else { + WASIExit.__proto__ = Error; +} + +function handleWASIExit(e) { + if (e.return_value != 0) { + console.log("program exited with non-zero exit status " + e.return_value); + } +} + +// Safari doesn't have instantiateStreaming +function wasi_instantiateStreaming(response, imports) { + if (WebAssembly && WebAssembly.instantiateStreaming) { + return WebAssembly.instantiateStreaming(response, imports); + } + return response.arrayBuffer().then(function(buffer) { + return WebAssembly.instantiate(buffer, imports); + }); +} + +// The current guest wasm instance. +var currentInstance; + +// There are two heaps in play, the guest heap, which belongs to the WASI-using +// program, and the host heap, which belongs to the Emscripten-compiled polyfill +// library. The following declare support for the guest heap in a similar manner +// to Emscripten's heap. + +var GUEST_HEAP, + /** @type {ArrayBuffer} */ + GUEST_buffer, + /** @type {Int8Array} */ + GUEST_HEAP8, + /** @type {Uint8Array} */ + GUEST_HEAPU8, + /** @type {Int16Array} */ + GUEST_HEAP16, + /** @type {Uint16Array} */ + GUEST_HEAPU16, + /** @type {Int32Array} */ + GUEST_HEAP32, + /** @type {Uint32Array} */ + GUEST_HEAPU32, + /** @type {Float32Array} */ + GUEST_HEAPF32, + /** @type {Float64Array} */ + GUEST_HEAPF64; + +function setInstance(instance) { + currentInstance = instance; + updateGuestBuffer(); +} + +/// We call updateGuestBuffer any time the guest's memory may have changed, +/// such as when creating a new instance, or after calling _malloc. +function updateGuestBuffer() { + var buf = currentInstance.exports.memory.buffer; + Module["GUEST_buffer"] = GUEST_buffer = buf; + Module["GUEST_HEAP8"] = GUEST_HEAP8 = new Int8Array(GUEST_buffer); + Module["GUEST_HEAP16"] = GUEST_HEAP16 = new Int16Array(GUEST_buffer); + Module["GUEST_HEAP32"] = GUEST_HEAP32 = new Int32Array(GUEST_buffer); + Module["GUEST_HEAPU8"] = GUEST_HEAPU8 = new Uint8Array(GUEST_buffer); + Module["GUEST_HEAPU16"] = GUEST_HEAPU16 = new Uint16Array(GUEST_buffer); + Module["GUEST_HEAPU32"] = GUEST_HEAPU32 = new Uint32Array(GUEST_buffer); + Module["GUEST_HEAPF32"] = GUEST_HEAPF32 = new Float32Array(GUEST_buffer); + Module["GUEST_HEAPF64"] = GUEST_HEAPF64 = new Float64Array(GUEST_buffer); +} + +function copyin_bytes(src, len) { + let dst = _malloc(len); + updateGuestBuffer(); + + for (let i = 0; i < len; ++i) { + HEAP8[dst + i] = GUEST_HEAP8[src + i]; + } + return dst; +} + +function copyout_bytes(dst, src, len) { + updateGuestBuffer(); + + for (let i = 0; i < len; ++i) { + GUEST_HEAP8[dst + i] = HEAP8[src + i]; + } + _free(src); +} + +function copyout_i32(dst, src) { + updateGuestBuffer(); + + GUEST_HEAP32[dst >> 2] = HEAP32[src >> 2]; + _free(src); +} + +function copyout_i64(dst, src) { + updateGuestBuffer(); + + GUEST_HEAP32[dst >> 2] = HEAP32[src >> 2]; + GUEST_HEAP32[(dst + 4) >> 2] = HEAP32[(src + 4) >> 2]; + _free(src); +} + +function translate_ciovs(iovs, iovs_len) { + host_iovs = _malloc(8 * iovs_len); + updateGuestBuffer(); + + for (let i = 0; i < iovs_len; ++i) { + let ptr = GUEST_HEAP32[(iovs + i * 8 + 0) >> 2]; + let len = GUEST_HEAP32[(iovs + i * 8 + 4) >> 2]; + let buf = copyin_bytes(ptr, len); + HEAP32[(host_iovs + i * 8 + 0) >> 2] = buf; + HEAP32[(host_iovs + i * 8 + 4) >> 2] = len; + } + return host_iovs; +} + +function free_ciovs(host_iovs, iovs_len) { + for (let i = 0; i < iovs_len; ++i) { + let buf = HEAP32[(host_iovs + i * 8 + 0) >> 2]; + _free(buf); + } + _free(host_iovs); +} + +function translate_iovs(iovs, iovs_len) { + host_iovs = _malloc(8 * iovs_len); + updateGuestBuffer(); + + for (let i = 0; i < iovs_len; ++i) { + let len = GUEST_HEAP32[(iovs + i * 8 + 4) >> 2]; + let buf = _malloc(len); + updateGuestBuffer(); + HEAP32[(host_iovs + i * 8 + 0) >> 2] = buf; + HEAP32[(host_iovs + i * 8 + 4) >> 2] = len; + } + return host_iovs; +} + +function free_iovs(host_iovs, iovs_len, iovs) { + updateGuestBuffer(); + for (let i = 0; i < iovs_len; ++i) { + let buf = HEAP32[(host_iovs + i * 8 + 0) >> 2]; + let len = HEAP32[(host_iovs + i * 8 + 4) >> 2]; + let ptr = GUEST_HEAP32[(iovs + i * 8 + 0) >> 2]; + copyout_bytes(ptr, buf, len); + } + _free(host_iovs); +} + +var WASIPolyfill = { + args_get: function(argv, argv_buf) { + return 0; + }, + + args_sizes_get: function(argc, argv_buf_size) { + updateGuestBuffer(); + + // TODO: Implement command-line arguments. + GUEST_HEAP32[argc >> 2] = 0; + GUEST_HEAP32[argv_buf_size >> 2] = 0; + return 0; + }, + + clock_res_get: function(clock_id, resolution) { + let host_resolution = _malloc(8); + let ret = _wasi_common_clock_res_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + clock_id, + host_resolution + ); + copyout_i64(resolution, host_resolution); + return ret; + }, + + clock_time_get: function(clock_id, precision, time) { + let host_time = _malloc(8); + let ret = _wasi_common_clock_time_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + clock_id, + precision, + host_time + ); + copyout_i64(time, host_time); + return ret; + }, + + environ_get: function(environ, environ_buf) { + return 0; + }, + + environ_sizes_get: function(environ_size, environ_buf_size) { + updateGuestBuffer(); + + // TODO: Implement environment variables. + GUEST_HEAP32[environ_size >> 2] = 0; + GUEST_HEAP32[environ_buf_size >> 2] = 0; + return 0; + }, + + fd_prestat_get: function(fd, buf) { + let host_buf = _malloc(8); // sizeof __wasi_prestat_t + let ret = _wasi_common_fd_prestat_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_buf + ); + copyout_bytes(buf, host_buf, 8); + return ret; + }, + + fd_prestat_dir_name: function(fd, path, path_len) { + let host_buf = _malloc(path_len); + let ret = _wasi_common_fd_prestat_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_buf, + path_len + ); + copyout_bytes(path, host_buf, path_len); + return ret; + }, + + fd_close: function(fd) { + return _wasi_common_fd_close(_get_wasi_context(), fd); + }, + + fd_datasync: function(fd) { + return _wasi_common_fd_datasync(_get_wasi_context(), fd); + }, + + fd_pread: function(fd, iovs, iovs_len, offset, nread) { + let host_iovs = translate_iovs(iovs, iovs_len); + let host_nread = _malloc(4); + let ret = _wasi_common_fd_pread( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_iovs, + iovs_len, + offset, + host_nread + ); + copyout_i32(nread, host_nread); + free_iovs(host_iovs, iovs_len, iovs); + return ret; + }, + + fd_pwrite: function(fd, iovs, iovs_len, offset, nwritten) { + let host_iovs = translate_ciovs(iovs, iovs_len); + let host_nwritten = _malloc(4); + let ret = _wasi_common_fd_pwrite( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_iovs, + iovs_len, + offset, + host_nwritten + ); + copyout_i32(nwritten, host_nwritten); + free_ciovs(host_iovs, iovs_len); + return ret; + }, + + fd_read: function(fd, iovs, iovs_len, nread) { + let host_iovs = translate_iovs(iovs, iovs_len); + let host_nread = _malloc(4); + let ret = _wasi_common_fd_read( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_iovs, + iovs_len, + host_nread + ); + copyout_i32(nread, host_nread); + free_iovs(host_iovs, iovs_len, iovs); + return ret; + }, + + fd_renumber: function(from, to) { + return _wasi_common_fd_renumber(_get_wasi_context(), from, to); + }, + + fd_seek: function(fd, offset, whence, newoffset) { + let host_newoffset = _malloc(8); + let ret = _wasi_common_fd_seek( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + offset, + whence, + host_newoffset + ); + copyout_i64(newoffset, host_newoffset); + return ret; + }, + + fd_tell: function(fd, newoffset) { + let host_newoffset = _malloc(8); + let ret = _wasi_common_fd_seek( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_newoffset + ); + copyout_i64(newoffset, host_newoffset); + return ret; + }, + + fd_fdstat_get: function(fd, buf) { + let host_buf = _malloc(24); // sizeof __wasi_fdstat_t + let ret = _wasi_common_fd_fdstat_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_buf + ); + copyout_bytes(buf, host_buf, 24); + return ret; + }, + + fd_fdstat_set_flags: function(fd, flags) { + return _wasi_common_fd_fdstat_set_flags(_get_wasi_context(), fd, flags); + }, + + fd_fdstat_set_rights: function(fd, fs_rights_base, fs_rights_inheriting) { + return _wasi_common_fd_fdstat_set_rights( + _get_wasi_context(), + fd, + fs_rights_base, + fs_rights_inheriting + ); + }, + + fd_sync: function(fd) { + return _wasi_common_fd_sync(_get_wasi_context(), fd); + }, + + fd_write: function(fd, iovs, iovs_len, nwritten) { + let host_iovs = translate_ciovs(iovs, iovs_len); + let host_nwritten = _malloc(4); + let ret = _wasi_common_fd_write( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_iovs, + iovs_len, + host_nwritten + ); + copyout_i32(nwritten, host_nwritten); + free_ciovs(host_iovs, iovs_len); + return ret; + }, + + fd_advise: function(fd, offset, len, advice) { + return _wasi_common_fd_advise(_get_wasi_context(), fd, offset, len, advice); + }, + + fd_allocate: function(fd, offset, len) { + return _wasi_common_fd_allocate(_get_wasi_context(), fd, offset, len); + }, + + path_create_directory: function(fd, path, path_len) { + let host_path = copyin_bytes(path, path_len); + let ret = _wasi_common_path_create_directory( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_path, + path_len + ); + _free(host_path); + return ret; + }, + + path_link: function(fd0, path0, path_len0, fd1, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path_len0); + let host_path1 = copyin_bytes(path1, path_len1); + let ret = _wasi_common_path_link( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd0, + host_path0, + path_len0, + fd1, + host_path1, + path1_len + ); + _free(host_path1); + _free(host_path0); + return ret; + }, + + path_open: function( + dirfd, + dirflags, + path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd + ) { + let host_path = copyin_bytes(path, path_len); + let host_fd = _malloc(4); + let ret = _wasi_common_path_open( + _get_wasi_context(), + HEAP8, + HEAP8.length, + dirfd, + dirflags, + host_path, + path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + host_fd + ); + copyout_i32(fd, host_fd); + _free(host_path); + return ret; + }, + + fd_readdir: function(fd, buf, buf_len, cookie, buf_used) { + let host_buf = _malloc(buf_len); + let host_buf_used = _malloc(4); + let ret = _wasi_common_fd_readdir( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + buf, + buf_len, + cookie, + host_buf_used + ); + copyout_i32(buf_used, host_buf_used); + copyout_bytes(buf, host_buf, buf_len); + return ret; + }, + + path_readlink: function(fd, path, path_len, buf, buf_len, buf_used) { + let host_path = copyin_bytes(path, path_len); + let host_buf = _malloc(buf_len); + let host_buf_used = _malloc(4); + let ret = _wasi_common_path_readlink( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + path, + path_len, + buf, + buf_len, + host_buf_used + ); + copyout_i32(buf_used, host_buf_used); + copyout_bytes(buf, host_buf, buf_len); + _free(host_path); + return ret; + }, + + path_rename: function(fd0, path0, path_len0, fd1, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path_len0); + let host_path1 = copyin_bytes(path1, path_len1); + let ret = _wasi_common_path_rename( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd0, + host_path0, + path_len0, + fd1, + host_path1, + path1_len + ); + _free(host_path1); + _free(host_path0); + return ret; + }, + + fd_filestat_get: function(fd, buf) { + let host_buf = _malloc(56); // sizeof __wasi_filestat_t + let ret = _wasi_common_fd_filestat_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_buf + ); + copyout_bytes(buf, host_buf, 56); + return ret; + }, + + fd_filestat_set_size: function(fd, size) { + return _wasi_common_fd_filestat_set_size( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + size + ); + }, + + fd_filestat_set_times: function(fd, st_atim, st_mtim, fstflags) { + return _wasi_common_fd_filestat_set_times( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + st_atim, + st_mtim, + fstflags + ); + }, + + path_filestat_get: function(fd, path, path_len, buf) { + let host_path = copyin_bytes(path, path_len); + let host_buf = _malloc(56); // sizeof __wasi_filestat_t + let ret = _wasi_common_path_filestat_get( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_path, + path_len, + host_buf + ); + copyout_bytes(buf, host_buf, 56); + _free(host_path); + return ret; + }, + + path_filestat_set_times: function( + fd, + path, + path_len, + st_atim, + st_mtim, + flags + ) { + let host_path = copyin_bytes(path, path_len); + let ret = _wasi_common_path_filestat_set_times( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_path, + st_atim, + st_mtim, + fstflags + ); + _free(host_path); + return ret; + }, + + path_symlink: function(path0, path_len0, fd, path1, path_len1) { + let host_path0 = copyin_bytes(path0, path0_len); + let host_path1 = copyin_bytes(path1, path1_len); + let ret = _wasi_common_path_symlink( + _get_wasi_context(), + HEAP8, + HEAP8.length, + host_path0, + path_len0, + fd, + host_path1, + path_len1 + ); + _free(host_path1); + _free(host_path0); + return ret; + }, + + path_unlink_file: function(fd, path, path_len, flags) { + let host_path = copyin_bytes(path, path_len); + let ret = _wasi_common_path_unlink_file( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_path, + path_len, + flags + ); + _free(host_path); + return ret; + }, + + path_remove_directory: function(fd, path, path_len, flags) { + let host_path = copyin_bytes(path, path_len); + let ret = _wasi_common_path_remove_directory( + _get_wasi_context(), + HEAP8, + HEAP8.length, + fd, + host_path, + path_len, + flags + ); + _free(host_path); + return ret; + }, + + poll_oneoff: function(in_, out, nsubscriptions, nevents) { + let host_in = copyin_bytes(in_, nsubscriptions * 56); // sizeof __wasi_subscription_t + let host_out = _malloc(nsubscriptions * 32); // sizeof __wasi_event_t + let host_nevents = _malloc(4); + let ret = _wasi_common_poll_oneoff( + _get_wasi_context(), + HEAP8, + HEAP8.length, + host_in, + host_out, + host_nevents + ); + copyout_bytes(out, host_out, nsubscriptions * 32); + copyout_i32(nevents, host_nevents); + _free(host_in); + return ret; + }, + + proc_exit: function(rval) { + let message; + if (rval == 0) { + message = "success"; + } else { + message = "error code " + rval; + } + throw new WASIExit(rval, message); + }, + + proc_raise: function(sig) { + if ( + sig == 18 || // SIGSTOP + sig == 19 || // SIGTSTP + sig == 20 || // SIGTTIN + sig == 21 || // SIGTTOU + sig == 22 || // SIGURG + sig == 16 || // SIGCHLD + sig == 13 + ) { + // SIGPIPE + return 0; + } + + let message = "raised signal " + sig; + throw new WASIExit(128 + sig, message); + }, + + random_get: function(buf, buf_len) { + let host_buf = _malloc(buf_len); + let ret = _wasi_common_random_get(HEAP8, HEAP8.length, host_buf, buf_len); + copyout_bytes(buf, host_buf, buf_len); + return ret; + }, + + sched_yield: function() { + return _wasi_common_sched_yield(); + }, + + sock_recv: function( + sock, + ri_data, + ri_data_len, + ri_flags, + ro_datalen, + ro_flags + ) { + let host_ri_data = translate_iovs(ri_data, ri_data_len); + let host_ro_datalen = _malloc(4); + let ret = _wasi_common_sock_recv( + _get_wasi_context(), + HEAP8, + HEAP8.length, + sock, + host_ri_data, + ri_data_len, + ri_flags, + host_ro_data, + ro_flags + ); + copyout_i32(ro_datalen, host_ro_datalen); + free_iovs(host_ri_data, ri_data_len, ri_data); + return ret; + }, + + sock_send: function(sock, si_data, si_data_len, si_flags, so_datalen) { + let host_si_data = translate_ciovs(si_data, si_data_len); + let host_so_datalen = _malloc(4); + let ret = _wasi_common_sock_send( + _get_wasi_context(), + HEAP8, + HEAP8.length, + sock, + host_si_data, + si_data_len, + si_flags, + host_so_datalen + ); + copyout_i32(so_datalen, host_so_datalen); + free_ciovs(host_si_data, si_data_len); + return ret; + }, + + sock_shutdown: function(sock, how) { + return _wasi_common_sock_shutdown( + _get_wasi_context(), + HEAP8, + HEAP8.length, + sock, + how + ); + } +}; diff --git a/crates/wasi-common/js-polyfill/build.sh b/crates/wasi-common/js-polyfill/build.sh new file mode 100755 index 000000000000..f4064918fd58 --- /dev/null +++ b/crates/wasi-common/js-polyfill/build.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -euo pipefail + +WORKDIR=assets # our workdir +JS_LIBRARY=$WORKDIR/load-files.js # JS helper lib which we use in main.rs to handle loading WASI binaries in Emscripten +EM_SHELL=$WORKDIR/shell.html # basic Emscripten shell env +WASI=$WORKDIR/wasi.js # WASI polyfill JS glue code +POLYFILL=$WORKDIR/polyfill.html # our output + +EXPORTED_FUNCTIONS=( + _main + _get_wasi_context + _handleFiles + _wasi_common_args_get + _wasi_common_args_sizes_get + _wasi_common_clock_res_get + _wasi_common_clock_time_get + _wasi_common_environ_get + _wasi_common_environ_sizes_get + _wasi_common_fd_advise + _wasi_common_fd_allocate + _wasi_common_fd_close + _wasi_common_fd_datasync + _wasi_common_fd_fdstat_get + _wasi_common_fd_fdstat_set_flags + _wasi_common_fd_fdstat_set_rights + _wasi_common_fd_filestat_get + _wasi_common_fd_filestat_set_size + _wasi_common_fd_filestat_set_times + _wasi_common_fd_pread + _wasi_common_fd_prestat_dir_name + _wasi_common_fd_prestat_get + _wasi_common_fd_pwrite + _wasi_common_fd_read + _wasi_common_fd_readdir + _wasi_common_fd_renumber + _wasi_common_fd_seek + _wasi_common_fd_sync + _wasi_common_fd_tell + _wasi_common_fd_write + _wasi_common_path_create_directory + _wasi_common_path_filestat_get + _wasi_common_path_filestat_set_times + _wasi_common_path_link + _wasi_common_path_open + _wasi_common_path_readlink + _wasi_common_path_remove_directory + _wasi_common_path_rename + _wasi_common_path_symlink + _wasi_common_path_unlink_file + _wasi_common_poll_oneoff + _wasi_common_proc_exit + _wasi_common_proc_raise + _wasi_common_random_get + _wasi_common_sched_yield + _wasi_common_sock_recv + _wasi_common_sock_send + _wasi_common_sock_shutdown +) +EXPORTED_FUNCTIONS_CONCAT=$(printf ",'%s'" "${EXPORTED_FUNCTIONS[@]}") +EXPORTED_FUNCTIONS_CONCAT=${EXPORTED_FUNCTIONS_CONCAT:1} + +cargo +nightly rustc --target wasm32-unknown-emscripten --release -vv -- -C link-args="--js-library ${JS_LIBRARY} --shell-file ${EM_SHELL} --pre-js ${WASI} -s EXPORTED_FUNCTIONS=[${EXPORTED_FUNCTIONS_CONCAT}] -o ${POLYFILL}" diff --git a/crates/wasi-common/js-polyfill/src/main.rs b/crates/wasi-common/js-polyfill/src/main.rs new file mode 100644 index 000000000000..366a2201d0d4 --- /dev/null +++ b/crates/wasi-common/js-polyfill/src/main.rs @@ -0,0 +1,34 @@ +use std::cell::RefCell; +use wasi_common::{WasiCtx, WasiCtxBuilder}; + +extern "C" { + fn loadFiles(); +} + +thread_local! { + static WASI_CTX: RefCell> = RefCell::new(None); +} + +fn main() {} + +#[no_mangle] +unsafe fn get_wasi_context() -> *mut WasiCtx { + WASI_CTX.with(|ctx| { + ctx.borrow_mut() + .as_mut() + .expect("WasiCtx should be initialized by now") as *mut _ + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +fn handleFiles() { + WASI_CTX.with(|ctx| { + let wasi_ctx = WasiCtxBuilder::new() + .inherit_stdio() + .build() + .expect("could build WasiCtx with stdio inherited"); + ctx.replace(Some(wasi_ctx)); + }); + unsafe { loadFiles() } +}