importScripts(location.origin + "/browser_wasi_shim/index.js"); importScripts(location.origin + "/browser_wasi_shim/wasi_defs.js"); importScripts(location.origin + "/worker-util.js"); importScripts(location.origin + "/wasi-util.js"); onmessage = (msg) => { serveIfInitMsg(msg); var fds = [ undefined, // 0: stdin undefined, // 1: stdout undefined, // 2: stderr undefined, // 3: receive certificates undefined, // 4: socket listenfd undefined, // 5: accepted socket fd (multi-connection is unsupported) // 6...: used by wasi shim ]; var certfd = 3; var listenfd = 4; var args = ['arg0', '--certfd='+certfd, '--net-listenfd='+listenfd, '--debug']; var env = []; var wasi = new WASI(args, env, fds); wasiHack(wasi, certfd, 5); wasiHackSocket(wasi, listenfd, 5); fetch(getImagename(), { credentials: 'same-origin' }).then((resp) => { resp['arrayBuffer']().then((wasm) => { WebAssembly.instantiate(wasm, { "wasi_snapshot_preview1": wasi.wasiImport, "env": envHack(wasi), }).then((inst) => { wasi.start(inst.instance); }); }) }); }; // definition from wasi-libc https://github.com/WebAssembly/wasi-libc/blob/wasi-sdk-19/expected/wasm32-wasi/predefined-macros.txt const ERRNO_INVAL = 28; const ERRNO_AGAIN= 6; function wasiHack(wasi, certfd, connfd) { var certbuf = new Uint8Array(0); var _fd_close = wasi.wasiImport.fd_close; wasi.wasiImport.fd_close = (fd) => { if (fd == certfd) { sendCert(certbuf); return 0; } return _fd_close.apply(wasi.wasiImport, [fd]); } var _fd_fdstat_get = wasi.wasiImport.fd_fdstat_get; wasi.wasiImport.fd_fdstat_get = (fd, fdstat_ptr) => { if (fd == certfd) { return 0; } return _fd_fdstat_get.apply(wasi.wasiImport, [fd, fdstat_ptr]); } wasi.wasiImport.fd_fdstat_set_flags = (fd, fdflags) => { // TODO return 0; } var _fd_write = wasi.wasiImport.fd_write; wasi.wasiImport.fd_write = (fd, iovs_ptr, iovs_len, nwritten_ptr) => { if ((fd == 1) || (fd == 2) || (fd == certfd)) { var buffer = new DataView(wasi.inst.exports.memory.buffer); var buffer8 = new Uint8Array(wasi.inst.exports.memory.buffer); var iovecs = Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); var wtotal = 0 for (i = 0; i < iovecs.length; i++) { var iovec = iovecs[i]; var buf = buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len); if (buf.length == 0) { continue; } console.log(new TextDecoder().decode(buf)); if (fd == certfd) { certbuf = appendData(certbuf, buf); } wtotal += buf.length; } buffer.setUint32(nwritten_ptr, wtotal, true); return 0; } console.log("fd_write: unknown fd " + fd); return _fd_write.apply(wasi.wasiImport, [fd, iovs_ptr, iovs_len, nwritten_ptr]); } wasi.wasiImport.poll_oneoff = (in_ptr, out_ptr, nsubscriptions, nevents_ptr) => { if (nsubscriptions == 0) { return ERRNO_INVAL; } let buffer = new DataView(wasi.inst.exports.memory.buffer); let in_ = Subscription.read_bytes_array(buffer, in_ptr, nsubscriptions); let isReadPollStdin = false; let isReadPollConn = false; let isClockPoll = false; let pollSubStdin; let pollSubConn; let clockSub; let timeout = Number.MAX_VALUE; for (let sub of in_) { if (sub.u.tag.variant == "fd_read") { if ((sub.u.data.fd != 0) && (sub.u.data.fd != connfd)) { return ERRNO_INVAL; // only fd=0 and connfd is supported as of now (FIXME) } if (sub.u.data.fd == 0) { isReadPollStdin = true; pollSubStdin = sub; } else { isReadPollConn = true; pollSubConn = sub; } } else if (sub.u.tag.variant == "clock") { if (sub.u.data.timeout < timeout) { timeout = sub.u.data.timeout isClockPoll = true; clockSub = sub; } } else { return ERRNO_INVAL; // FIXME } } let events = []; if (isReadPollStdin || isReadPollConn || isClockPoll) { var sockreadable = sockWaitForReadable(timeout / 1000000000); if (isReadPollConn) { if (sockreadable == errStatus) { return ERRNO_INVAL; } else if (sockreadable == true) { let event = new Event(); event.userdata = pollSubConn.userdata; event.error = 0; event.type = new EventType("fd_read"); events.push(event); } } if (isClockPoll) { let event = new Event(); event.userdata = clockSub.userdata; event.error = 0; event.type = new EventType("clock"); events.push(event); } } var len = events.length; Event.write_bytes_array(buffer, out_ptr, events); buffer.setUint32(nevents_ptr, len, true); return 0; } } function envHack(wasi){ return { http_send: function(addressP, addresslen, reqP, reqlen, idP){ var buffer = new DataView(wasi.inst.exports.memory.buffer); var address = new Uint8Array(wasi.inst.exports.memory.buffer, addressP, addresslen); var req = new Uint8Array(wasi.inst.exports.memory.buffer, reqP, reqlen); streamCtrl[0] = 0; postMessage({ type: "http_send", address: address, req: req, }); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { return ERRNO_INVAL; } var id = streamStatus[0]; buffer.setUint32(idP, id, true); return 0; }, http_writebody: function(id, bodyP, bodylen, nwrittenP, isEOF){ var buffer = new DataView(wasi.inst.exports.memory.buffer); var body = new Uint8Array(wasi.inst.exports.memory.buffer, bodyP, bodylen); streamCtrl[0] = 0; postMessage({ type: "http_writebody", id: id, body: body, isEOF: isEOF, }); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { return ERRNO_INVAL; } buffer.setUint32(nwrittenP, bodylen, true); return 0; }, http_isreadable: function(id, isOKP){ var buffer = new DataView(wasi.inst.exports.memory.buffer); streamCtrl[0] = 0; postMessage({type: "http_isreadable", id: id}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { return ERRNO_INVAL; } var readable = 0; if (streamData[0] == 1) { readable = 1; } buffer.setUint32(isOKP, readable, true); return 0; }, http_recv: function(id, respP, bufsize, respsizeP, isEOFP){ var buffer = new DataView(wasi.inst.exports.memory.buffer); var buffer8 = new Uint8Array(wasi.inst.exports.memory.buffer); streamCtrl[0] = 0; postMessage({type: "http_recv", id: id, len: bufsize}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { return ERRNO_INVAL; } var ddlen = streamLen[0]; var resp = streamData.slice(0, ddlen); buffer8.set(resp, respP); buffer.setUint32(respsizeP, ddlen, true); if (streamStatus[0] == 1) { buffer.setUint32(isEOFP, 1, true); } else { buffer.setUint32(isEOFP, 0, true); } return 0; }, http_readbody: function(id, bodyP, bufsize, bodysizeP, isEOFP){ var buffer = new DataView(wasi.inst.exports.memory.buffer); var buffer8 = new Uint8Array(wasi.inst.exports.memory.buffer); streamCtrl[0] = 0; postMessage({type: "http_readbody", id: id, len: bufsize}); Atomics.wait(streamCtrl, 0, 0); if (streamStatus[0] < 0) { return ERRNO_INVAL; } var ddlen = streamLen[0]; var body = streamData.slice(0, ddlen); buffer8.set(body, bodyP); buffer.setUint32(bodysizeP, ddlen, true); if (streamStatus[0] == 1) { buffer.setUint32(isEOFP, 1, true); } else { buffer.setUint32(isEOFP, 0, true); } return 0; } }; }