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...
+
+
+
+
+
+
+ {{{ 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