haskell-wasm-repl/web/stack-worker.js

246 lines
9.1 KiB
JavaScript
Raw Permalink Normal View History

2024-10-10 19:12:48 +05:30
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;
}
};
}