498 lines
15 KiB
JavaScript
498 lines
15 KiB
JavaScript
// 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_clock_res_get(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_clock_time_get(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_fd_prestat_get(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_fd_prestat_get(fd, host_buf, path_len);
|
|
copyout_bytes(buf, host_buf, path_len);
|
|
return ret;
|
|
},
|
|
|
|
fd_close: function(fd) {
|
|
return ___wasi_fd_close(fd);
|
|
},
|
|
|
|
fd_datasync: function(fd) {
|
|
return ___wasi_fd_datasync(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_fd_pread(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_fd_pwrite(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_fd_read(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_fd_renumber(from, to);
|
|
},
|
|
|
|
fd_seek: function(fd, offset, whence, newoffset) {
|
|
let host_newoffset = _malloc(8);
|
|
let ret = ___wasi_fd_seek(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_fd_seek(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_fd_fdstat_get(fd, host_buf);
|
|
copyout_bytes(buf, host_buf, 24);
|
|
return ret;
|
|
},
|
|
|
|
fd_fdstat_set_flags: function(fd, flags) {
|
|
return ___wasi_fd_fdstat_set_flags(fd, flags);
|
|
},
|
|
|
|
fd_fdstat_set_rights: function(fd, fs_rights_base, fs_rights_inheriting) {
|
|
return ___wasi_fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting);
|
|
},
|
|
|
|
fd_sync: function(fd) {
|
|
return ___wasi_fd_sync(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_fd_write(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_fd_advise(fd, offset, len, advice);
|
|
},
|
|
|
|
fd_allocate: function(fd, offset, len) {
|
|
return ___wasi_fd_allocate(fd, offset, len);
|
|
},
|
|
|
|
path_create_directory: function(fd, path, path_len) {
|
|
let host_path = copyin_bytes(path, path_len);
|
|
let ret = ___wasi_path_create_directory(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_path_link(fd, 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_path_open(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_fd_readdir(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_path_readlink(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_path_rename(fd, 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_fd_filestat_get(host_buf);
|
|
copyout_bytes(buf, host_buf, 56);
|
|
return ret;
|
|
},
|
|
|
|
fd_filestat_set_size: function(fd, size) {
|
|
return ___wasi_fd_filestat_set_size(fd, size);
|
|
},
|
|
|
|
fd_filestat_set_times: function(fd, st_atim, st_mtim, fstflags) {
|
|
return ___wasi_fd_filestat_set_times(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_path_filestat_get(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_path_filestat_set_times(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_path_symlink(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_path_unlink_file(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_path_remove_directory(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_poll_oneoff(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_random_get(host_buf, buf_len);
|
|
copyout_bytes(buf, host_buf, buf_len);
|
|
return ret;
|
|
},
|
|
|
|
sched_yield: function() {
|
|
return ___wasi_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_sock_recv(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_sock_send(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_sock_shutdown(sock, how);
|
|
}
|
|
|
|
};
|