From 8d0033a50b078d57ce4d4718d4d4bb369de5c16f Mon Sep 17 00:00:00 2001 From: Pip Cet Date: Sun, 31 Jan 2021 22:58:14 +0000 Subject: [PATCH] jsc/wasm32/wasm32.jsc: consolidate *.jsc into a single file --- jsc/wasm32/wasm32.jsc | 5765 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5765 insertions(+) create mode 100644 jsc/wasm32/wasm32.jsc diff --git a/jsc/wasm32/wasm32.jsc b/jsc/wasm32/wasm32.jsc new file mode 100644 index 00000000..85ca12ce --- /dev/null +++ b/jsc/wasm32/wasm32.jsc @@ -0,0 +1,5765 @@ +#{#define _GNU_SOURCE} +#{#include "js3.h"} +#{#include "zeropage.h"} +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } +#{#include } + +#{#include "zeropage.h"} +#{#include "js3.h"} +#{#define AT_FDROOTD -101} +#{#define offsetof(type, field) ((unsigned)&((type *)0)->field)} + +#{JSV direntp("direntp");} +#{JSV intptr("intptr");} +#{JSV iov("iov");} +#{JSV fdsptr("fdsptr");} +#{JSV i("i");} +#{JSV statbufptr("statbufptr");} +#{JSV tp1("8192", "HEAP");} +#{JSV timespec("timespec");} +#{JSV tvptr("tvptr");} +#{JSV zp("4096", "HEAP");} + +#{Heap thisheap("this.HEAP");} +#{Context context(&thisheap);} + +var args = (typeof (scriptArgs) === "undefined") ? process.argv.slice(2) : scriptArgs.slice(0); + +var gdbpipes; +if (args[0] === "--gdbpipes") { + gdbpipes = args[1]; + args.shift(); + args.shift(); +} + +var path = args.shift(); +args.unshift("../../miniperl"); +var remote; + +function CStringAt(heap, offset) +{ + var ret = ''; + + for (var i0 = offset; heap[i0]; i0++) { + ret += String.fromCharCode(heap[i0]); + } + + return ret; +} + +function CStringsAt(heap, ptr) +{ + var HEAP32 = new Int32Array(heap); + var HEAP8 = new Int8Array(heap); + var res = []; + + while (HEAP32[ptr>>2]) { + res.push(CStringAt(HEAP8, HEAP32[ptr>>2])); + ptr += 4; + } + + return res; +} + +function CStringTo(str, heap, offset) +{ + var i0; + + for (i0 = 0; i0 < str.length; i0++) { + heap[offset + i0] = str.charCodeAt(i0); + } + + heap[offset + i0] = 0; + + return i0+1; +} + +if (false) if (typeof window !== "undefined") { + window.addEventListener("message", function (e) { + var origin = e.origin || e.originalEvent.origin; + alert (origin); + + if (origin && + origin !== "http://example.org:8080") + return; + + var data = e.data; + + switch (data[0]) { + case "send": + var rfdno = data[1]; + var payload = data[2]; + var fd = gRemoteFDs[rfdno]; + + alert("unknown rfd " + rfdno + " " + gRemoteFDs); + if (fd) { + fd.write(payload, 0, payload.byteLength); + + e.source.postMessage([rfdno, "sent"], "*"); + } else { + } + return; + + case "receive": + var rfdno = data[1]; + var fd = gRemoteFDs[rfdno]; + var len = data[2]; + + if (fd) { + var heap = new ArrayBuffer(len); + var heap8 = new Int8Array(heap); + + fd.read(heap8, 0, len).then(l => { + e.source.postMessage([rfdno, "read", heap], "*"); + }); + } + + return; + } + }); +} + +var sys; +var worker; + +var copyvars = ["HOME", "MAKE", "LIBPERL_A", "PERL_CORE", "PATH", "EMACSLOADPATH", "EMACS_LOADPATH", "PERL_MM_USE_DEFAULT", "INSTALLDIRS", "PYTHONHOME"]; + +var restart; + +function environment_variables() +{ + var env = []; + + if (typeof os === "undefined") { + } else if ("getenvironment" in os) { + env = os.getenvironment(); + } else { + for (var i = 0; i < copyvars.length; i++) { + var copyvar = copyvars[i]; + var value = os.getenv(copyvar); + + if (value !== undefined) + env.push(copyvar + "=" + value); + } + env.push("TERM=vt100"); + } + + return env; +} + +function newAsmJSModule(mod) +{ + sys = new AsmJSSystem(); + var env = []; + + if (typeof os === "undefined") { + } else if ("getenvironment" in os) { + env = os.getenvironment(); + } else { + for (var i = 0; i < copyvars.length; i++) { + var copyvar = copyvars[i]; + var value = os.getenv(copyvar); + + if (value !== undefined) + env.push(copyvar + "=" + value); + } + env.push("TERM=vt100"); + } + if (typeof global !== "undefined") + args.shift(); + sys.instantiate(mod, args, env); + //restart = function () { + //sys.instantiate(mod, args, env); + //}; + + while (sys.runqueue.length) + sys.step(); +} + +if (typeof global !== "undefined") + run = () => global.setInterval(() => sys.step(), 0); + +function update() { + if (!sys.threads || !sys.threads[0]) { + return; + } + + while (sys.threads[0].stopped() == 0) + sys.step(); + + if (sys.threads.length == 0 || sys.threads[0].stopped() == -1) + restart(); + else if (sys.threads[0].exports.dograph) { + outstr = ""; + counter++; + document.getElementById("dot-counter").innerHTML = counter.toString(); + content = document.getElementById("dot-input").value; + var start = Date.now(); + var i = 0; + var res; + var str; + res = sys.threads[0].exports.dograph(content); + var dur = Date.now() - start; + + if (res) + str = res.toString(); + else + str = ""; + + outstr = str; + document.getElementById("svg-output").innerHTML = str; + counter = dur; + document.getElementById("dot-counter").innerHTML = counter.toString(); + + if (res) + sys.threads[0].exports.free(res); + } +} + +if (false) if (typeof window !== "undefined") { + document.getElementById("dot-input").oninput = update; + window.setInterval(update, 5); +} + +// worker = new Worker("asmjs-worker.js"); +// worker.onmessage = function (e) { +// var data = e.data; + +// switch (data[0]) { +// case "send": +// var rfdno = data[1]; +// var payload = data[2]; +// var fd = gRemoteFDs[rfdno]; + +// if (fd) { +// fd.write(new Uint8Array(payload), 0, payload.byteLength) +// .then((len) => { +// worker.postMessage(["sent", rfdno, len]); +// }); +// } else { +// alert("unknown rfd " + rfdno + " " + gRemoteFDs); +// } +// return; + +// case "receive": +// var rfdno = data[1]; +// var fd = gRemoteFDs[rfdno]; +// var len = data[2]; + +// if (fd) { +// var heap = new ArrayBuffer(len); +// var heap8 = new Int8Array(heap); + +// fd.read(heap8, 0, len).then(l => { +// e.source.postMessage([rfdno, "read", heap], "*"); +// }); +// } + +// return; +// } +// }; +// worker.postMessage(["process", sys.processes[0].freeze()]); +// } + +function AllocatorRange(free, start, end) +{ + this.start = start; + this.end = end; + this.length = end - start; + this.free = free; +} + +function RangeAllocator(grow) +{ + this.start = 0; + this.end = 0; + this.ranges_by_start = {}; + this.ranges_by_end = {}; + + this.grow = grow; +} + +RangeAllocator.prototype.clonedesc = function () +{ + return { + start: this.start, + end: this.end, + ranges_by_start: this.ranges_by_start, + ranges_by_end: this.ranges_by_end, + }; +}; + +RangeAllocator.prototype.split_range = function (range, new_start) +{ + let new_range = new AllocatorRange(range.free, new_start, range.end); + this.ranges_by_end[range.end] = new_range; + range.end = new_start; + this.ranges_by_start[new_start] = new_range; + this.ranges_by_end[range.end] = range; +}; + +RangeAllocator.prototype.merge_range = function (range0, range1) +{ + if (range0.free !== range1.free) + throw "merging heterogenous ranges"; + + delete this.ranges_by_start[range0.end]; + delete this.ranges_by_end[range0.end]; + this.ranges_by_end[range1.end] = range0; + range0.end = range1.end; + + return range0; +}; + +RangeAllocator.prototype.alloc_range = function (n) +{ + for (let start in this.ranges_by_start) { + let range = this.ranges_by_start[start]; + + if (range.free && range.length >= n) { + if (range.length == n) { + range.free = false; + return range; + } + let range0 = this.split_range(range, start + n); + + range0.free = false; + + return range0; + } + } + + let lastrange = this.ranges_by_end[this.end]; + if (lastrange && lastrange.free) { + this.grow(n - lastrange.length); + delete this.ranges_by_end[this.end]; + this.end += n - lastrange.length; + this.ranges_by_end[this.end] = lastrange; + lastrange.end = this.end; + lastrange.free = false; + return lastrange; + } else { + this.grow(n); + let range = new AllocatorRange(true, this.end, this.end + n); + this.ranges_by_start[this.end] = range; + this.ranges_by_end[this.end + n] = range; + this.end += n; + range.free = false; + return range; + } +}; + +RangeAllocator.prototype.free_range = function (range) +{ + range.free = true; + let range1; + if (range1 = this.ranges_by_end[range.start].free) + range = this.merge_range(range1, range); + if (range1 = this.ranges_by_start[range.end].free) + range = this.merge_range(range, range1); + + this.ranges_by_start[range.start] = range; + this.ranges_by_end[range.end] = range; +}; + +RangeAllocator.prototype.alloc = function (n) +{ + let range = this.alloc_range(n); + + return range.start; +}; + +RangeAllocator.prototype.free = function (start) +{ + let range = this.ranges_by_start[start]; + + this.free_range(range); +}; + +var global = this; + +/* process -> vm + * -> thread + */ + +function Wasm32Symbol(name) +{ + this.name = name; + this.definitions = []; + this.references = []; +} + +Wasm32Symbol.prototype.add_definition = function (definition) +{ + this.definitions.push(definition); + this.check_definitions(); +}; + +Wasm32Symbol.prototype.check_definitions = function () +{ + for (let snippet of this.definitions) + for (let snippet2 of this.definitions) { + let key = snippet[0]; + if (snippet2[0] === key) { + if (snippet[1] !== snippet2[1]) + ;//console.log(`conflicting ${key}: ${snippet[1]} vs ${snippet2[1]} for ${this.name}`); + } + } +}; + +Wasm32Symbol.prototype.get_addr = function () +{ + for (let def of this.definitions) + if (def[0] === "code" || def[0] === "data") + return def[1]; + + return undefined; + throw "undefined symbol " + this.name; +}; + +Wasm32Symbol.prototype.add_reference = function (reference) +{ + this.references.push(reference); +}; + +/* A link-map object. */ +function Wasm32LM(vm) +{ + this.vm = vm; + + this.def = {}; + this.defun = {}; + this.ref = {}; + this.refun = {}; + this.copy = {}; + this.dep = {}; + this.lazy = {}; + this.symbols = new Map(); + this.allsymbols = new Set(); +} + +Wasm32LM.prototype.dyninfo_fixups = function (dyninfo, imports, module) +{ + var vm = this.vm; + + for (let [addr1, addr2] of dyninfo.fixup || []) { + addr1 = addr1 - 0x4000 + imports.sys.got; + addr2 = addr2 - 0x4000 + imports.sys.got; + + vm.HEAPU32[addr1>>2] = addr2; + } + + for (let [addr1, addr2] of dyninfo.fixupfun || []) { + addr1 = addr1 - 0x4000 + imports.sys.got; + addr2 = addr2 + imports.sys.plt; + + vm.HEAPU32[addr1>>2] = addr2; + } +}; + +Wasm32LM.prototype.dyninfo_symbol_definitions = + function (dyninfo, imports, module) +{ + let vm = this.vm; + let symbols = new Map(); + for (let [name, addr] of dyninfo.def || []) { + addr += imports.sys.got - 0x4000; + if (!symbols.has(name)) + symbols.set(name, this.symbols.get(name) || new Wasm32Symbol(name)); + if (symbols.get(name).get_addr()) { + if (name === "__environ") + continue; + let symbol = new Wasm32Symbol(name) + symbols.set(name, symbol); + this.allsymbols.add(symbol); + } + symbols.get(name).add_definition(["data", addr]); + } + for (let [name, addr] of dyninfo.defun || []) { + addr += imports.sys.plt; + if (!symbols.has(name)) + symbols.set(name, this.symbols.get(name) || new Wasm32Symbol(name)); + if (symbols.get(name).get_addr()) { + let symbol = new Wasm32Symbol(name) + symbols.set(name, symbol); + this.allsymbols.add(symbol); + } + symbols.get(name).add_definition(["code", addr]); + } + for (let [name] of dyninfo.copy || []) + symbols.delete(name); + for (let [name, symbol] of symbols) { + this.symbols.set(name, symbol); + this.allsymbols.add(symbol); + module.symtab[symbol] = symbol.get_addr(); + } +}; + +Wasm32LM.prototype.dyninfo_symbol_references = + function (dyninfo, imports, module) +{ + for (let [name, addr] of dyninfo.ref || []) { + addr -= 0x4000; + addr += imports.sys.got; + if (!this.symbols.has(name)) { + let symbol = new Wasm32Symbol(name); + this.symbols.set(name, symbol); + this.allsymbols.add(symbol); + } + this.symbols.get(name).add_reference(["data", addr]); + } + for (let [name, addr] of dyninfo.refun || []) { + addr += imports.sys.plt; + name = name.replace(/@@.*/, ""); + name = name.replace(/@.*/, ""); + if (!this.symbols.has(name)) { + let symbol = new Wasm32Symbol(name); + this.symbols.set(name, symbol); + this.allsymbols.add(symbol); + } + this.symbols.get(name).add_reference(["code", addr]); + } + for (let [name, addr, size] of dyninfo.copy || []) { + addr -= 0x4000; + addr += imports.sys.got; + if (!this.symbols.has(name)) { + let symbol = new Wasm32Symbol(name); + this.symbols.set(name, symbol); + this.allsymbols.add(symbol); + } + this.symbols.get(name).add_reference(["copy", addr, size]); + } +}; + +Wasm32LM.prototype.dyninfo_dump_symbols = + function (dyninfo, imports, module) +{ + for (let symbol of [...this.allsymbols].sort((a,b) => a.name > b.name)) { + if (symbol.references.length === 0) + continue; + console.log(`Symbol ${symbol.name}`); + for (let definition of symbol.definitions) { + console.log(" " + JSON.stringify(definition)) + } + console.log(" references:"); + for (let reference of symbol.references) { + console.log(" " + JSON.stringify(reference)) + } + } +}; + +Wasm32LM.prototype.dyninfo_apply_symbols = + function (dyninfo, imports, module) +{ + let vm = this.vm; + for (let symbol of [...this.allsymbols].sort((a,b) => a.name > b.name)) { + for (let definition of symbol.definitions) + for (let reference of symbol.references) { + if (definition[0] === "code" && + reference[0] === "code") { + vm.tableset(reference[1], definition[1]); + if (reference[1] === definition[1]) + throw "circular ref"; + } + if (definition[0] === "data" && + reference[0] === "data") + vm.HEAPU32[reference[1]>>2] = definition[1]; + if (definition[0] === "data" && + reference[0] === "copy") { + for (let i = 0; i < reference[2]; i++) + vm.HEAPU8[reference[1]+i] = vm.HEAPU8[definition[1]+i]; + vm.copies.push([reference[1],definition[1],reference[2],symbol.name]); + } + if (definition[0] === "code" && + reference[0] === "data") + vm.HEAPU32[reference[1]>>2] = definition[1]; + if (definition[0] === "data" && + reference[0] === "code") + throw `impossible symbol definition: ${symbol.name}`; + } + } +}; + +Wasm32LM.prototype.dyninfo_misc = + function (dyninfo, imports, module) +{ + for (let lib of dyninfo.libs || []) { + this.dep[lib] = -1; + } + + if ("lazy" in dyninfo) { + for (let [symbol, addr, version] of dyninfo.lazy || []) { + if (version === "libc.so") { + console.log("libc can't be lazy!"); + continue; + } + if (version === undefined) + continue; + console.log("forwarding " + (addr + imports.sys.plt - 1) + " to " + vm.table.get(addr + imports.sys.plt) + " version " + version); + vm.tableset(addr + imports.sys.plt - 1, addr + imports.sys.plt); + version = version.replace(/\.so$/, ".wasm"); + this.lazy[addr+imports.sys.plt] = version; + delete this.dep[version]; + } + } +}; + +Wasm32LM.prototype.dyninfo_apply = + function (dyninfo, imports, module) +{ + this.dyninfo_symbol_definitions (dyninfo, imports, module); + this.dyninfo_symbol_references (dyninfo, imports, module); + //this.dyninfo_dump_symbols (dyninfo, imports, module); + this.dyninfo_apply_symbols (dyninfo, imports, module); + this.dyninfo_fixups (dyninfo, imports, module); + this.dyninfo_misc (dyninfo, imports, module); +} + +var gWasm32VMIndex = 0; +/* A virtual memory object. */ +function Wasm32VM(sizes) +{ + this.index = gWasm32VMIndex++; + this.table = new WebAssembly.Table({element:"anyfunc",initial:sizes.tablesize,maximum:sizes.tablesize}); + this.table_length = 0; + this.shadowtable = {}; + this.sizes = sizes; + this.memory = new WebAssembly.Memory({initial: 1, maximum: 65535}); + + this.memory.grow(this.sizes.memsize / 65536 - 1); + + this.heap = this.memory.buffer; + this.HEAP8 = new Int8Array(this.memory.buffer); + this.HEAP16 = new Int16Array(this.memory.buffer); + this.HEAP32 = new Int32Array(this.memory.buffer); + this.HEAPU8 = new Uint8Array(this.memory.buffer); + this.HEAPU16 = new Uint16Array(this.memory.buffer); + this.HEAPU32 = new Uint32Array(this.memory.buffer); + this.HEAPF32 = new Float32Array(this.memory.buffer); + this.HEAPF64 = new Float64Array(this.memory.buffer); + + this.base_lm = new Wasm32LM(this); + + this.space_functions = new RangeAllocator(this.grow_functions.bind(this)); + this.space_pcs = new RangeAllocator(this.grow_pcs.bind(this)); + this.copies = []; +} + +Wasm32VM.prototype.fork = function () +{ + let ret = new Wasm32VM(this.sizes); + let count = 0; + let lim = this.%{zp[&zp->top_of_sbrk]}; + for (let i = 0; i < lim/4; i++) + ret.HEAP32[i] = this.HEAP32[i]; + lim = this.sizes.memsize; + for (let i = this.sizes.stackbottom/4; i < lim/4; i++) + ret.HEAP32[i] = this.HEAP32[i]; + return ret; +}; + +Wasm32VM.prototype.clonedesc = function () +{ + this.%{zp[&zp->top_of_sbrk]} += 64 * 1024 * 1024; + return { + sizes: this.sizes, + table: this.shadowtable, + table_length: this.table_length, + memory: this.memory.buffer.slice(0, this.%{zp[&zp->top_of_sbrk]}), + stack: this.memory.buffer.slice(this.sizes.stackbottom, this.sizes.stacksize), + + space_functions: this.space_functions.clonedesc(), + space_pcs: this.space_pcs.clonedesc() + }; +}; + +Wasm32VM.prototype.alive = function (clonedesc) +{ + var HEAPU32 = new Uint32Array(clonedesc.memory); + var HEAP32 = new Int32Array(clonedesc.memory); + var end = %{zp[&zp->top_of_sbrk]}/4; + for (var i = 0; i < end; i++) + this.HEAPU32[i] = HEAPU32[i]; + HEAPU32 = new Uint32Array(clonedesc.stack); + var size = clonedesc.sizes.stacksize / 4; + for (var i = 0; i < size; i++) + this.HEAPU32[clonedesc.sizes.stackbottom + i] = HEAPU32[i]; + + this.table_length = clonedesc.table_length; + this.shadowtable = clonedesc.table; + + for (var x in clonedesc.table) + this.table.set(x, this.table.get(clonedesc.table[x])); +}; + +Wasm32VM.prototype.tableset = function (x, y) +{ + if (!(y in this.shadowtable)) + this.shadowtable[y] = y; + + this.shadowtable[x] = this.shadowtable[y]; + + this.table.set(x, this.table.get(y)); +}; + +Wasm32VM.prototype.grow_functions = function (limit) +{ +}; + +Wasm32VM.prototype.grow_pcs = function (limit) +{ +}; + +var Wasm32ModuleIndex = 1; +var Wasm32Modules = {}; + +function Wasm32Module(process, bytes, name) +{ + this.bytes = bytes; + this.ctime = Date.now(); + this.index = Wasm32ModuleIndex++; + this.symtab = {}; + this.name = name; + Wasm32Modules[this.index] = this; + + process.modules.push(this); + this.process = process; +} + +Wasm32Module.prototype.fork = +Wasm32Module.prototype.clonedesc = function () +{ + return { + bytes: this.bytes, + module: this.module, + got: this.imports.sys.got, + gpo: this.imports.sys.gpo, + plt: this.imports.sys.plt, + dyninfo: this.dyninfo, + }; +}; + +Wasm32Module.prototype.reload_at = function (thread, vm, clonedesc) +{ + this.imports = {}; + this.imports.sys = {}; + + this.imports.sys.call = thread.extcall.bind(thread); + this.imports.sys.debug = thread.debug.bind(thread); + this.imports.sys.eh_return = thread.eh_return.bind(thread); + this.imports.sys.indcall = thread.indcall.bind(thread); + this.imports.sys.trace = thread.trace.bind(thread); + this.imports.sys.truncdfsi = thread.truncdfsi.bind(thread); + this.imports.sys.null = thread.null_called.bind(thread); + this.imports.sys.got = clonedesc.got; + this.imports.sys.gpo = clonedesc.gpo; + this.imports.sys.plt = clonedesc.plt; + this.imports.sys.table = vm.table; + this.imports.sys.memory = vm.memory; + + return Promise.resolve().then(() => WebAssembly.instantiate(this.module, this.imports)) + .then((instance) => { + this.instance = instance; + this.instime = Date.now(); + thread.exports = this.instance.exports; + thread.memory = vm.memory; + thread.table = vm.table; + if (this.instance.exports.entry !== undefined) { + thread.entry = this.imports.sys.plt + this.instance.exports.entry; + } + return true; + }).catch((e) => { + console.log("died instantiating: " + e + "\n" + e.stack); + }); +}; + +Wasm32Module.prototype.fill_table = function (thread, vm, got, plt) +{ + var pc = 0; + var seen = 0; + var count = 0; + do { + var str = pc.toString(16); + while (str.length < 16) + str = str + " "; + var f = thread.exports["f_" + str]; + if (f) { + vm.tableset(pc+plt, f); + seen++; + } else if (seen || count++ == 128) { + break; + } + pc++; + } while (true); +}; + +Wasm32Module.prototype.check_libs = function (thread) +{ + var promise = Promise.resolve(0); + + for (var lib0 in this.vm.base_lm.dep) { + let lib = lib0; + if (this.vm.base_lm.dep[lib] === -1) { + promise = promise.then(() => { + if (this.vm.base_lm.dep[lib] !== -1) { + return; + } + + if (typeof os !== "undefined") { + var module = new Wasm32Module(thread.process, os.file.readFile(os.getenv("WASMDIR") + "/wasm/" + lib, "binary"), lib); + return module.load(thread, this.vm).then(() => { + var mem = this.vm.%{zp[&zp->top_of_sbrk]}; + var tom = (mem + module.dyninfo.data_end - 0x4000 + 4095)&-4096; + this.vm.%{zp[&zp->top_of_sbrk] = "tom"} + var ret = module.instantiate(thread, this.vm, mem, true); + this.vm.base_lm.dep[lib] = 0; + + return ret; + }); + } else if (typeof fetch !== "undefined") { + var module; + return fetch(lib).then(response => { + if (response.ok) + return response.arrayBuffer(); + + return Promise.reject(response); + }).then((buffer) => { + module = new Wasm32Module(thread.process, buffer); + + return module.load(thread, this.vm); + }).then(() => { + var mem = this.vm.%{zp[&zp->top_of_sbrk]}; + var tom = (mem + module.dyninfo.data_end - 0x4000 + 4095)&-4096; + this.vm.%{zp[&zp->top_of_sbrk] = "tom"} + var ret = module.instantiate(thread, this.vm, mem, true); + this.vm.base_lm.dep[lib] = 0; + + return ret; + }).catch((e) => { + console.log("died resolving libraries: " + lib + "\n" + e + " " + e.stack); + }); + } + }); + } + } + + return promise; +}; + +Wasm32Module.prototype.load = function (thread, vm) +{ + this.vm = vm; + thread.vm = vm; + + this.modulePromise = WebAssembly.compile(this.bytes); + return this.modulePromise.then(module => { + this.module = module; + this.comtime = Date.now(); + var dyninfo; + var cs = WebAssembly.Module.customSections(module, "dyninfo" /* + ".json" */); + for (let c of cs) { + let s = abtoascii(c); + this.dyninfo = JSON.parse(s); + } + + return this.index; + }); +}; + +var lastdonetime = 0; + +Wasm32Module.prototype.instantiate = function (thread, vm, mem, recurse) +{ + this.imports = {}; + this.imports.sys = {}; + + this.imports.sys.call = thread.extcall.bind(thread); + this.imports.sys.debug = thread.debug.bind(thread); + this.imports.sys.eh_return = thread.eh_return.bind(thread); + this.imports.sys.indcall = thread.indcall.bind(thread); + this.imports.sys.trace = thread.trace.bind(thread); + this.imports.sys.truncdfsi = thread.truncdfsi.bind(thread); + this.imports.sys.null = thread.null_called.bind(thread); + this.imports.sys.got = mem; + this.imports.sys.gpo = vm.space_pcs.alloc(this.dyninfo.pc_end); + this.imports.sys.plt = vm.space_functions.alloc(this.dyninfo.plt_end); + this.imports.sys.table = vm.table; + this.imports.sys.memory = vm.memory; + return WebAssembly.instantiate(this.module, this.imports) + .then(instance => { + this.instance = instance; + this.instime = Date.now(); + thread.exports = this.instance.exports; + thread.memory = vm.memory; + thread.table = vm.table; + if (this.instance.exports.entry !== undefined) { + thread.entry = this.imports.sys.plt + this.instance.exports.entry; + } + this.fill_table(thread, vm, this.imports.sys.got, this.imports.sys.plt); + this.vm.base_lm.dyninfo_apply(this.dyninfo, this.imports, this); + lastdonetime = this.donetime = Date.now(); + + this.instance = instance; + if (recurse) + return this.check_libs(thread); + else + return true; + }).catch((e) => { + console.log("died instantiating: " + e + "\n" + e.stack); + }); +}; + +function Wasm32Thread(kport, process, threadpage, vm) +{ + this.pwd = os.getenv("PWD") || "."; + if (kport) + this.kport = kport; + else + this.kport = (new Wasm32Kernel).init_port(this); + if (this.kport) + this.kport.start(); + this.process = process; + process.threads.push(this); + var system = process.system; + + this.fds = process.fds; + + this.system = system; + system.threads.push(this); + + var sizes = { + tablesize: 65536, + memsize: 512 * 1024 * 1024, + stacksize: 1 * 1024 * 1024, + stackbottom: 511 * 1024 * 1024, + }; + vm ||= new Wasm32VM(sizes); + + process.vm = vm; + this.vm = vm; + this.HEAP8 = vm.HEAP8; + this.HEAP16 = vm.HEAP16; + this.HEAP32 = vm.HEAP32; + this.HEAPU8 = vm.HEAPU8; + this.HEAPU16 = vm.HEAPU16; + this.HEAPU32 = vm.HEAPU32; + this.HEAPF32 = vm.HEAPF32; + this.HEAPF64 = vm.HEAPF64; + + this.threadpage = threadpage; + + this.extcallRet = {}; + this.exports = {}; + this.types = {}; + this.types_by_id = {}; + this.vars = {}; + this.queue = []; + + var thread = this; + + this.types_by_id["Kc"] = this.types["string"] = { + constructor: function (thread, ptr) { + if (ptr === 0) + return null; + + var str = CStringAt(thread.HEAP8, ptr); + + this.thread = thread; + this.address = ptr; + this.str = str; + + this.toString = function () { + return this.str; + }; + + var ret = new String(this.str); + + ret.address = ptr; + + return ret; + }, + construct: function (thread, ptr) { + if (ptr === 0) + return null; + + return new this.constructor(thread, ptr); + }, + }; + + if (this.kport) + this.kport.start(); +} + +Wasm32Thread.prototype.find_type = function (typeid) +{ + typeid = typeid.replace(/^P/, ""); + + if (typeid in this.types_by_id) + return this.types_by_id[typeid]; + else if (typeid.match(/^P/)) + return new ThinThin.TypePtr(typeid, typeid); + else + switch (typeid) { + case "i": + return this.types_by_id[typeid] = new ThinThin.Type32(typeid); + + case "m": + return this.types_by_id[typeid] = new ThinThin.TypeU32(typeid); + + default: + return new ThinThin.TypeOpaque(typeid); + } +}; + +#{JSV tp("this.threadpage");} + +Wasm32Thread.prototype.stopped = function () +{ + return %{tp[&tp->stop_reason]}; +}; + +Wasm32Thread.prototype.pc = function () +{ + return %{tp[&tp->pc]}; +}; + +Wasm32Thread.prototype.pc0 = function () +{ + return %{tp[&tp->pc]}; +}; + +Wasm32Thread.prototype.dpc = function () +{ + return %{tp[&tp->pc]}; +}; + +Wasm32Thread.prototype.sp = function () +{ + return %{tp[&tp->sp]}; +}; + +Wasm32Thread.prototype.initsp = function () +{ + return %{tp[&tp->initsp]}; +}; + +var run; + +Wasm32Thread.prototype.stop = function (reason) +{ + var stopped = this.stopped(); + %{tp[&tp->stop_reason]} = reason; + + if (stopped && reason == 0) { + this.system.runqueue.push(this); + if (run) + run(); + } +}; + +Wasm32Thread.prototype.set_rv = function (rv) +{ + //%XXX{tp[&tp->rv]} = rv; + this.HEAPU32[8192 + 12 * 8>>2] = rv; +}; + +Wasm32Thread.prototype.set_pc = function (pc) +{ + %{tp[&tp->pc]} = pc; +}; + +Wasm32Thread.prototype.set_sp = function (sp) +{ + %{tp[&tp->sp]} = sp; +}; + +Wasm32Thread.prototype.set_initsp = function (initsp) +{ + %{tp[&tp->initsp]} = initsp; +}; + +Wasm32Thread.prototype.errorToCode = function (e) +{ + if (typeof e === "number" && e <= 0) + return e; + + return -%{EIO}; +}; + +Wasm32Thread.prototype.debug = function (dpc) +{ + return 0; +}; + +Wasm32Thread.prototype.check_debug = function () +{ + console.log("check_debug"); + return 1; +}; + +Wasm32Thread.prototype.write_debug = function (chp) +{ + //console.log(String.fromCharCode(this.vm.HEAPU8[chp])); + let r = redirect(`${gdbpipes}/out`); + putstr(String.fromCharCode(this.vm.HEAPU8[chp])); + redirect(r); + return 1; +}; + +let gdb_debug_string = ""; + +Wasm32Thread.prototype.read_debug = function (chp) +{ + while (gdb_debug_string === "") { + try { + gdb_debug_string = os.file.readFile(`${gdbpipes}/in`); + if (gdb_debug_string !== "") { + os.system(`rm -f ${gdbpipes}/in`); + } + } catch (e) { + } + } + this.vm.HEAPU8[chp] = gdb_debug_string.charCodeAt(0); + gdb_debug_string = gdb_debug_string.substr(1); + return 1; +}; + +Wasm32Thread.prototype.extcall = function (a, pc, sp1, b, c, d) +{ + var sp = sp1 - 56 - 16 + 16; + this.syscall_sp = sp; + var modstr = b; + var funstr = c; + var mod = CStringAt(this.HEAPU8, modstr); + var fun = CStringAt(this.HEAPU8, funstr); + + //console.log("//mod " + mod + " fun " + fun + "("+modstr+"/"+funstr+")"); + + if (sp in this.extcallRet) { + var ret = this.extcallRet[sp]; + + if (ret !== undefined) { + if (typeof Promise !== "undefined" && + ret instanceof Promise) { + this.stop(1); + return sp|1; + } + + this.set_rv(ret); + + delete this.extcallRet[sp]; + + return sp; + } + } + + var nargs = this.HEAP32[sp1+8>>2]; + if (nargs == -1) + nargs = 7; + var is_void = ((nargs & 0x40000000) != 0) != + ((nargs & 0x80000000) != 0); + var is_void_2 = this.HEAP32[sp1>>2] != 0; + + if (is_void) + nargs ^= 0x40000000; + + var args = []; + if (nargs >= 0) { + for (var ai = 0; ai < nargs; ai++) { + args[ai] = this.HEAP32[sp1+16+4*ai>>2]; + } + } + + let retv; + outer: + switch (mod) { + case "thinthin": + if (ThinThin[fun]) { + try { + retv = ThinThin[fun].apply(this, args); + break; + } catch (e) { + console.log("exception!"); + console.log(e); + console.log(e.stack); + retv = this.errorToCode(e); + break; + } + } + console.log ("could not find " + fun); + quit(0); + case "debug": { + switch (fun) { + case "check_debug": + case "write_debug": + case "read_debug": + //console.log(fun); + try { + retv = this[fun].apply(this, args); + break outer; + } catch (e) { + retv = this.errorToCode(e); + break outer; + } + } + } + default: + i.dont.exist++; + throw("giving up,", this.vm.index, "pc " + pc.toString(16) + " fun " + funstr.toString(16) + " " + fun + " mod " + modstr.toString(16) + " " + mod); + } + + if (typeof Promise !== "undefined" && + retv instanceof Promise) { + this.extcallRet[sp] = retv; + this.stop(1); + + retv.then(r => { + this.extcallRet[sp] = r; + if (this.system.runqueue.indexOf(this) === -1) + this.system.runqueue.push(this); + this.stop(0); + }).catch(e => { + console.log("exception"); + console.log(e); + console.log(e.stack); + this.extcallRet[sp] = this.errorToCode(e); + this.stop(0); + }); + + return sp|1; + } + + if (retv !== undefined && retv === retv) { + this.set_rv(retv); + } else if (retv !== retv) { + this.stop(1); + return sp|1; + } else { + throw "cannot resolve " + mod + ":" + fun; + } + + return sp; +}; + +Wasm32Thread.prototype.restart = function (dst, src, len, entry) +{ + var initsp = this.initsp(); + + this.restartCode = () => { + if (len) { + var i; + + for (i = 0; i < len; i++) + this.HEAP8[dst+i] = this.HEAP8[src+i]; + } else { + //datainit(); + } + + this.set_pc(entry>>4); + this.set_sp(initsp+16); + this.set_initsp(initsp); + + delete this.restartCode; + }; + + this.stop(0); + + return 0; +}; + +Wasm32Thread.prototype.step = function () +{ + var first = this.first; + if (first === undefined) + first = true; + var dpc = this.%{tp1[&tp1->initsp]}; + var pc0 = this.%{tp1[&tp1->pc]}; + var sp = this.%{tp1[&tp1->sp]}; + var rpc = 0; + var ret; + + while (true) { + if (first) { + ret = this.indcall(-1, sp+16, 0, 0, rpc, this.entry); + first = this.first = false; + } else { + ret = this.indcall(dpc, sp+16, 0, 0, rpc, pc0); + } + + if (ret & 3) { + sp = ret; + sp &= -4; + pc0 = this.HEAPU32[sp+8>>2]; + dpc = 0; + break; + } else { + sp = ret; //system.threads[0].HEAPU32[ret+16>>2]; + sp = this.HEAPU32[ret>>2]; + pc0 = this.HEAPU32[sp+8>>2]; + dpc = 0; //system.threads[0].HEAPU32[sp+28>>2]; + } + } + + this.%{tp1[&tp1->pc]} = pc0; + this.%{tp1[&tp1->initsp]} = dpc; + this.%{tp1[&tp1->sp]} = sp; + + return ret&-4; +}; + +Wasm32Thread.prototype.trace = function (off, pc0, dpc, a0, a1, a2) +{ + console.log("trace off " + off + " sp " + pc0 + " fp " + dpc + + a0 + " " + a1 + " " + a2); + + var start = new Date(); + + while ((new Date()) - start < 100); + return 0; +}; + +Wasm32Thread.prototype.truncdfsi = function (v) +{ + return v; +}; + +Wasm32Thread.prototype.null_called = function () +{ + console.log("NULL called!"); + quit(1); +}; + +function build_sig(sigstr) +{ + var ret = []; + ret.push(1); // 1 signature; + ret.push(0x60); // signature; + ret.push(sigstr.length - 3); + for (var i = 2; i < sigstr.length && sigstr[i] !== 'E'; i++) { + switch (sigstr[i]) { + case 'i': + ret.push(0x7f); + break; + case 'l': + ret.push(0x7e); + break; + case 'f': + ret.push(0x7d); + break; + case 'd': + ret.push(0x7c); + break; + } + } + + if (sigstr[1] == 'v') { + ret.push(0x00); + } else { + ret.push(0x01); + switch (sigstr[1]) { + case 'i': + ret.push(0x7f); + break; + case 'l': + ret.push(0x7e); + break; + case 'f': + ret.push(0x7d); + break; + case 'd': + ret.push(0x7c); + break; + } + } + + return ret; +} + +Wasm32Thread.prototype.indcall = function (mbz, sp, r0, r1, rpc, pc) +{ + return this.vm.table.get(pc)(mbz, sp, r0, r1, rpc, pc); +}; + +Wasm32Thread.prototype.set_arg = function (index, arg) +{ + this.HEAP32[8296 + index * 8 >> 2] = arg; +}; + +/* This is somewhat tricky. The dwarf expressions we want to generate are: + * DW_CFA_offset_extended_sf: r36 at cfa+8 + * DW_CFA_expression: r0 (DW_OP_breg2 (r2): 0; DW_OP_breg2 (r2): 0; DW_OP_deref; DW_OP_breg2 (r2): 0; DW_OP_minus; DW_OP_plus) + * DW_CFA_def_cfa_register: r0 + * + * (I have to try again to make the second expression less redundant). + * The point is that the stack pointer is implicitly set to the CFA, + * so we work around gcc's apparent inability to generate a + * DW_OP_call_frame_cfa opcode or use the CFA argument pushed onto the + * stack by CFA expressions. + */ + +Wasm32Thread.prototype.eh_return = function (fp, sp, handler) +{ + var a0 = this.HEAP32[fp+48>>2]|0; + var a1 = this.HEAP32[fp+56>>2]|0; + var a2 = this.HEAP32[fp+64>>2]|0; + var a3 = this.HEAP32[fp+72>>2]|0; + + this.set_arg(0, a0); + this.set_arg(1, a1); + this.set_arg(2, a2); + this.set_arg(3, a3); + + fp = this.HEAP32[this.HEAP32[fp>>2]>>2]|0; + + this.HEAP32[fp+16>>2] = handler - this.HEAP32[fp+28>>2]; + + fp |= 3; + + return fp; +}; + +Wasm32Thread.prototype.to_address = function (x) +{ + if (x === null) + return 0; + + if (typeof x === "number") + return x; + + if ("address" in x) + return x.address; + + throw "cannot convert " + x + " to address"; +}; + +Wasm32Thread.prototype.from_address = function (type, addr) +{ + if (addr === 0) + return null; + + var t = this.types_by_id[type]; + + return new t.constructor(this, address); +}; + +Wasm32Thread.prototype.freeze = function () +{ + return new FrozenWasm32Thread(this); +}; + +function Wasm32Process(system, fds) +{ + this.system = system; + system.processes.push(this); + + this.fds = fds ? fds : []; + this.ddroot = system.ddroot = new ThinThinDD(this, 4); + this.ddroot.path = "."; + this.ddcwd = system.ddroot; + + //system.ddroot.open(); + + this.functions = {}; + this.threads = []; + this.modules = []; +} + +Wasm32Process.prototype.fork = function (kport) +{ + let extcallRet = {}; + for (let sp in this.threads[0].extcallRet) { + extcallRet[sp] = 0; + } + let ret = this.system.restore(this.modules, this.threads[0].vm.fork(), this.threads.map(x => x.pc())[0], this.threads.map(x => x.sp())[0], kport, this.fds.map(x => x), extcallRet, this.threads[0].vm.HEAP8.slice(), this.threads[0].pwd, this.threads[0].vm.HEAP32.slice()); + return ret.then(process => { + process.threads[0].extcallRet = extcallRet; + return process; + }); +}; + +Wasm32Process.prototype.clonedesc = function () +{ + return { + modules: this.modules.map(x => x.clonedesc()), + fds: this.fds.map(x => x.clonedesc()), + }; +}; + +Wasm32Process.prototype.deffun = function(page, fo) +{ + this.functions[page] = fo; + + if (fo.name == "__pre_main") { + this.entry = fo.pc0; + premain_pc = fo.pc0; + } else if (fo.name == "_start") { + if (this.entry === undefined) + this.entry = fo.pc0; + main_pc = fo.pc0; + } else if (fo.name == "gdbstub_entry") { + gdbstub_pc = fo.pc0; + } +}; + +Wasm32Process.prototype.freeze = function () +{ + return new FrozenWasm32Process(this); +}; + +function FrozenWasm32Process(process) +{ + this.fds = process.fds; + this.threads = process.threads; + this.system = process.system; + this.functions = process.functions; + this.heap = process.heap; + + for (var i = 0; i < this.threads.length; i++) + this.threads[i] = this.threads[i].freeze(); + + var fds = []; + for (var i = 0; i < this.fds.length; i++) + fds[i] = this.fds[i].freeze(); + + this.fds = fds; + + delete this.system; + delete this.functions; +} + +FrozenWasm32Process.prototype.thaw = function (system) +{ + var n = new Wasm32Process(system); + + var fds = []; + for (var i = 0; i < this.fds.length; i++) { + var fd = this.fds[i]; + fds[i] = FrozenThinThinFD.prototype.thaw.call(fd, + n, + function (x) { return global.postMessage(x) }); + } + n.fds = fds; + + n.threads = []; + n.system = system; + n.functions = {}; + n.heap = new ArrayBuffer(this.heap.byteLength); + var newHEAP8 = new Int8Array(n.heap); + var oldHEAP8 = new Int8Array(this.heap); + for (var i = 0; i < this.heap.byteLength; i++) + newHEAP8[i] = oldHEAP8[i]; + + n.newHEAP8 = newHEAP8; + n.oldHEAP8 = oldHEAP8; + + n.HEAP8 = new Int8Array(n.heap); + n.HEAP16 = new Int16Array(n.heap); + n.HEAP32 = new Int32Array(n.heap); + n.HEAPU8 = new Uint8Array(n.heap); + n.HEAPU16 = new Uint16Array(n.heap); + n.HEAPU32 = new Uint32Array(n.heap); + n.HEAPF32 = new Float32Array(n.heap); + n.HEAPF64 = new Float64Array(n.heap); + + for (var i = 0; i < this.threads.length; i++) + n.threads[i] = FrozenWasm32Thread.prototype.thaw.call(this.threads[i], + n, + gMod); + + return n; +}; + +function MessageChannel(kernel) +{ + this.kernel = kernel; + this.port1 = new Object(); + this.port1.channel = this; + this.port2 = new Object(); + this.port2.channel = this; + this.port1.start = this.port2.start = () => {}; + this.port1.req = (kind, ...data) => { + var seq = this.kernel.seq++; + this.kernel.requesters[seq] = () => { + }; + return new Promise((resolve, reject) => { + var seq = this.kernel.seq++; + var msg = [kind, seq]; + msg.push(...data); + + this.port1.postMessage(msg); + }); + }; + this.port2.req = (kind, ...data) => { + var seq = this.kernel.seq++; + return new Promise((resolve, reject) => { + var msg = [kind, seq]; + msg.push(...data); + + this.kernel.requesters[seq] = data => { + resolve(data); + }; + this.port2.postMessage(msg); + }); + }; + this.port1.postMessage = data => { + this.port2.onmessage({data}); + }; + this.port2.postMessage = data => { + this.port1.onmessage({data}); + }; + + return this; +} + +/* The kernel: code that must run once only, even from workers. */ +function Wasm32Kernel() +{ + this.space_pids = new RangeAllocator(() => {}); + this.space_pids.alloc(2); + this.ports_by_pid = {}; + this.pids_by_port = new WeakMap(); + this.threads_by_port = new WeakMap(); + this.ppids = { 1: 1 }; + this.wait4 = {}; + this.seq = 0; + this.requesters = {}; + this.responders = {}; +} + +Wasm32Kernel.prototype.init_port = function (thread) +{ + let channel = new MessageChannel(this); + channel.port1.onmessage = event => { + this.message(channel.port1, event.data); + }; + channel.port2.onmessage = ({data}) => { + if (this.requesters[data[1]]) + this.requesters[data[1]](data[2]); + }; + + this.ports_by_pid[1] = channel.port1; + this.pids_by_port.set(channel.port1, 1); + this.ppids[1] = 1; + this.threads_by_port.set(channel.port1, thread); + this.threads_by_port.set(channel.port2, thread); + + return channel.port2; +} + +Wasm32Kernel.prototype.get_port = function () +{ + var channel = new MessageChannel(this); + + channel.port1.onmessage = event => { + this.message(channel.port1, event.data); + }; + + channel.port2.onmessage = ({data}) => { + this.requesters[data[1]](data[2]); + }; + + if (this.ports_by_pid[1] === undefined) { + this.ports_by_pid[1] = channel.port1; + this.pids_by_port.set(channel.port1, 1); + this.ppids[1] = 1; + } + + return [channel.port2, channel.port1]; +}; + +Wasm32Kernel.prototype.message = function (port, data) +{ + var req = data[0]; + var seq = data[1]; + + if (data[0] === "kill") { + var pid = data[1]; + var signal = data[2]; + + this.ports_by_pid[pid].postMessage(["kill", signal]); + } else if (data[0] === "fork") { + var pid = this.space_pids.alloc(1); + var [nport, tport] = this.get_port(); + + let newproc = this.threads_by_port.get(port).process.fork(nport); + this.ports_by_pid[pid] = tport; + this.pids_by_port.set(tport, pid); + this.ppids[pid] = this.pids_by_port.get(port); + + //console.log("assigned pid " + pid + " " + nport); + port.postMessage(["response", seq, [pid, nport]], [nport]); + } else if (data[0] === "exit") { + var exitcode = data[2]; + var pid = this.pids_by_port.get(port); + var ppid = this.ppids[pid]; + var pport = this.ports_by_pid[ppid]; + + if (this.ports_by_pid[ppid]) { + this.ports_by_pid[ppid].postMessage(["response", this.wait4[ppid], [pid, exitcode]]); + } + } else if (data[0] === "wait4") { + var pid = this.pids_by_port.get(port); + this.wait4[pid] = seq; + } else { + console.log(data); + throw "unknown message!"; + } +}; + +function Wasm32System(port) +{ + this.processes = []; + this.threads = []; + this.runqueue = []; + if (port) + this.port = port; +} + +Wasm32System.prototype.init_sp = function (vm, off, args, env) +{ + var HEAP8 = vm.HEAP8; + var HEAP32 = vm.HEAP32; + var i; + var eo = []; + var ao = []; + for (i = env.length-1; i >= 0; i--) { + var str = env[i]; + off -= str.length+1; + CStringTo(str, HEAP8, off); + eo.push(off); + } + for (i = args.length-1; i >= 0; i--) { + var str = args[i]; + off -= str.length+1; + CStringTo(str, HEAP8, off); + ao.push(off); + } + off &= -8; + + HEAP32[(off-=4)>>2] = 0; + + for (i = 0; i < eo.length; i++) { + HEAP32[(off-=4)>>2] = eo[i]; + } + + var envp = off; + + HEAP32[(off-=4)>>2] = 0; + + for (i = 0; i < ao.length; i++) { + HEAP32[(off-=4)>>2] = ao[i]; + } + + var argv = off; + var argc = ao.length; + + off &= -16; + + HEAP32[(off-=4)>>2] = 0; /* padding */ + HEAP32[(off-=4)>>2] = envp; + HEAP32[(off-=4)>>2] = argv; + HEAP32[(off-=4)>>2] = argc; + + HEAP32[(off-=4)>>2] = 3; /* argument count */ + off -= 4; + HEAP32[off>>2] = off+48; /* argument something */ + HEAP32[(off-=4)>>2] = 0; /* return FP */ + HEAP32[(off-=4)>>2] = 0; /* return PC */ + + if (off & 4) { + throw "unaligned off"; + } + + return off; +}; + +function remote_process(kernel, arraybuffer) +{ + var [kport, kport2] = kernel.get_port(); + kport.start(); + kport2.start(); + + var mchs = [new MessageChannel(), new MessageChannel(), new MessageChannel(), new MessageChannel(), new MessageChannel(), new MessageChannel()]; + var lports = mchs.map(x => x.port1); + var rports = mchs.map(x => x.port2); + + var worker = new Worker("wasm32-worker.js"); + + lports[5].onmessage = function (event) { + document.getElementById("debug").innerHTML = event.data; + }; + + return new Promise((resolve, reject) => { + worker.onmessage = () => resolve(lports); + worker.postMessage([kport, arraybuffer].concat(rports), [kport, arraybuffer].concat(rports)); + }) +} + +function abtoascii(ab) +{ + let h8 = new Uint8Array(ab); + let s = ""; + + for (var i = 0; i < h8.byteLength; i++) { + let c = h8[i]; + + if (c >= 0x80) + throw "non-ASCII byte"; + + s += String.fromCharCode(h8[i]); + } + + return s; +} + +Wasm32System.prototype.restore = async function (modules, vm, pc, sp, kport, + fds, extcallRet, heap, pwd, + heap32) +{ + var process; + var thread; + process = new Wasm32Process(this, fds); + thread = new Wasm32Thread(kport, process, 8192); + thread.pwd = pwd || "."; + thread.vm = vm; + thread.extcallRet = extcallRet; + process.vm = vm; + for (let module of [modules[0]]) { + var tos = (module.dyninfo.data_end + 4095) & -4096; + vm.%{zp[&zp->top_of_sbrk] = "tos"} + await module.load(thread, vm); + var ret = await module.instantiate(thread, vm, 0x4000, true); + var tos = (module.dyninfo.data_end + 4095) & -4096; + vm.%{zp[&zp->top_of_sbrk] = "tos"} + + process.HEAP8 = vm.HEAP8; + process.HEAP16 = vm.HEAP16; + process.HEAP32 = vm.HEAP32; + process.HEAPU8 = vm.HEAPU8; + process.HEAPU16 = vm.HEAPU16; + process.HEAPU32 = vm.HEAPU32; + process.HEAPF32 = vm.HEAPF32; + process.HEAPF64 = vm.HEAPF64; + + thread.heap = vm.heap; + thread.HEAP8 = vm.HEAP8; + thread.HEAP16 = vm.HEAP16; + thread.HEAP32 = vm.HEAP32; + thread.HEAPU8 = vm.HEAPU8; + thread.HEAPU16 = vm.HEAPU16; + thread.HEAPU32 = vm.HEAPU32; + thread.HEAPF32 = vm.HEAPF32; + thread.HEAPF64 = vm.HEAPF32; + + var HEAP32 = vm.HEAP32; + + thread.%{zp[&zp->top_of_memory] = "module.top_of_memory"} + thread.%{zp[&zp->thread_list]} = %{tp1}; + thread.%{zp[&zp->bottom_of_sbrk] = "module.start_of_sbrk"} + + thread.%{tp1[&tp1->next]} = %{tp1}; + thread.%{tp1[&tp1->prev]} = %{tp1}; + thread.%{tp1[&tp1->id] = "1"} + thread.%{tp1[&tp1->bottom_of_stack] = "module.bottom_of_stack"} + thread.%{tp1[&tp1->top_of_stack] = "module.top_of_stack"} + + module.top_of_stack = vm.sizes.stackbottom + vm.sizes.stacksize; + thread.%{tp1[&tp1->sp] = "sp"} + thread.%{tp1[&tp1->pc] = "pc"} + } + let count = 0; + var HEAP32 = heap32; + let lim = %{zp[&zp->top_of_sbrk]}; + for (let i = 0; i < lim/4; i++) + vm.HEAP32[i] = heap32[i]; + lim = vm.sizes.memsize; + for (let i = vm.sizes.stackbottom/4; i < lim/4; i++) + vm.HEAP32[i] = heap32[i]; + this.runqueue.unshift(thread); + thread.first = false; + if (gRetrigger) + gRetrigger(); + return process; +}; + +Wasm32System.prototype.instantiate = function (module, vm, args, env) +{ + var process; + var thread; + if (!this.threads.length) { + process = module.process; + thread = new Wasm32Thread(undefined, process, 8192, vm); + thread.main_thread = true; + return module.load(thread, vm).then(() => { + var ret = module.instantiate(thread, vm, 0x4000, true); + var tos = (module.dyninfo.data_end + 4095) & -4096; + + vm.%{zp[&zp->top_of_sbrk] = "tos"} + + return ret; + }).then(() => { + process.HEAP8 = vm.HEAP8; + process.HEAP16 = vm.HEAP16; + process.HEAP32 = vm.HEAP32; + process.HEAPU8 = vm.HEAPU8; + process.HEAPU16 = vm.HEAPU16; + process.HEAPU32 = vm.HEAPU32; + process.HEAPF32 = vm.HEAPF32; + process.HEAPF64 = vm.HEAPF64; + + thread.heap = vm.heap; + thread.HEAP8 = vm.HEAP8; + thread.HEAP16 = vm.HEAP16; + thread.HEAP32 = vm.HEAP32; + thread.HEAPU8 = vm.HEAPU8; + thread.HEAPU16 = vm.HEAPU16; + thread.HEAPU32 = vm.HEAPU32; + thread.HEAPF32 = vm.HEAPF32; + thread.HEAPF64 = vm.HEAPF32; + + var HEAP32 = vm.HEAP32; + + %{zp[&zp->top_of_memory] = "module.top_of_memory"} + %{zp[&zp->thread_list]} = %{tp1}; + %{zp[&zp->bottom_of_sbrk] = "module.start_of_sbrk"} + + %{tp1[&tp1->next]} = %{tp1}; + %{tp1[&tp1->prev]} = %{tp1}; + %{tp1[&tp1->id] = "1"} + %{tp1[&tp1->bottom_of_stack] = "module.bottom_of_stack"} + %{tp1[&tp1->top_of_stack] = "module.top_of_stack"} + + module.top_of_stack = module.sizes.stackbottom + module.sizes.stacksize; + var sp = this.init_sp(vm, module.top_of_stack, args, env); + %{tp1[&tp1->sp] = "sp"} + %{tp1[&tp1->initsp] = "sp"} + + %{tp1[&tp1->pc] = "process.entry"} + if (false && typeof document !== "undefined") { + new ThinThinFD(process, 0); + new VT100FD(process, document.getElementById("output2"), 1, 0); + new ThinThinFD(process, 2); + } else if (typeof document !== "undefined") { + new HTMLFD(process, undefined, 0); + new ThinThinFD(process, 1); + new ThinThinFD(process, 2); + + ////console.log('restarting'); + + // let first = true; + + // process.fds[0].inputPromise = function () { + // if (first) { + // first = false; + // return new Promise((resolve,reject) => { + // var content = document.getElementById("dot-input").innerHTML; + // //console.log(content); + // resolve(content); + // }); + // } else { + // //console.log("EOF"); + // return Promise.resolve(""); + // } + // }; + + // outstr = ""; + } else { + new ThinThinFD(process, 0); + new ThinThinFD(process, 1); + new ThinThinFD(process, 2); + } + this.runqueue.push(thread); + }); + } else { + process = this.processes[0]; + thread = this.threads[0]; + } + %{tp1[&tp1->pc] = "process.entry"} + if (false && typeof document !== "undefined") { + new ThinThinFD(process, 0); + new VT100FD(process, document.getElementById("output2"), 1, 0); + new ThinThinFD(process, 2); + } else if (typeof document !== "undefined") { + new HTMLFD(process, undefined, 0); + new ThinThinFD(process, 1); + new ThinThinFD(process, 2); + + ////console.log('restarting'); + + // let first = true; + + // process.fds[0].inputPromise = function () { + // if (first) { + // first = false; + // return new Promise((resolve,reject) => { + // var content = document.getElementById("dot-input").innerHTML; + // //console.log(content); + // resolve(content); + // }); + // } else { + // //console.log("EOF"); + // return Promise.resolve(""); + // } + // }; + + // outstr = ""; + } else { + new ThinThinFD(process, 0); + new ThinThinFD(process, 1); + new ThinThinFD(process, 2); + } + this.runqueue.push(thread); +}; + +Wasm32System.prototype.step = function () +{ + if (this.runqueue.length == 0) + return true; + + var thread = this.runqueue.shift(); + + thread.step(); + + if (!thread.stopped()) + this.runqueue.push(thread); +}; + +var ThinThin = {}; + +ThinThin.recopy = function () +{ + let vm = this.vm; + + for (let [dst, src, size, symbol] of vm.copies) { + for (let i = 0; i < size; i++) + vm.HEAPU8[dst+i] = vm.HEAPU8[src+i]; + } + + return 0; +}; + +ThinThin.abort = function () +{ + try { + i.dont.exist++; + } catch (e) { + console.log(e); + console.log(e.stack); + } +}; + +ThinThin.sbrk = function (size) +{ + size = (size + 4095) & -4096; + var ret = this.%{zp[&zp->top_of_sbrk]}; + + this.%{zp[&zp->top_of_sbrk]} += size; + + if (this.%{zp[&zp->top_of_sbrk]} > this.%{zp[&zp->top_of_memory]}) { + throw "OOM"; + } + + return ret; +}; +//** ThinThin.brk (unused) +ThinThin.brk = function (addr) +{ + return 0; +}; +//* globals +var data = {}; +var offsets = {}; + +var data_sections = []; +var gFunctions = {}; +var main_pc; +var premain_pc; +var gdbstub_pc; +var initsp; + +var MyCode; + +var args; +//** argument initialization +if (args) { +} +else if (typeof process !== "undefined") { + args = process.argv.slice(1); +} +else if (typeof scriptArgs !== "undefined") { + args = scriptArgs.slice(0); +} else { + args = [""]; +} +//* Syscall +function Syscall(number, argspec0, argspec1, argspec2, argspec3, argspec4) +{ + var argspecs = []; + if (argspec0 !== undefined) argspecs.push(argspec0); + if (argspec1 !== undefined) argspecs.push(argspec1); + if (argspec2 !== undefined) argspecs.push(argspec2); + if (argspec3 !== undefined) argspecs.push(argspec3); + if (argspec4 !== undefined) argspecs.push(argspec4); + return function(arg0, arg1, arg2, arg3, arg4, arg5) { + var args = [arg0, arg1, arg2, arg3, arg4, arg5]; + var rargs = [number]; + var i; + var ret; + for (i = 0; i < argspecs.length; i++) { + var spec = argspecs[i]; + if (spec == "ptr") { + rargs.push(this.HEAPU8); + } + rargs.push(args[i]); + } + ret = os.sys.call.apply(undefined, rargs); + + return ret; + }; +} +//* Syscall64 +function Syscall64(number, argspec0, argspec1, argspec2, argspec3, + argspec4, argspec5) +{ + var argspecs = []; + if (argspec0 !== undefined) argspecs.push(argspec0); + if (argspec1 !== undefined) argspecs.push(argspec1); + if (argspec2 !== undefined) argspecs.push(argspec2); + if (argspec3 !== undefined) argspecs.push(argspec3); + if (argspec4 !== undefined) argspecs.push(argspec4); + if (argspec5 !== undefined) argspecs.push(argspec5); + return function(arg0, arg1, arg2, arg3, arg4, arg5) { + var args = [arg0, arg1, arg2, arg3, arg4, arg5]; + var rargs = [number, 0]; + var i; + var ret; + for (i = 0; i < argspecs.length; i++) { + var spec = argspecs[i]; + switch (spec) { + case "fd": + case "u64": + rargs.push(args[i]); + rargs.push(0); + break; + + case "ptr": + case "str": + case "path": + rargs.push(this.HEAPU8); + rargs.push(args[i]); + break; + + case "ptrs": + case "strs": + var arg = []; + var j; + + for (j = 0; this.HEAP32[args[i]+4*j>>2]; j++) { + arg.push(this.HEAPU8); + arg.push(this.HEAP32[args[i]+4*j>>2]); + } + arg.push(0); + arg.push(0); + + rargs.push(arg); + } + } + ret = os.sys.call64.call(undefined, rargs); + + return ret; + }; +} +//* init_syscall +function init_syscall(name, number, ...args) +{ + Syscalls[name] = Syscalls[number] = new Syscall(number, ...args); +} +//* init_syscall64 +function init_syscall64(name, number, ...args) +{ + Syscalls[name] = Syscalls[number] = new Syscall64(number, ...args); +} +//* syscall table +var Syscalls = {}; + +init_syscall( "read", 0, "fd", "ptr", "u64"); +init_syscall( "write", 1, "fd", "ptr", "u64"); +init_syscall( "open", 2, "ptr", "u64", "u64"); +init_syscall( "close", 3, "fd"); +init_syscall( "stat", 4, "ptr", "ptr"); +init_syscall( "fstat", 5, "u64", "ptr"); +init_syscall( "lseek", 8, "u64", "u64", "u64"); +init_syscall( "rt_sigprocmask", 14, "u64", "ptr", "ptr"); +init_syscall( "ioctl_p", 16, "u64", "u64", "ptr"); +init_syscall( "access", 21, "ptr", "u64"); +init_syscall( "select", 23, "u64", "ptr", "ptr", "ptr", "ptr"); +init_syscall( "sched_yield", 24); +init_syscall( "dup", 32, "u64"); +init_syscall( "dup2", 33, "u64", "u64"); +init_syscall( "getpid", 39); +init_syscall( "clone", 56, "u64", "ptr", "ptr", "ptr", "ptr"); +init_syscall( "fork", 57); +init_syscall64("execve", 59, "path", "strs", "strs"); +//init_syscall( "exit", 60, "u64"); +init_syscall( "exit", 231, "u64"); +init_syscall( "wait4", 61, "u64", "ptr", "u64", "ptr"); +init_syscall( "kill", 62, "u64", "u64"); +init_syscall( "fcntl_v", 72, "u64", "u64"); +init_syscall( "fcntl_i", 72, "u64", "u64", "u64"); +init_syscall( "fcntl_p", 72, "u64", "u64", "ptr"); +init_syscall( "ftruncate", 77, "u64", "u64"); +init_syscall( "getcwd", 79, "ptr", "u64"); +init_syscall( "chdir", 80, "ptr"); +init_syscall( "fchdir", 81, "u64"); +init_syscall( "rename", 82, "ptr", "ptr"); +init_syscall( "mkdir", 83, "ptr", "u64"); +init_syscall( "rmdir", 84, "ptr"); +init_syscall( "unlink", 87, "ptr"); +init_syscall( "rename", 82, "ptr", "ptr"); +init_syscall( "chdir", 80, "ptr"); +init_syscall( "fchmod", 91, "fd", "u64"); +init_syscall( "gettimeofday", 96, "ptr", "u64"); +init_syscall( "getuid", 102); +init_syscall( "getgid", 104); +init_syscall( "geteuid", 107); +init_syscall( "getegid", 108); +init_syscall( "gettid", 186); +init_syscall( "tkill", 200); +init_syscall( "getdents", 217, "fd", "ptr", "u64"); +init_syscall( "clock_gettime", 228, "u64", "ptr"); +init_syscall( "exit_group", 231, "u64"); +init_syscall( "openat", 257, "fd", "ptr", "u64", "u64"); +init_syscall( "mkdirat", 258, "fd", "ptr", "u64"); +init_syscall( "newfstatat", 262, "fd", "ptr", "ptr", "u64"); +init_syscall( "unlinkat", 263, "fd", "ptr", "u64"); +init_syscall( "linkat", 265, "fd", "ptr", "fd", "ptr", "u64"); +init_syscall( "readlinkat", 267, "fd", "ptr", "ptr", "u64"); +init_syscall( "fchmodat", 268, "fd", "ptr", "u64", "u64"); +init_syscall( "faccessat", 269, "fd", "ptr", "u64", "u64"); +init_syscall( "ppoll", 271, "ptr", "u64", "ptr", "ptr"); +init_syscall( "utimensat", 280, "fd", "ptr", "ptr", "u64"); +init_syscall( "pipe2", 293, "ptr", "u64"); +init_syscall( "renameat2", 316, "fd", "ptr", "fd", "ptr", "u64"); +init_syscall( "execveat", 333, "fd", "ptr", "aptr", "aptr", "u64"); + +var SyscallSignatures = { + read: [ 0, "fd", "ptr", "u64"], + write: [ 1, "fd", "ptr", "u64"], + open: [ 2, "ptr", "u64", "u64"], + close: [ 3, "fd"], + stat: [ 4, "ptr", "ptr"], + fstat: [ 5, "u64", "ptr"], + lseek: [ 8, "u64", "u64", "u64"], + ioctl_p: [ 16, "u64", "u64", "ptr"], + access: [ 21, "ptr", "u64"], + select: [ 23, "u64", "ptr", "ptr", "ptr", "ptr"], + sched_yield: [ 24], + dup: [ 32, "u64"], + dup2: [ 33, "u64", "u64"], + getpid: [ 39], + clone: [ 56, "u64", "ptr", "ptr", "ptr", "ptr"], + fork: [ 57], + execve: [ 59, "path", "strs", "strs"], + exit: [ 60, "u64"], + wait4: [ 61, "u64", "ptr", "u64", "ptr"], + kill: [ 62, "u64", "u64"], + fcntl_v: [ 72, "u64", "u64"], + fcntl_i: [ 72, "u64", "u64", "u64"], + fcntl_p: [ 72, "u64", "u64", "ptr"], + ftruncate: [ 77, "u64", "u64"], + getcwd: [ 79, "ptr", "u64"], + chdir: [ 80, "ptr"], + fchdir: [ 81, "u64"], + rename: [ 82, "ptr", "ptr"], + mkdir: [ 83, "ptr", "u64"], + rmdir: [ 84, "ptr"], + unlink: [ 87, "ptr"], + rename: [ 82, "ptr", "ptr"], + chdir: [ 80, "ptr"], + gettimeofday: [ 96, "ptr", "u64"], + getuid: [102], + getgid: [104], + geteuid: [107], + getegid: [108], + getdents: [217, "fd", "ptr", "u64"], + clock_gettime:[227, "u64", "ptr"], + openat: [257, "fd", "ptr", "u64", "u64"], + mkdirat: [258, "fd", "ptr", "u64"], + newfstatat: [262, "fd", "path", "wptr", "u64"], + unlinkat: [263, "fd", "ptr", "u64"], + linkat: [265, "fd", "ptr", "fd", "ptr", "u64"], + readlinkat: [267, "fd", "ptr", "ptr", "u64"], + fchmodat: [268, "fd", "ptr", "u64", "u64"], + faccessat: [269, "fd", "ptr", "u64", "u64"], + ppoll: [271, "ptr", "u64", "ptr", "ptr"], + utimensat: [280, "fd", "ptr", "ptr", "u64"], + pipe2: [293, "ptr", "u64"], + renameat2: [316, "fd", "ptr", "fd", "ptr", "u64"], + execveat: [333, "fd", "ptr", "aptr", "aptr", "u64"], +}; +//* utility +//** TimeoutPromise +function TimeoutPromise(timeout) +{ + return new Promise((resolve, reject) => { + setTimeout(resolve, timeout); + }); +} +//** SplitPromise +function SplitPromise(n, promise) +{ + var resolve_lhs, resolve_rhs; + var reject_lhs, reject_rhs; + + var promise_lhs = new Promise((resolve, reject) => { + resolve_lhs = resolve; + reject_lhs = reject; + }); + + var promise_rhs = new Promise((resolve, reject) => { + resolve_rhs = resolve; + reject_rhs = reject; + }); + + promise.then(value => { + var lhs = value.substr(0, n); + var rhs = value.substr(n); + + resolve_rhs(rhs); + resolve_lhs(lhs); + }); + + return [promise_lhs, promise_rhs]; +} +//** DissectedPromise +function DissectedPromise() +{ + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); +} + +DissectedPromise.prototype.then = function (consequence) +{ + return this.promise.then(consequence); +}; +//** ImmediatePromise +function ImmediatePromise(v) +{ + if (v instanceof Promise) + return v; + + this.value = v; +} + +ImmediatePromise.prototype.then = function (consequence) +{ + return consequence(this.value); +}; +//** ThinThinRWer +function ThinThinRWer() +{ +} + +ThinThinRWer.prototype.kind = () => "rw"; + +ThinThinRWer.prototype.toString = function () +{ + if ("data" in this) + return this.kind() + ` "${this.data}"`; + return this.kind(); +}; + +ThinThinRWer.prototype.debug = function (f) +{ + this.debugf = f; + + return this.toString(); +}; +//** ThinThinReader +function ThinThinReader() +{ +} + +ThinThinReader.prototype = Object.create(ThinThinRWer.prototype); + +ThinThinReader.prototype.kind = () => "reader"; + +ThinThinReader.prototype.max = function () +{ + return 0; +}; + +ThinThinReader.prototype.consume = function (s) +{ + return 0; +}; + +ThinThinReader.prototype.done = function () +{ + return true; +}; +//** ThinThinWriter +function ThinThinWriter() +{ +} + +ThinThinWriter.prototype = Object.create(ThinThinRWer.prototype); + +ThinThinWriter.prototype.kind = () => "writer"; + +ThinThinWriter.prototype.provide = function (n) +{ + return ""; +}; + +ThinThinWriter.prototype.advance = function (n) +{ +}; + +ThinThinWriter.prototype.done = function () +{ + return true; +}; +//** ThinThinMetaReader +function ThinThinMetaReader(req, resolve, reject) +{ + this.req = req; + this.resolve = resolve; + this.reject = reject; +} + +ThinThinMetaReader.prototype = Object.create(ThinThinRWer.prototype); + +ThinThinMetaReader.prototype.kind = () => "metareader"; + +ThinThinMetaReader.prototype.request = function () +{ + return this.req; +}; + +ThinThinMetaReader.prototype.consume = function (data) +{ + this.resolve(data); +}; + +ThinThinMetaReader.prototype.done = function () +{ + return true; +}; +//** ThinThinMetaWriter +function ThinThinMetaWriter() +{ +} + +ThinThinMetaWriter.prototype = Object.create(ThinThinRWer.prototype); + +ThinThinMetaWriter.prototype.kind = () => "metawriter"; + +ThinThinMetaWriter.prototype.provide = function (request) +{ + return [[], []]; +}; + +ThinThinMetaWriter.prototype.done = function () +{ + return false; +}; +//** ThinThinFlow +function ThinThinFlow() +{ + this.readers = []; + this.writers = []; + this.metareaders = []; + this.metawriters = []; +} + +ThinThinFlow.prototype.reader = function (reader) +{ + this.readers.push(reader); + this.connect(); +}; + +ThinThinFlow.prototype.writer = function (writer) +{ + this.writers.push(writer); + this.connect(); +}; + +ThinThinFlow.prototype.metareader = function (reader) +{ + this.metareaders.push(reader); + this.metaconnect(); +}; + +ThinThinFlow.prototype.metawriter = function (writer) +{ + this.metawriters.push(writer); + this.metaconnect(); +}; + +ThinThinFlow.prototype.connect = function () +{ + if (this.readers.length && this.writers.length) { + var r = this.readers.shift(); + var w = this.writers.shift(); + var nmax; + + this.state = 0; + + Promise.resolve(r.max()).then(n => { + nmax = n; + return w.provide(n); + }).then(s => { + r.data = w.data = s; + if (s.length > nmax) + s = s.substr(0, nmax); + + return r.consume(s); + }).then(n => { + return w.advance(n); + }).then(() => { + Promise.all([Promise.resolve(r.done()).then(done => { + if (!done) + this.readers.push(r); + }), + Promise.resolve(w.done()).then(done => { + if (!done) + this.writers.push(w); + })]).then(() => { + this.connect(); + }); + })/*.catch(err => { + console.log(err); + r.close(err); + w.close(err); + this.readers.shift(); + this.writers.shift(); + });*/ + } +}; + +ThinThinFlow.prototype.metaconnect = function () +{ + if (this.metareaders.length && this.metawriters.length) { + var r = this.metareaders[0]; + var w = this.metawriters[0]; + + Promise.resolve(r.request()).then(request => { + return w.provide(request); + }).then(data => { + return r.consume(data); + }).then(() => { + Promise.all([Promise.resolve(r.done()).then(done => { + if (done) + this.metareaders.shift(); + }), + Promise.resolve(w.done()).then(done => { + if (done) + this.metawriters.shift(); + })]).then(() => { + this.metaconnect(); + }); + })/*.catch(err => { + console.log(err); + r.close(err); + w.close(err); + this.readers.shift(); + this.writers.shift(); + });*/ + } +}; +//** ThinThinFlowReader +function ThinThinFlowReader(nmax, resolve, reject) +{ + this.nmax = nmax; + this.resolve = resolve; + this.reject = reject; +} +ThinThinFlowReader.prototype = Object.create(ThinThinReader.prototype); + +ThinThinFlowReader.prototype.kind = () => "flow-reader"; + +ThinThinFlowReader.prototype.max = function () +{ + return this.nmax; +}; + +ThinThinFlowReader.prototype.consume = function (s) +{ + this.resolve(s); + + return s.length; +}; + +ThinThinFlowReader.prototype.done = function () +{ + return true; +}; + +ThinThinFlowReader.prototype.close = function (err) +{ + this.reject(err); +}; +//** ThinThinFlow +ThinThinFlow.prototype.read = function (n) +{ + return new Promise((resolve, reject) => { + this.reader(new ThinThinFlowReader(n, resolve, reject)); + }); +}; +//** ThinThinFlowWriter +function ThinThinFlowWriter(s, resolve, reject) +{ + this.s = s; + this.resolve = resolve; + this.reject = reject; +} +ThinThinFlowWriter.prototype = Object.create(ThinThinWriter.prototype); + +ThinThinFlowWriter.prototype.kind = () => "flow-writer"; + +ThinThinFlowWriter.prototype.provide = function (n) +{ + return this.s; +}; + +ThinThinFlowWriter.prototype.advance = function (n) +{ + this.resolve(n); +}; + +ThinThinFlowWriter.prototype.done = function () +{ + return true; +}; + +ThinThinFlowWriter.prototype.close = function (err) +{ + this.reject(err); +}; +//** ThinThinFlow +ThinThinFlow.prototype.write = function (s) +{ + return new Promise((resolve, reject) => { + this.writer(new ThinThinFlowWriter(s, resolve, reject)); + }); +}; + +ThinThinFlow.prototype.meta = function (request) +{ + return new Promise((resolve, reject) => { + this.metareader(new ThinThinMetaReader(request, resolve, reject)); + }); +}; + +ThinThinFlow.prototype.debug = function (name, f) +{ + var str = ""; + + str += `flow ${name}
`; + str += (this.readers.map(x => x.debug(f)).join(":")) + " <- " + (this.writers.map(x => x.debug(f))) + "["+this.state+"]"; + str += "
"; + str += (this.metareaders.map(x => x.debug(f)).join(":")) + " <= " + (this.metawriters.map(x => x.debug(f))) + "["+this.state+"]"; + str += "
"; + + this.debugf = f; + + return str; +}; +//** ThinThinHalf +function ThinThinHalf(flows, metas) +{ + Array.call(this); + this[0] = flows && flows[0] || new ThinThinFlow(); + this[1] = flows && flows[1] || new ThinThinFlow(); +} +ThinThinHalf.prototype = Object.create(Array.prototype); + +ThinThinHalf.prototype.reverse = function() +{ + return new ThinThinHalf([this[1], this[0]]); +} + +ThinThinHalf.prototype.debug = function (name, f) +{ + var str = ""; + + str += `half ${name}
`; + str += this[0].debug(`${name}[0]`, f); + str += this[1].debug(`${name}[1]`, f); + + this.debugf = f; + + return str; +}; + +ThinThinHalf.prototype.meta = function (request) +{ + return this[0].meta(request); +}; +//*** sendflow (unused?) +function sendflow(flow1, flow2, n, count) +{ + if (count === undefined) + count = 0; + + if (n === undefined) + n = Infinity; + + if (n === 0) + return Promise.resolve(count); + + flow1.read(n).then(s => { + return flow2.write(s); + }).then(k => { + count += k; + n -= k; + + return sendflow(flow1, flow2, n, count); + }); +} +//** ThinThinDD (kill) +function ThinThinDD(process, fdno) +{ + ThinThinFD.call(this, process, fdno); + this.entries = { + ".": this, + "..": this.parent, + } +} +ThinThinDD.prototype = Object.create(ThinThinFD.prototype); + +ThinThinDD.prototype.mode = function () +{ + return %{S_IFDIR + 0777}; +}; + +ThinThinDD.prototype.read = function () +{ + return -%{EISDIR}; +}; + +ThinThinDD.prototype.openat = function (path, flags) +{ + if (path[0] !== "/") + path = this.path + "/" + path; + if (path.match(/(emacs-lisp|progmodes|language|international|textmodes|vc)$/)) { + let ret = new ThinThinDD(this.process); + ret.path = path; + return Promise.resolve(ret); + } + if (path.match(/^\/dev\/(u?random)$/)) + return Promise.resolve(-%{ENOENT}); + try { + let data = os.file.readFile(path, "binary"); + if (data !== null) { + let view = new Uint8Array(data); + let str = ""; + for (let i = 0; i < view.length; i++) + str += String.fromCharCode(view[i]); + let ret = new ThinThinFD(this.process); + ret.path = path; + ret.read_string(str); + return Promise.resolve(ret); + } + } catch (e) { + } + try { + let data = os.file.readFile(path + "/.dir", "binary"); + if (data !== null) { + let ret = new ThinThinDD(this.process); + ret.path = path; + return Promise.resolve(ret); + } + } catch (e) { + } + if (flags & %{O_CREAT}) { + let ret = new ThinThinFD(this.process); + ret.path = path; + return Promise.resolve(ret); + } + return Promise.resolve(-%{ENOENT}); +}; + +ThinThinDD.prototype.walk = function (component, isdir) +{ + if (component in this.entries) + return this.entries[component]; + + var ret = Promise.resolve(this.discover(component, isdir)); + + return ret.then(entry => { + if (entry) + this.entries[component] = entry; + }); +}; +//** ThinThinFetchDD (kill) +function ThinThinFetchDD(process, parent, stem, cache, fdno) +{ + ThinThinDD.call(this, process, fdno); + this.stem = stem; + this.cache = cache; +} + +ThinThinFetchDD.prototype = Object.create(ThinThinDD.prototype); +//** ThinThinFetchDD +ThinThinFetchDD.prototype.discover = function (component, isdir) +{ + var url = this.stem + "/" + component; + var ret; + + if (isdir) { + ret = new ThinThinFetchDD(this.process, this, url, this.cache); + } else { + ret = new ThinThinFetchFD(this.process, url, this.cache); + } + + return ret; +}; + +ThinThinFetchDD.prototype.openat_dir = function (path) +{ + return (new ThinThinFetchDD(this.process, this, this.stem + "/" + path, + this.cache)) + .open(); +}; + +ThinThinFetchDD.prototype.openat = function (path) +{ + return (new ThinThinFetchFD(this.process, this.stem + "/" + path, + this.cache)) + .open(); +}; +//** ThinThinFD +function ThinThinFD(process, fdno) +{ + if (fdno === undefined) + for (fdno = 0; process.fds[fdno]; fdno++) + ; + this.fdno = fdno; + this.readers = 0; + this.readData = ""; + this.readPosition = 0; + this.readTo = 0; + this.onstuff = new Set(); + + this.writers = 0; + this.writeData = ""; + this.writePosition = 0; + this.writeTo = 0; + this.onprovide = new Set(); + this.onadvance = new Set(); + + process.fds[fdno] = this; + this.process = process; +} + +ThinThinFD.prototype.clonedesc = function () +{ + return { + fdno: this.fdno, + }; +}; + +ThinThinFD.prototype.mode = function () +{ + if (this.fdno >= 3) + return %{__S_IFREG + 0777}; + return %{__S_IFCHR + 0777}; +}; + +ThinThinFD.prototype.size = function () +{ + return this.readData.length; +} + +ThinThinFD.prototype.open = function () +{ + return Promise.resolve(this); +}; + +ThinThinFD.prototype.close = function () +{ + while (this.readers) + this.pause(); + + try { + i.dont.exist++; + } catch (e) { + //console.log(e.stack); + } + //console.log("closing " + this.fdno); + + //delete this.process.fds[this.fdno]; + + if (this.fdno >= 3 && this.writeData !== "") { + let buf = new ArrayBuffer(this.writeData.length); + let arr = new Uint8Array(buf); + for (let i = 0; i < this.writeData.length; i++) { + arr[i] = this.writeData.charCodeAt(i) & 0xff; + } + os.file.writeTypedArrayToFile(this.path, arr); + } + + return Promise.resolve(0); +}; + +ThinThinFD.prototype.advance = function (i) +{ + this.writeData = this.writeData.substr(i); + + var onadvance = this.onadvance; + this.onadvance = new Set(); + + for (var handler of onadvance) + handler(); +}; + +ThinThinFD.prototype.consume = function (i) +{ + this.readPosition += i; + //this.readData = this.readData.substr(i); +}; + +ThinThinFD.prototype.provideString = function (data) +{ + this.writeData += data; + + var onprovide = this.onprovide; + this.onprovide = new Set(); + + for (var handler of onprovide) + handler(); +}; + +ThinThinFD.prototype.provideOpened = function () +{ + this.writeOpened = true; + + var onprovide = this.onprovide; + this.onprovide = new Set(); + + for (var handler of onprovide) + handler(); +}; + +ThinThinFD.prototype.provideError = function (error) +{ + this.writeError = error; + this.writeEOF = true; + + var onprovide = this.onprovide; + this.onprovide = new Set(); + + for (var handler of onprovide) + handler(); + + this.close(); +}; + +ThinThinFD.prototype.provideEOF = function () +{ + this.writeEOF = true; + + var onprovide = this.onprovide; + this.onprovide = new Set(); + + for (var handler of onprovide) + handler(); +}; + +ThinThinFD.prototype.startProviding = function () +{ +}; + +ThinThinFD.prototype.stuffString = function (data) +{ + this.readData += data; + + var onstuff = this.onstuff; + this.onstuff = new Set(); + + + for (var handler of onstuff) { + handler(); + } +}; + +ThinThinFD.prototype.stuffOpened = function () +{ + this.readOpened = true; + + var onstuff = this.onstuff; + this.onstuff = new Set(); + + for (var handler of onstuff) + handler(); +}; + +ThinThinFD.prototype.stuffError = function (error) +{ + this.readError = error; + this.readEOF = true; + + var onstuff = this.onstuff; + this.onstuff = new Set(); + + for (var handler of onstuff) + handler(); + + this.close(); +}; + +ThinThinFD.prototype.stuffEOF = function () +{ + this.readEOF = true; + + var onstuff = this.onstuff; + this.onstuff = new Set(); + + for (var handler of onstuff) + handler(); +}; + +ThinThinFD.prototype.startStuffing = function () +{ + this.stuffing = true; + + if (this.startStuffingResolve) { + this.startStuffingResolve(Infinity); + delete this.startStuffingResolve; + } + + if (!this.activeInputPromise) { + this.activeInputPromise = new DissectedPromise(); + + this.inputPromise().then(data => { + this.stuffString(data); + }); + } + + return Promise.resolve(); +}; + +ThinThinFD.prototype.stopStuffing = function () +{ + if (this.activeInputPromise) { + var activeInputPromise = this.activeInputPromise; + delete this.activeInputPromise; + activeInputPromise.resolve(data); + } +}; + +ThinThinFD.prototype.unpause = function (len) +{ + if (this.readTo < this.readPosition + len) + this.readTo = this.readPosition + len; + + if (this.readers++ === 0) { + return new Promise((resolve, reject) => { + this.startStuffing().then(() => { + this.stuffOpened(); + resolve(); + }); + }); + } + + return Promise.resolve(); +}; + +ThinThinFD.prototype.pause = function () +{ + if (--this.readers === 0) + this.stopStuffing(); +}; + +ThinThinFD.prototype.makeSeekable = function () +{ + this.consume = i => { + this.readPosition += i; + }; +}; + +ThinThinFD.prototype.readAsString = function () +{ + return new Promise((resolve, reject) => { + if (!this.readEOF) { + this.onstuff.add(() => { + resolve(this.readData); + }); + this.unpause(Infinity); + return; + } + + resolve(this.readData); + }); +}; + +ThinThinFD.prototype.readAsArrayBuffer = function () +{ + return Promise.resolve(this.readAsString()).then(str => { + var ab = new ArrayBuffer(str.length); + var av = new Uint8Array(ab); + + for (var i = 0; i < str.length; i++) + av[i] = str.charCodeAt(i)&0xff; + + return ab; + }); +}; + +ThinThinFD.prototype.read_stdin = function () +{ + let line; + if (typeof(this.readData) !== "string") + this.readData = ""; + while ((line = readline()) !== null) + this.readData += line + "\n"; +}; + +ThinThinFD.prototype.read_string = function (string) +{ + this.readData = string; +}; + +/* .read(undefined, 0, 0) returns a promise that resolves to 0 when + * data is available to read. */ +ThinThinFD.prototype.read = function (heap, ptr, len) +{ + if (this.fdno === 0 && this.read_stdin) { + this.read_stdin(); + this.read_stdin = undefined; + } + if (ptr === undefined) + ptr = 0; + if (len === undefined) + len = 0; + + if (this.readData.length > this.readPosition || + this.readEOF) { + var i; + + for (i=0; i i+j); + } + + if (this.readPosition >= this.readData.length) + this.readEOF = true; + if (false) + try { + this.readData = readline(); + if (this.readData == null) { + this.readData = ""; + this.readEOF = true; + } else { + this.readData += "\n"; + } + } catch (e) { + console.log("exception!"); + this.readData = ""; + this.readEOF = true; + } + return this.read(heap, ptr, len); + + return this.unpause(len).then(() => { + return new Promise((resolve, reject) => { + this.onstuff.add(() => { + this.pause(); + resolve(this.read(heap, ptr, len)); + }); + + if (this.readData.length > this.readPosition || + this.readEOF) { + var onstuff = this.onstuff; + for (var handler of onstuff) + handler(); + } + }); + }); +}; + +ThinThinFD.prototype.available = function () +{ + if (this.readData.length > this.readPosition) + return Promise.resolve(this.readData.length - this.readPosition); + + if (!this.readAvailable) { + var readAvailable = () => { + this.pause(); + this.onstuff.delete(this.readAvailable); + delete this.readAvailable; + }; + this.unpause(0).then(() => { + this.readAvailable = readAvailable; + this.onstuff.add(this.readAvailable); + }); + } + + return this.unpause(0).then(() => { + this.pause(); + return this.readData.length - this.readPosition; + }); +}; + +ThinThinFD.prototype.inputPromise = function () { + this.stuffEOF(); + return Promise.resolve(""); +}; + +ThinThinFD.prototype.write = function (heap, ptr, len) { + var initialPosition = this.writePosition; + + if (ptr === undefined) + ptr = 0; + if (len === undefined) + len = 0; + + if (len == 0) + return Promise.resolve(0); + + var data = ""; + + for (let i=0; i= 3) { + this.writeData += data; + } else { + putstr(data); + } + + return data.length; + } +}; + +ThinThinFD.prototype.providee = function () { + var handler; handler = () => { + var len = this.writeData.length; + + putstr(this.writeData); + + this.advance(len); + + this.onprovide.add(handler); + }; + + this.onprovide.add(handler); +}; + +ThinThinFD.prototype.domPromise = function () { + return document.getElementById("output"); +}; + +ThinThinFD.prototype.connectPort = function (port) { + var n = 0; + + var handler = () => { + if (this.readData.length > this.readPosition || + this.readEOF) { + if (n > this.readData.length - this.readPosition) + n = this.readData.length - this.readPosition; + port.postMessage(this.readData.substr(this.readPosition, n)); + this.consume(n); + } + + this.onstuff.add(handler); + }; + + port.onmessage = event => { + var data = event.data; + var kind = data[0]; + + if (kind === "write") { + this.provideString(data[1]); + } else if (kind === "read") { + n = data[1]; + + handler(); + } + }; + + port.postMessage(["read"]); +}; + +ThinThinFD.prototype.readdir = function () +{ + return []; +}; + +ThinThinFD.prototype.clone = function () +{ + return this; +}; + +ThinThinFD.prototype.freeze = function () +{ + return new FrozenThinThinFD(this); +}; +//** ThinThinFDReader +function ThinThinFDReader(fd, len) +{ + this.fd = fd; + this.len = len; +} +ThinThinFDReader.prototype = Object.create(ThinThinReader.prototype); + +ThinThinFDReader.prototype.kind = () => "fd-reader"; + +ThinThinFDReader.prototype.max = function () +{ + return Infinity; +}; + +ThinThinFDReader.prototype.consume = function (s) +{ + return new Promise((resolve, reject) => { + this.fd.stuffString(s); + resolve(s.length); + }); +}; + +ThinThinFDReader.prototype.done = function () +{ + return true; +}; +//** ThinThinFDWriter +function ThinThinFDWriter(fd) +{ + this.fd = fd; +} + +ThinThinFDWriter.prototype = Object.create(ThinThinWriter.prototype); + +ThinThinFDWriter.prototype.kind = () => "fd-writer"; + +ThinThinFDWriter.prototype.provide = function (n) +{ + return this.fd.writeData; +}; + +ThinThinFDWriter.prototype.consume = function (s) +{ + return new Promise((resolve, reject) => { + this.fd.stuffString(s); + resolve(s.length); + }); +}; + +ThinThinFDWriter.prototype.advance = function (n) +{ + return this.fd.advance(n); +}; + +ThinThinFDWriter.prototype.done = function () +{ + return true; +}; +//** ThinThinHalfFD +function ThinThinHalfFD(process, half, fdno) +{ + ThinThinFD.call(this, process, fdno); + + this.half = half; + + this.providee(); +} +ThinThinHalfFD.prototype = Object.create(ThinThinFD.prototype); + +ThinThinHalfFD.prototype.unpause = function (len) +{ + return new Promise((resolve, reject) => { + this.half[1].reader(new ThinThinFDReader(this, len)); + + resolve(); + }); +}; + +ThinThinHalfFD.prototype.providee = function () { + var handler; handler = () => { + if (this.writeData.length > 0) { + this.half[1].writer(new ThinThinFDWriter(this)); + } + + this.onprovide.add(handler); + }; + + this.onprovide.add(handler); +}; + +ThinThinHalfFD.prototype.modeXXX = function () +{ + return new Promise((resolve, reject) => { + this.half[1].metareader(new ThinThinMetaReader(["mode"], resolve, + reject)); + }); +}; + +ThinThinHalfFD.prototype.openat = function (path, flags) +{ + return new Promise((resolve, reject) => { + this.half[1].metareader(new ThinThinMetaReader(["openat", path, flags], resolve, reject)); + }).then(response => { + var obj = response[0][0]; + + if ((obj instanceof Array) && obj.length && (obj[0] instanceof File)) { + var pipe = new ThinThinHalf(); + new FileListHalf(pipe, obj); + return new ThinThinHalfFD(this.process, pipe); + } else if (obj instanceof File) { + var pipe = new ThinThinHalf(); + new FileHalf(pipe, obj); + return new ThinThinHalfFD(this.process, pipe); + } + }); +}; + +ThinThinHalfFD.prototype.readdir = function () +{ + return new Promise((resolve, reject) => { + this.half[1].metareader(new ThinThinMetaReader(["readdir"], resolve, reject)); + }); +}; + +ThinThinHalfFD.prototype.write = function (heap, ptr, len) +{ + let ret = ThinThinFD.prototype.write.call(this, heap, ptr, len); + if (!this.other.reader_promise) { + return new Promise(r => this.other.reader_promise_promise = r).then(() => { + this.other.reader_promise(this.readData); + return ret; + }) + } + this.other.reader_promise(CStringAt(heap, ptr)); + return ret; +}; + +ThinThinHalfFD.prototype.read = function (heap, ptr, len) +{ + if (!this.reader) { + this.reader = new Promise(r => { + this.reader_promise = r; + if (this.reader_promise_promise) { + this.reader_promise_promise(); + } + }); + } + return this.reader.then(data => { + this.readData = data; + return ThinThinFD.prototype.read.call(this, heap, ptr, len); + }); +}; + +function SparseMetaWriter(half) +{ + this.half = half; +} +SparseMetaWriter.prototype = Object.create(ThinThinMetaWriter.prototype); + +SparseMetaWriter.prototype.kind = () => "sparse-metawriter"; + +SparseMetaWriter.prototype.provide = function (request) +{ + if (request[0] === "readdir") + return this.half.readdir(); + else if (request[0] === "openat") + return this.half.openat(request[1], request[2], request[3]); + else if (request[0] === "openat_dir") + return this.half.openat_dir(request[1], request[2], request[3]); + else + return this.half.base.meta(request); +}; + +//** SparseHalf +function SparseHalf(flows, base) +{ + ThinThinHalf.call(this, flows); + this.base = base; + + this.entries = {}; +} +SparseHalf.prototype = Object.create(ThinThinHalf.prototype); + +SparseHalf.prototype.readdir = function () +{ + var ret = []; + + for (var name in this.entries) + ret.push(name); + + return ret; +}; + +SparseHalf.prototype.openat = function (path, flags, mode) +{ + var ret = this.base.openat(path, flags, mode); + + return ret.then(obj => { + this.entries[path] = true; + }).then(() => { + return ret; + }); +}; + +SparseHalf.prototype.openat_dir = function (path, flags, mode) +{ + var ret = this.base.openat_dir(path, flags, mode); + + return ret.then(obj => { + this.entries[path] = true; + }).then(() => { + return ret; + }); +}; + +//** HTMLReader +function HTMLReader() +{ +} +HTMLReader.prototype = Object.create(ThinThinReader.prototype); + +HTMLReader.prototype.kind = () => "html-reader"; + +HTMLReader.prototype.max = function () +{ + return Infinity; +}; + +HTMLReader.prototype.consume = function (s) +{ + putstr(s); + + return s.length; +}; + +HTMLReader.prototype.done = function () +{ + return false; +}; +//** BufferedWriter +function BufferedWriter(writer) +{ + this.writer = writer; +} +BufferedWriter.prototype = Object.create(ThinThinWriter.prototype); + +BufferedWriter.prototype.kind = function () +{ + return "buffered(" + this.writer.kind() + ")"; +}; + +BufferedWriter.prototype.provide = function (n) +{ + if (this.buffer === undefined) { + return this.writer.provide(n).then(s => { + this.buffer = s; + }).then(() => { + this.writer.advance(this.buffer.length); + }).then(() => { + return this.buffer; + }); + } + + return this.buffer; +}; + +BufferedWriter.prototype.advance = function (n) +{ + this.buffer = this.buffer.substr(n); + if (this.buffer === "") + delete this.buffer; +}; + +BufferedWriter.prototype.done = function () +{ + if (this.buffer) + return false; + + return this.writer.done(); +}; +//** HTMLWriter +function HTMLWriter(dom) +{ + this.dom = dom; +} +HTMLWriter.prototype = Object.create(ThinThinWriter.prototype); + +HTMLWriter.prototype.kind = () => "html-writer"; + +HTMLWriter.prototype.activate = function () +{ + if (!this.active) { + this.active = true; + + this.node = document.createElement("div"); + this.node_ta = document.createElement("textarea"); + this.node_ta.spellcheck = false; + this.node_ta.inputmode = "verbatim"; + this.node_eof = document.createElement("button"); + this.node_eof.innerHTML = "EOF"; + + this.node_send = document.createElement("button"); + this.node_send.innerHTML = "Send"; + + this.node.appendChild(this.node_ta); + this.node.appendChild(this.node_send); + this.node.appendChild(this.node_eof); + + this.dom.appendChild(this.node); + + this.node_eof.onclick = e => { + this.resolve(this.node_ta.value); + this.deactivate(); + this.node_ta.value = ""; + + e.stopPropagation(); + e.preventDefault(); + }; + + this.node_send.onclick = e => { + this.resolve(this.node_ta.value); + this.node_ta.value = ""; + + e.stopPropagation(); + e.preventDefault(); + }; + } + + this.node.focus(); +}; + +HTMLWriter.prototype.deactivate = function () +{ + if (this.active) { + delete this.active; + + this.dom.removeChild(this.node); + } +} + +HTMLWriter.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + + this.activate(); + }); +}; + +HTMLWriter.prototype.advance = function (n) +{ +}; + +HTMLWriter.prototype.done = function () +{ + return !this.active; +}; +//** BufferedWriter +function BufferedWriter(writer) +{ + this.writer = writer; +} +BufferedWriter.prototype = Object.create(ThinThinWriter.prototype); + +BufferedWriter.prototype.kind = function () +{ + return "buffered(" + this.writer.kind() + ")"; +}; + +BufferedWriter.prototype.provide = function (n) +{ + if (this.buffer === undefined) { + return this.writer.provide(n).then(s => { + this.buffer = s; + }).then(() => { + this.writer.advance(this.buffer.length); + }).then(() => { + return this.buffer; + }); + } + + return this.buffer; +}; + +BufferedWriter.prototype.advance = function (n) +{ + this.buffer = this.buffer.substr(n); + if (this.buffer === "") + delete this.buffer; +}; + +BufferedWriter.prototype.done = function () +{ + if (this.buffer) + return false; + + return this.writer.done(); +}; +//** HTMLWriter +function HTMLWriter(dom) +{ + this.dom = dom; +} +HTMLWriter.prototype = Object.create(ThinThinWriter.prototype); + +HTMLWriter.prototype.kind = () => "html-writer"; + +HTMLWriter.prototype.activate = function () +{ + if (!this.active) { + this.active = true; + + this.node = document.createElement("div"); + this.node_ta = document.createElement("textarea"); + this.node_eof = document.createElement("button"); + this.node_eof.innerHTML = "EOF"; + + this.node_send = document.createElement("button"); + this.node_send.innerHTML = "Send"; + + this.node.appendChild(this.node_ta); + this.node.appendChild(this.node_send); + this.node.appendChild(this.node_eof); + + this.dom.appendChild(this.node); + + this.node_eof.onclick = e => { + this.resolve(this.node_ta.value); + this.deactivate(); + this.node_ta.value = ""; + + e.stopPropagation(); + e.preventDefault(); + }; + + this.node_send.onclick = e => { + this.resolve(this.node_ta.value); + this.node_ta.value = ""; + + e.stopPropagation(); + e.preventDefault(); + }; + } + + this.node.focus(); +}; + +HTMLWriter.prototype.deactivate = function () +{ + if (this.active) { + delete this.active; + + this.dom.removeChild(this.node); + } +} + +HTMLWriter.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + + this.activate(); + }); +}; + +HTMLWriter.prototype.advance = function (n) +{ +}; + +HTMLWriter.prototype.done = function () +{ + return !this.active; +}; +//** HTMLMetaWriter +function HTMLMetaWriter(half) +{ + this.half = half; +} +HTMLMetaWriter.prototype = Object.create(ThinThinMetaWriter.prototype); + +HTMLMetaWriter.prototype.kind = () => "html-metawriter"; + +HTMLMetaWriter.prototype.provide = function (request) +{ + if (request[0] === "AYT") + return [["yes", "HTML"]]; + else if (request[0] === "mode") + return [%{__S_IFCHR + 0777}]; + else if (request[0] === "readdir") + return this.half.readdir(); + else if (request[0] === "openat") + return this.half.openat(request[1], request[2]); + else if (request[0] === "openat_dir") + return this.half.openat_dir(request[1], request[2]); + + return [[]]; +}; +//** HTMLHalf +function HTMLHalf(flows, dom) +{ + ThinThinHalf.call(this, flows); + + this.dom = dom; + this[0].reader(new HTMLReader()); + this[1].writer(new BufferedWriter(new HTMLWriter(this.dom))); + this.meta = new HTMLMetaWriter(this); + this[0].metawriter(this.meta); + this[1].metawriter(this.meta); +} +HTMLHalf.prototype = Object.create(ThinThinHalf.prototype); + +HTMLHalf.prototype.openat = function (path, flags) +{ + return new Promise((resolve, reject) => { + this.pickdir().then(dir => { + if (this.input_f) + this.dom.removeChild(this.input_f); + return dir.getFilesAndDirectories(); + }).then(fs => { + for (var f of fs) { + if (f.name === path) { + resolve(f); + break; + } + } + reject(-%{ENOENT}); + }); + this.input_f = document.createElement("input"); + this.input_f.accept = "*/*"; + this.input_f.allowdirs = true; + //this.input_f.webkitdirectory = "true"; + //this.input_f.multiple = true; + this.input_f.type = "file"; + this.dom.appendChild(this.input_f); + + this.input_f.addEventListener("change", () => { + if (this.input_d) + this.dom.removeChild(this.input_d); + var flist = this.input_f.files; + + if (!flist || flist.length !== 1) + resolve([["no such file"]]); + + resolve([[flist[0]]]); + + this.dom.removeChild(this.input_f); + }); + }); +}; + +HTMLHalf.prototype.openat_dir = function (path, flags) +{ + return new Promise((resolve, reject) => { + this.input_f = document.createElement("input"); + this.input_f.type = "file"; + this.input_f.multiple = true; + this.dom.appendChild(this.input_f); + + this.input_f.addEventListener("change", () => { + var flist = this.input_f.files; + var array = [...flist]; + + resolve([[array]]); + + this.dom.removeChild(this.input_f); + }); + }); +}; + +HTMLHalf.prototype.pickdir = function () +{ + if (this.directory) + return this.directory; + + return new Promise((resolve, reject) => { + this.input_d = document.createElement("input"); + this.input_d.type = "file"; + this.input_d.allowdirs = true; + this.dom.appendChild(this.input_d); + + this.input_d.addEventListener("change", () => { + this.input_d.getFilesAndDirectories().then(ds => { + var promises = []; + + for (var d of ds) { + this.directory = d; + resolve(d); + this.dom.removeChild(this.input_d); + + return; + } + }); + }); + }); +}; + +HTMLHalf.prototype.readdir = function () +{ + var names = []; + + return Promise.resolve(this.pickdir()).then(d => { + return d.getFilesAndDirectories().then(fs => { + for (var f of fs) + names.push(f.name); + + return names; + }); + }); +}; +//** CLogReader +function CLogReader() +{ +} +CLogReader.prototype = Object.create(ThinThinReader.prototype); + +CLogReader.prototype.kind = () => "clog-reader"; + +CLogReader.prototype.max = function () +{ + return Infinity; +}; + +CLogReader.prototype.consume = function (s) +{ + console.log("clog: " + s); + + return s.length; +}; + +CLogReader.prototype.done = function () +{ + return false; +}; +//** CLogHalf +function CLogHalf(flows) +{ + ThinThinHalf.call(this, flows); + + this[0].reader(new CLogReader()); +} +CLogHalf.prototype = Object.create(ThinThinHalf.prototype); +//** ForwardReader +function ForwardReader(half) +{ + this.half = half; +} +ForwardReader.prototype = Object.create(ThinThinReader.prototype); + +ForwardReader.prototype.kind = () => "forward-reader"; + +ForwardReader.prototype.max = function () +{ + return Infinity; +}; + +ForwardReader.prototype.consume = function (s) +{ + return this.half[0].write(s).then(() => { + return s.length; + }); +}; + +ForwardReader.prototype.done = function () +{ + return false; +}; +//** ForwardWriter +function ForwardWriter(half) +{ + this.half = half; +} +ForwardWriter.prototype = Object.create(ThinThinWriter.prototype); + +ForwardWriter.prototype.kind = () => "forward-writer"; + +ForwardWriter.prototype.provide = function (n) +{ + return this.half[1].read(n); +}; + +ForwardWriter.prototype.advance = function (n) +{ +}; + +ForwardWriter.prototype.done = function () +{ + return false; +}; +//** ForwardMetaWriter +function ForwardMetaWriter(half) +{ + this.half = half; +} +ForwardMetaWriter.prototype = Object.create(ThinThinMetaWriter.prototype); + +ForwardMetaWriter.prototype.kind = () => "forward-metawriter"; + +ForwardMetaWriter.prototype.provide = function (request) +{ + return this.half[0].meta(request); +}; + +ForwardMetaWriter.prototype.done = function () +{ + return false; +}; +//** ForwardHalf +function ForwardHalf(flows, half) +{ + ThinThinHalf.call(this, flows); + + this[0].reader(new ForwardReader(half)); + this[1].writer(new ForwardWriter(half)); + this[0].metawriter(new ForwardMetaWriter(half)); + this[1].metawriter(new ForwardMetaWriter(half)); +} +ForwardHalf.prototype = Object.create(ThinThinHalf.prototype); +//** MessagePortReader1 +function MessagePortReader1(port) +{ + this.port = port; + this.port.addEventListener("close", () => { + delete this.port; + }); +} +MessagePortReader1.prototype = Object.create(ThinThinReader.prototype); + +MessagePortReader1.prototype.kind = () => "messageport-reader"; + +MessagePortReader1.prototype.max = function () +{ + return Infinity; +}; + +MessagePortReader1.prototype.consume = function (s) +{ + if (!this.port) + return 0; + + this.port.postMessage(s); + + return s.length; +}; + +MessagePortReader1.prototype.done = function () +{ + return !this.port; +}; +//** MessagePortWriter1 +function MessagePortWriter1(port) +{ + this.port = port; + this.port.addEventListener("close", () => { + delete this.port; + }); +} +MessagePortWriter1.prototype = Object.create(ThinThinWriter.prototype); + +MessagePortWriter1.prototype.kind = () => "messageport-writer"; + +MessagePortWriter1.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + if (!this.port) + reject("EOF"); + + this.oldonmessage = this.port.onmessage; + this.port.onmessage = event => { + resolve(event.data); + this.port.onmessage = this.oldonmessage; + }; + }); +}; + +MessagePortWriter1.prototype.advance = function (n) +{ +}; + +MessagePortWriter1.prototype.done = function () +{ + return !this.port; +}; +//** MessagePortHalf1 +function MessagePortHalf1(flows, port) +{ + ThinThinHalf.call(this, flows); + + this[0].reader(new MessagePortReader1(port)); + this[1].writer(new MessagePortWriter1(port)); +} +MessagePortHalf1.prototype = Object.create(ThinThinHalf.prototype); +//** RRPort +function RRPort(port) +{ + this.port = port; + this.seq = 1; + + this.requesters = {}; + this.responders = {}; + this.rejecters = {}; + + this.port.addEventListener("message", event => { + var [kind, seq, data] = event.data; + + if (kind === "response") { + var requester = this.requesters[seq]; + delete this.requesters[seq]; + delete this.rejecters[seq]; + + requester(data); + } else if (kind in this.responders) { + var responder = this.responders[kind]; + + Promise.resolve(responder(data)).then(([resp, transfer]) => { + var msg = ["response", seq]; + msg.push(resp); + this.port.postMessage(msg, transfer); + }); + } else { + console.log("could not deal with it"); + } + }, false); + this.port.start(); + + this.port.onclose = event => { + for (var seq in this.rejecters) + this.rejecters[seq](event); + }; +} + +RRPort.prototype.open = function () +{ + return Promise.resolve(); +}; + +RRPort.prototype.req = function (kind, ...data) +{ + return new Promise((resolve, reject) => { + var seq = this.seq++; + var msg = [kind, seq]; + msg.push(...data); + + this.requesters[seq] = data => { + resolve(data); + }; + this.rejecters[seq] = err => { + reject(err); + }; + this.port.postMessage(msg); + }); +}; + +RRPort.prototype.resp = function (kind, f) +{ + this.responders[kind] = f; +}; +//** RRPortReader +function RRPortReader(rr) { + this.rr = rr; +} +RRPortReader.prototype = Object.create(ThinThinReader.prototype); + +RRPortReader.prototype.kind = () => "rrport-reader"; + +RRPortReader.prototype.max = function () +{ + return this.rr.req("max"); +}; + +RRPortReader.prototype.consume = function (s) +{ + return this.rr.req("consume", s); +}; + +RRPortReader.prototype.done = function () +{ + return this.rr.req("done"); +}; +//** RRPortWriter +function RRPortWriter(rr) { + this.rr = rr; + this.max = {}; + + this.rr.resp("max", () => new Promise((resolve, reject) => { + if (this.max.n !== undefined) { + resolve([[this.max.n]]); + } else { + this.max.resolve = resolve; + this.max.reject = reject; + } + })); + + this.consume = {}; + this.consume1 = {}; + this.consume2 = {}; + + this.rr.resp("consume", s => new Promise((resolve, reject) => { + this.consume1.resolve(s); + if (this.consume1.n !== undefined) + resolve([[this.consume1.n]]); + else { + this.consume2.resolve = resolve; + this.consume2.reject = reject; + } + })); + + this.rr.resp("done", () => new Promise((resolve, reject) => { + resolve([false]); + })); + this.rr.open(); +} +RRPortWriter.prototype = Object.create(ThinThinWriter.prototype); + +RRPortWriter.prototype.kind = () => "rrport-writer"; + +RRPortWriter.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + if (this.max.resolve !== undefined) { + this.max.resolve([[n]]); + } else { + this.max.n = n; + } + + this.consume1.resolve = resolve; + this.consume1.reject = reject; + }); +}; + +RRPortWriter.prototype.advance = function (n) +{ + return new Promise((resolve, reject) => { + this.consume2.resolve([[n]]); + resolve(); + }); +}; + +RRPortWriter.prototype.done = function () +{ + return false; +}; +//** RRPortMetaWriter +function RRPortMetaWriter(rr) +{ + this.rr = rr; +} +RRPortMetaWriter.prototype = Object.create(ThinThinMetaWriter.prototype); + +RRPortMetaWriter.prototype.kind = () => "rrport-metawriter"; + +RRPortMetaWriter.prototype.provide = function (request) +{ + return this.rr.req("meta", request); +}; +//** RRPortHalf +function RRPortHalf(flows, rr, metawriter) +{ + ThinThinHalf.call(this, flows); + + this[0].reader(new RRPortReader(rr)); + this[1].writer(new RRPortWriter(rr)); + if (metawriter) { + this.meta = new RRPortMetaWriter(rr); + this[0].metawriter(this.meta); + this[1].metawriter(this.meta); + } else { + rr.resp("meta", request => new Promise((resolve, reject) => { + this[0].metareader(new ThinThinMetaReader(request, data => { console.log(data); resolve([data]); }, reject)); + })); + } +} +RRPortHalf.prototype = Object.create(ThinThinHalf.prototype); +//** WebSocketWriter +function WebSocketWriter(websocket) +{ + this.websocket = websocket; + this.websocket.addEventListener("message", ev => this.onmessage(ev)); + this.websocket.addEventListener("close", ev => this.onclose(ev)); +} +WebSocketWriter.prototype = Object.create(ThinThinWriter.prototype); + +WebSocketWriter.prototype.kind = () => "websocket-writer"; + +WebSocketWriter.prototype.onmessage = function (event) +{ + this.resolve(event.data); +}; + +WebSocketWriter.prototype.onclose = function (event) +{ + this.reject("EOF"); +}; + +WebSocketWriter.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + return reader.readAsArrayBuffer(this.webSocket); + }).then(abuf => { + var view = new Uint8Array(abuf); + var s = ""; + for (var i = 0; i < abuf.byteLength; i++) + s += String.fromCharCode(view[i]); + + return s; + }); +}; + +WebSocketWriter.prototype.advance = function (n) +{ +}; + +WebSocketWriter.prototype.done = function () +{ + return true; +}; +//** WebSocketHalf +function WebSocketHalf(flows, webSocket) +{ + ThinThinHalf.call(this, flows); + this[1].writer(new BufferedWriter(new WebSocketWriter(webSocket))); +} +WebSocketHalf.prototype = Object.create(ThinThinHalf.prototype); +//** FileListMetaWriter +function FileListMetaWriter(filelist) +{ + this.filelist = filelist; +}; +FileListMetaWriter.prototype = Object.create(ThinThinMetaWriter.prototype); + +FileListMetaWriter.prototype.kind = () => "filelist-metawriter"; + +FileListMetaWriter.prototype.provide = function (request) +{ + if (request[0] === "openat") { + for (var i = 0; i < this.filelist.length; i++) + if (this.filelist[i].name === request[1]) + return Promise.resolve([[this.filelist[i]]]); + return Promise.reject(); + } else if (request[0] === "readdir") { + var names = []; + + for (var i = 0; i < this.filelist.length; i++) + names.push(this.filelist[i].name); + + return Promise.resolve([[names]]); + } +}; +//** FileListHalf +function FileListHalf(flows, filelist) +{ + ThinThinHalf.call(this, flows); + + this.meta = new FileListMetaWriter(filelist); + this[0].metawriter(this.meta); + this[1].metawriter(this.meta); +} +FileListHalf.prototype = Object.create(ThinThinHalf.prototype); +//** FileWriter +function FileWriter(file) +{ + this.file = file; +} +FileWriter.prototype = Object.create(ThinThinWriter.prototype); + +FileWriter.prototype.kind = () => "file-writer"; + +FileWriter.prototype.provide = function (n) +{ + return new Promise((resolve, reject) => { + var reader = new FileReader(); + + reader.onload = () => { + resolve(reader.result); + }; + return reader.readAsArrayBuffer(this.file); + }).then(abuf => { + var view = new Uint8Array(abuf); + var s = ""; + for (var i = 0; i < abuf.byteLength; i++) + s += String.fromCharCode(view[i]); + + return s; + }); +}; + +FileWriter.prototype.advance = function (n) +{ +}; + +FileWriter.prototype.done = function () +{ + return true; +}; +//** FileHalf +function FileHalf(flows, file) +{ + ThinThinHalf.call(this, flows); + this[1].writer(new BufferedWriter(new FileWriter(file))); +} +FileHalf.prototype = Object.create(ThinThinHalf.prototype); +//* globals +var gLimboFD; +var gLimboPath; + +var gRemoteFDs = {}; +//** FrozenThinThinFD +function FrozenThinThinFD(fd) +{ + var rfdno; + + for (rfdno = 0; rfdno in gRemoteFDs; rfdno++); + + gRemoteFDs[rfdno] = fd; + + this.rfdno = rfdno; + this.fdno = fd.fdno; +} + +FrozenThinThinFD.prototype.thaw = function (process, target) +{ + var fd = new RemoteFD(process, target, this.rfdno, this.fdno); + gRemoteFDs[this.rfdno] = fd; + fd.target = target; + + return fd; +}; +//** ThinThinFetchFD (kill) +function ThinThinFetchFD(process, url, cache, fdno) +{ + ThinThinFD.call(this, process, fdno); + this.url = url; + this.cache = cache; + this.makeSeekable(); +} + +ThinThinFetchFD.prototype = Object.create(ThinThinFD.prototype); + +ThinThinFetchFD.prototype.open = function () +{ + return new Promise((resolve, reject) => { + this.unpause(0).then(() => { + var handler = () => { + if (this.readError) { + reject(this.readError); + } else if (this.readOpened || this.readEOF) { + resolve(this); + } else { + this.onstuff.add(handler); + } + }; + handler(); + }); + }); +}; + +ThinThinFetchFD.prototype.startStuffing = function () +{ + var url = this.url; + + delete this.url; + + if (url === undefined) { + return Promise.reject("EOF"); + } + + delete this.cache; + + if (this.cache) { + var req = new Request(url); + + return this.cache.match(req).then(response => { + if (response === undefined) { + console.log("have to fetch " + url); + return fetch(url); + } + return response; + }).then(response => { + this.cache.put(url, response.clone()); + if (response.ok) + return response.arrayBuffer(); + + return Promise.reject(response); + }).then(abuf => { + var view = new Uint8Array(abuf); + var text = ""; + + for (var i = 0; i < abuf.byteLength; i++) { + var cc = view[i]; + if (cc & 0x80) cc += 0x100; + text += String.fromCharCode(cc); + } + + this.stuffString(text); + this.stuffEOF(); + }); + } + + return fetch(url).then(response => { + if (response.ok) + return response.arrayBuffer(); + + return Promise.reject(response); + }).then(abuf => { + var view = new Uint8Array(abuf); + var text = ""; + + for (var i = 0; i < abuf.byteLength; i++) { + var cc = view[i]; + if (cc & 0x80) cc += 0x100; + text += String.fromCharCode(cc); + } + + this.stuffString(text); + this.stuffEOF(); + }); +}; + +ThinThinFetchFD.prototype.inputPromise = function () +{ + var url = this.url; + + delete this.url; + + if (!url) { + return Promise.resolve(""); + } + +}; +//** SparseDD (kill) +function SparseDD(base, fdno) +{ + ThinThinDD.call(this, base.process, fdno); + this.base = base; + + this.entries = {}; +} +SparseDD.prototype = Object.create(ThinThinDD.prototype); + +SparseDD.prototype.mode = function () +{ + return this.base.mode(); +}; + +SparseDD.prototype.open = function () +{ + if (this.readOpened) + return Promise.resolve(this); + + return this.base.open().then(fd => { + return this.readMeta(); + }).then(() => { + this.stuffOpened(); + + return this; + }); +}; + +SparseDD.prototype.readMeta = function () +{ + return this.base.openat(".dirs").then(fd => { + return fd.readAsString(); + }).then(str => { + var dirs = str.split(/\0/); + + for (var dir of dirs) { + dir = dir.replace(/^\.\//, ""); + this.entries[dir] = true; + } + + return true; + }); +}; + +SparseDD.prototype.openat = function (path) +{ + var m; + if (m = path.match(/^([^/]+)\/(.+)$/)) { + return this.openat(m[1]).then(fd => { + return fd.open().then(() => { + return fd; + }); + }).then(fd => { + return fd.openat(m[2]); + }); + } else { + if (path === ".") { + return Promise.resolve(this); + } + if (path in this.entries) + return this.base.openat_dir(path).then(fd => { + return fd.open().then(() => { + return new SparseDD(fd); + }); + }); + else + return this.base.openat(path); + } +}; +//** ReadableFD +function ReadableFD(process, readable, fdno) +{ + ThinThinFD.call(this, process, fdno); + this.readable = readable; + this.makeSeekable(); +}; + +ReadableFD.prototype = Object.create(ThinThinFD.prototype); + +ReadableFD.prototype.startStuffing = function () +{ + var readable = this.readable; + this.onend = () => { + this.stuffEOF(); + }; + this.ondata = (data) => { + var text = ""; + + for (var i = 0; i < data.length; i++) { + var cc = data[i]; + if (cc & 0x80) cc += 0x100; + text += String.fromCharCode(cc); + } + + this.stuffString(text); + }; + + readable.on("end", this.onend); + readable.on("data", this.ondata); + + return Promise.resolve(); +}; + +ReadableFD.prototype.stopStuffing = function () +{ + var readable = this.readable; + + readable.removeListener("end", this.onend); + delete this.onend; + + readable.removeListener("data", this.ondata); + delete this.ondata; +}; +//** WritableFD +function WritableFD(process, writable, fdno) +{ + ThinThinFD.call(this, process, fdno); + this.writable = writable; +}; + +WritableFD.prototype = Object.create(ThinThinFD.prototype); + +WritableFD.prototype.outputPromise = function (heap, ptr, len) +{ + var data = ""; + + if (len == 0) + return 0; + + for (let i=0; i { + var data = event.data; + var seq = data[0]; + + this.onmessage[seq](data); + }; +} +MessagePortDD.prototype = Object.create(ThinThinDD.prototype); + +MessagePortDD.prototype.openat = function (path) +{ + return new Promise((resolve, reject) => { + var seq = this.seq++; + this.onmessage[seq] = (data) => { + delete this.onmessage[seq]; + var success = data[1]; + + if (success) + resolve(new MessagePortFD(this.process, data[2])); + else + reject(data[2]); + } + + this.port.postMessage([seq, "openat", path]); + }); +}; + +MessagePortDD.prototype.openat_dir = function (path) +{ + return new Promise((resolve, reject) => { + var seq = this.seq++; + this.onmessage[seq] = (data) => { + delete this.onmessage[seq]; + var success = data[1]; + + if (success) + resolve(new MessagePortDD(this.process, data[2])); + else + reject(data[2]); + } + + this.port.postMessage([seq, "openat_dir", path]); + }); +}; +//** RemoteFD +function RemoteFD(process, target, rfdno, fdno) +{ + ThinThinFD.call(this, process, fdno); + + gRemoteFDs[rfdno] = this; + this.target = target; + this.rfdno = rfdno; +} + +RemoteFD.prototype = Object.create(ThinThinFD.prototype); + +RemoteFD.prototype.inputPromise = function () +{ + return new Promise((resolve, reject) => { + this.target(["read", this.rfdno], "*"); + this.stuffString = (data) => { + resolve(data); + delete this.stuffString; + }; + }); +}; + +RemoteFD.prototype.outputPromise = function (heap, ptr, len) +{ + var buf = new ArrayBuffer(len); + var buf8 = new Uint8Array(buf); + for (var i = 0; i < buf.byteLength; i++) + buf8[i] = heap[ptr+i]; + + this.target(["send", this.rfdno, buf]); + + return Promise.resolve(len); +}; +//** FSDD +var fs; + +//if (typeof global !== "undefined") +// fs = require('fs'); + +function FSDD(process, root, fdno) +{ + + ThinThinDD.call(this, process, fdno); + this.root = root; +} + +FSDD.prototype = Object.create(ThinThinDD.prototype); + +FSDD.prototype.openat = function (path, flags, mode) +{ + if (!path.match(/^\//)) + path = this.root + "/" + path; + if (flags === undefined) + flags = "r"; + if (mode === undefined) + mode = 0; + return new Promise((resolve, reject) => { + fs.open(path, flags, mode, (err, fd) => { + if (err) + reject(err); + else + resolve(new ReadableFD(this.process, fs.createReadStream + (null, + { flags: "r", + encoding: null, + fd: fd, + mode: mode, + autoClose: true + }))); + }); + }); +}; +//** syscalls +if (typeof(os) !== "undefined" && + typeof(os.sys) !== "undefined") { + for (var syscall in Syscalls) + ThinThin[syscall] = Syscalls[syscall]; + ThinThin.exit = function (code) { + console.log("exiting"); + if (os.getenv("STATS")) + console.log("runtime: " + (Date.now() - lastdonetime)); + quit(code); + if (code != 0 && code !== undefined) + throw "Exit Status " + code; + else + throw new SuccessException(); + }; + ThinThin.gethostname = function (addr, len) { + this.HEAP8[addr] = 0; + + return 0; + }; + ThinThin.restart = function (dst, src, len, entry) + { + return this.restart(dst, src, len, entry); + }; + ThinThin.fork = function () { + return os.sys.fork(); + }; +} else { + if (typeof global !== "undefined") { + ThinThin.fcntl_v = function () { + return 0; + }; + ThinThin.clock_gettime = function (clk_id, timespec) + { + var date = new Date(); + var s = date / 1000.0; + var ns = (date % 1000.0) * 1000000.0; + + %{timespec[×pec::tv_sec] = 0} + %{timespec[×pec::tv_nsec] = 0} + %{timespec[×pec::tv_sec] = "s"} + %{timespec[×pec::tv_nsec] = "ns"} + + return 0; + }; + } else { + } + ThinThin.gethostname = function (addr, len) { + this.HEAP8[addr] = 0; + + return 0; + }; + ThinThin.read = function (fdno, ptr, len) { + var fd = this.fds[fdno]; + + if (!fd) + return -%{EBADF}; + + if (len == 0) + return 0; + + var ret = fd.read(this.HEAPU8, ptr, len); + + return ret; + }; + ThinThin[1] = ThinThin.write = function (fdno, ptr, len) { + var fd = this.fds[fdno]; + + if (!fd) + return -%{EBADF}; + + if (len == 0) + return 0; + + return fd.write(this.HEAPU8, ptr, len); + }; + ThinThin.close = function (fdno) { + var fd = this.fds[fdno]; + + if (!fd) + return -%{EBADF}; + + return fd.close(); + }; + ThinThin.newfstatat = function (fdno, pathstr, statbufptr, flags) { + var dd = this.fds[fdno] || this.process.ddroot; + var path = CStringAt(this.HEAPU8, pathstr); + if (this.pwd && path[0] !== "/") + path = this.pwd + "/" + path; + + if (path === "/home/pip/g/wasm/wasm32-unknown-none/wasm32-unknown-none/share/terminfo") { + let fd = dd; + %{statbufptr[&stat::st_nlink] = 1LL} + %{statbufptr[&stat::st_mode] = "fd.mode()"} + %{statbufptr[&stat::st_size] = "fd.size()"} + %{statbufptr[&stat::st_blksize] = "fd.size()"} + %{statbufptr[&stat::st_blocks] = "1"} + return 0; + } + if (path === "" && (flags & %{AT_EMPTY_PATH})) { + return ThinThin.fstat.call(this, fdno, statbufptr); + } + + if (!dd) + return -%{EBADF}; + + if (path.length === 0) + return Promise.resolve(dd).then(fd => { + var off; + for (off = 0; off < %{sizeof(struct stat)}; off += 4) + this.HEAP32[statbufptr+off>>2] = 0; + + %{statbufptr[&stat::st_nlink] = 1LL} + %{statbufptr[&stat::st_mode] = "fd.mode()"} + %{statbufptr[&stat::st_size] = "fd.size()"} + %{statbufptr[&stat::st_blksize] = "fd.size()"} + %{statbufptr[&stat::st_blocks] = "1"} + + return 0; + }); + + if ("openat" in dd) { + return dd.openat(path, flags).then(fd => { + if (typeof fd === "number") + return fd; + var off; + for (off = 0; off < %{sizeof(struct stat)}; off += 4) + this.HEAP32[statbufptr+off>>2] = 0; + + %{statbufptr[&stat::st_nlink] = 1LL} + %{statbufptr[&stat::st_mode] = "fd.mode()"} + %{statbufptr[&stat::st_size] = "fd.size()"} + %{statbufptr[&stat::st_blksize] = "fd.size()"} + %{statbufptr[&stat::st_blocks] = "1"} + + return 0; + }); + } else { + var fd = dd; + var off; + for (off = 0; off < %{sizeof(struct stat)}; off += 4) + this.HEAP32[statbufptr+off>>2] = 0; + + %{statbufptr[&stat::st_nlink] = 1LL} + %{statbufptr[&stat::st_mode] = "fd.mode()"}; + %{statbufptr[&stat::st_size] = "fd.size()"} + %{statbufptr[&stat::st_blksize] = "fd.size()"} + %{statbufptr[&stat::st_blocks] = "1"} + + return 0; + } + }; + ThinThin.getdents = function (fdno, direntp, count) + { + var fd = this.fds[fdno]; + + if (!fd) + return -%{EBADF}; + + return new Promise((resolve, reject) => { + if (!fd.readdir_entries) { + resolve(Promise.resolve(fd.readdir()).then(entries => { + fd.readdir_entries = entries; + }).then(() => ThinThin.getdents.call(this, fdno, direntp, + count))); + return; + } + + var es = fd.readdir_entries; + var ret = 0; + + if (es.length === 0) { + delete fd.readdir_entries; + resolve(0); + return; + } + + var s = %{sizeof(dirent)}; + while (es.length && ret === 0 && + ret + 4 * es[0].length + s < count) { + %{direntp[&dirent::d_ino] = 1} + %{direntp[&dirent::d_off] = "0"} + var l = CStringTo(es[0], this.HEAP8, %{direntp + &dirent::d_name}); + %{direntp[&dirent::d_reclen] = "s + l"} + ret += s + l; + direntp += s + l; + es.shift(); + } + + resolve(ret); + }); + }; + ThinThin.fstat = function (fdno, bufptr) { + var fd = this.fds[fdno]; + this.HEAP32[bufptr+ 0>>2] = 0; + this.HEAP32[bufptr+ 4>>2] = 0; + this.HEAP32[bufptr+ 8>>2] = 0; + this.HEAP32[bufptr+12>>2] = 0; + this.HEAP32[bufptr+16>>2] = 0; + this.HEAP32[bufptr+20>>2] = 0; + this.HEAP32[bufptr+24>>2] = 0; + this.HEAP32[bufptr+28>>2] = 0; + this.HEAP32[bufptr+32>>2] = 0; + this.HEAP32[bufptr+36>>2] = 0; + this.HEAP32[bufptr+40>>2] = 0; + this.HEAP32[bufptr+44>>2] = 0; + this.HEAP32[bufptr+48>>2] = 0; + this.HEAP32[bufptr+52>>2] = 0; + this.HEAP32[bufptr+56>>2] = 0; + this.HEAP32[bufptr+60>>2] = 0; + this.HEAP32[bufptr+64>>2] = 0; + + if (fd instanceof ThinThinDD) { + this.HEAP32[bufptr+6*4>>2] = 4 << 12; + } else { + this.HEAP32[bufptr+6*4>>2] = 1 << 15; + } + + return Promise.resolve(fd.read()).then(i => { + this.HEAP32[bufptr+0x30>>2] = fd.readData.length; + this.HEAP32[bufptr+0x38>>2] = fd.readData.length; + this.HEAP32[bufptr+0x40>>2] = fd.readData.length; + return 0; + }); + }; + ThinThin.ioctl_p = function (fdno, code, intptr) { + fdno = 0; + var fd = this.fds[fdno]; + + switch (code) { + case %{FIONREAD}: + return fd.available().then(avail => { + if (avail) { + %{*intptr = "avail"} + } else { + %{*intptr = 0} + } + + return 0; + }); + + default: + return -%{EINVAL}; + } + }; + ThinThin.ppoll = function (fdsptr, nfds, tvptr, sigmaskptr) { + var incall = true; + var retval = 0; + var retry = function () { + if (incall) + return ThinThin.ppoll.call(this, + fdsptr, nfds, tvptr, sigmaskptr); + }; + + var all = []; + var any = []; + + var ii; + for (ii = 0; ii < nfds; ii++) { + var i = ii; + var fdno = %{fdsptr[i+&pollfd::fd]}; + var fd = this.fds[fdno]; + var events = %{fdsptr[i+&pollfd::events]}; + %{fdsptr[i+&pollfd::revents] = 0} + if (events & %{POLLIN}) { + all.push(Promise.resolve(fd.read()).then(() => { + return fd.available(); + }).then(avail => { + if (incall) { + if (avail > 0) { + if (%{fdsptr[i+&pollfd::revents]} == 0) + retval++; + %{fdsptr[i+&pollfd::revents]} |= %{POLLIN}; + } + } + })); + any.push(() => Promise.resolve(fd.read())); + } + } + + return Promise.all(all).then(() => { + if (retval) + return retval; + + var a = []; + a.push(Promise.race(any.map(p => Promise.resolve(p()))).then(() => retry())); + if (tvptr) { + var s = %{tvptr[&timeval::tv_sec]}; + var ns = %{tvptr[&timeval::tv_usec]}; + + var ms = 1000 * s + 1e-6 * ns; + + a.push(TimeoutPromise(ms).then(() => { + return 0; + })); + } + + return Promise.race(a).then(rval => { + incall = false; + return rval; + }); + }); + }; + ThinThin.lseek = function (fdno, pos, whence) { + //console.log("lseek " + fdno + " " + pos + " " + whence); + var fd = this.fds[fdno]; + + if (whence == %{SEEK_SET}) + fd.readPosition = pos; + else if (whence == %{SEEK_CUR}) + fd.readPosition += pos; + else if (whence == %{SEEK_END}) + throw("SEEK_END not supported"); + else + return -%{EINVAL}; + + fd.readEOF = false; + + return fd.readPosition; + }; + ThinThin.access = function (ptr, mode) { + return ThinThin.stat.call(this, ptr, 0); + }; + //ThinThin.unlink = Module._unlink; + //ThinThin.rename = Module._rename; + //ThinThin.chdir = Module._chdir; + ThinThin.gettimeofday = function (tvptr, tzptr) { + var date = new Date(); + var s = date / 1000.0; + var us = (date % 1000.0) * 1000.0; + + %{tvptr[&timeval::tv_sec] = "s"} + %{tvptr[&timeval::tv_usec] = "us"} + + return 0; + }; + ThinThin.fork = function () + { + var okay = false; + return new Promise((resolve, reject) => { + var test; test = () => { + var sp = this.syscall_sp; + + if (this.extcallRet[sp] instanceof Promise || + okay) { + okay = true; + resolve(); + } else + Promise.resolve().then(test); + }; + + test(); + }).then(() => { + this.kport.channel.kernel.threads_by_port.set(this.kport, this); + this.kport.channel.kernel.threads_by_port.set(this.kport.channel.port1, this); + this.kport.channel.kernel.threads_by_port.set(this.kport.channel.port2, this); + return this.kport.req("fork"); + }).then(data => new Promise((resolve, reject) => { + var pid = data[0]; + var kport = data[1]; + //var worker = new Worker("wasm32-forkedworker.js"); + resolve(pid); + return; + var fdchannels = {}; + var lfdports = {}; + var rfdports = {}; + var rports = []; + for (var fdno in this.process.fds) { + fdchannels[fdno] = new MessageChannel(); + lfdports[fdno] = fdchannels[fdno].port1; + rfdports[fdno] = fdchannels[fdno].port2; + rports.push(rfdports[fdno]); + } + //workenr.postMessage([kport, this.process.clonedesc(), this.vm.clonedesc(), this.syscall_sp, rfdports], [kport, ...rports]); + + var pipes = {}; + for (var fdno in lfdports) { + pipes[fdno] = new ThinThinHalf(); + new ForwardHalf(pipes[fdno].reverse(), this.process.fds[fdno].half); + var rr = new RRPort(lfdports[fdno]); + new RRPortHalf(pipes[fdno], rr, false); + + rr.port.start(); + } + if (false) worker.onmessage = success => { + console.log(success); + + resolve(success ? pid : -1); + }; + })); + }; + ThinThin.restart = function (dst, src, len, entry) + { + return this.restart(dst, src, len, entry); + }; + ThinThin.execve = function (pathptr, argvptr, envpptr) + { + return ThinThin.execveat.call(this, 0, pathptr, argvptr, envpptr, 0); + }; + ThinThin.execveat = function (dirfdno, pathptr, argvptr, envpptr, flags) + { + if (this.HEAP8[pathptr] === "/".charCodeAt(0)) + dirfdno = %{AT_FDROOTD}; + + let args = CStringsAt(this.heap, argvptr); + let command = args.join(" "); + let timestamp = Date.now(); + if (this.pwd !== undefined) + command = "(cd " + this.pwd + "; PWD=. " + command + ")|tee /tmp/tmp.out." + timestamp; + try { + let pid = os.spawn(command); + let o = os.waitpid(pid); + let output = os.file.readFile("/tmp/tmp.out." + timestamp, "binary"); + let ret = this.fds[1].write(output, 0, output.length); + return new Promise(() => {}); + } catch (e) { + console.log("OH NO!"); + console.log(e); + return -%{EIO}; + } + }; + ThinThin.wait4 = function(pid, wstatusp, options, rusage) + { + return pid; // XXX, in the fork-exec-wait4 case this works okay. + var ret = this.kport.req("wait4"); + + if (wstatusp) + this.HEAP32[wstatusp>>2] = 0; + + if (options & 1) + return Promise.resolve(0); + + return ret.then(([pid, wstatus]) => { + if (wstatusp) + this.HEAP32[wstatusp>>2] = (wstatus << 8); + + return pid; + }); + }; +} + +ThinThin.getuid = ThinThin.geteuid = ThinThin.getgid = ThinThin.getegid = function () +{ + return 1; +}; + +ThinThin.getpid = ThinThin.getppid = function () +{ + return 1; +}; + +ThinThin.dlload = function (addr, len, memp) +{ + var sab = this.heap.slice(addr, addr+len); + var ab = new ArrayBuffer(len); + var sav = new Uint8Array(sab); + var av = new Uint8Array(ab); + for (var i=0; i < len; i++) + av[i] = sav[i]; + var module = new Wasm32Module(this.process, ab); + + return module.load(this, this.vm).then(ret => { + this.HEAP32[memp>>2] = module.dyninfo.data_end - module.dyninfo.data; + if (module.dyninfo.libs.length) + module.depstring = module.dyninfo.libs.join(String.fromCharCode(0)) + String.fromCharCode(0); + else + module.depstring = ""; + + return ret; + }); +}; + +ThinThin.dlreaddep = function (modi, mem, len) +{ + var module = Wasm32Modules[modi]; + var ret = 0; + + while (module.depstring.length > 0 && ret < len) { + this.HEAPU8[mem+ret] = module.depstring.charCodeAt(0); + module.depstring = module.depstring.substr(1); + ret++; + } + + return ret; +}; + +ThinThin.dlinstantiate = function (modi, mem) +{ + var module = Wasm32Modules[modi]; + + return module.instantiate(this, this.vm, mem).then(() => { + return 0; + }); +}; + +ThinThin.dlkill = function (modi) +{ + var module = Wasm32Modules[modi]; + + return module.kill(this, this.vm); +}; + +ThinThin.dlopen = function (cpath) +{ + console.log('dlopen') + var path = CStringAt(this.HEAP8, cpath); + console.log('dlopen: ' + cpath + ' ' + path) + var module = new Wasm32Module(os.file.readFile(path, "binary")); + console.log('dlopen: ' + module) + + return module.instantiate(this, this.vm).then(() => { + console.log("success"); + return 0; + }); +}; + +ThinThin.dlsym = function (modi, csym) +{ + var module = Wasm32Modules[modi]; + + return new Promise((resolve, reject) => { + var symbol = CStringAt(this.HEAP8, csym); + + let ret = module.symtab[symbol] || 0; + + resolve(ret); + }); +}; + +ThinThin[20] = ThinThin.writev = function (fd, iov, iovcnt) +{ + var gret = 0; + if (iovcnt == 0) + return 0; + var i = 0; + while (%{iov[i+&iovec::iov_len]} == 0) + i++; + var ret = ThinThin.write.call(this, fd, %{iov[i+&iovec::iov_base]}, %{iov[i+&iovec::iov_len]}); + + return ret; +}; + + +// mmap +ThinThin[9] = function () +{ + return -1; +}; + +// brk +ThinThin[12] = function (brk) +{ + return brk; +}; + +ThinThin.syscall = function (n, ...args) +{ + if (n.toString() in ThinThin) + return ThinThin[n].call(this, ...args); + else + return -38; +}; + +ThinThin.init_trampoline = function (mtramp) +{ + var sigstr = CStringAt(this.HEAPU8, this.HEAPU32[mtramp>>2]); + var sigarray = build_sig(sigstr); + var length = 8 + 11 + 7 + 1 + 1 + sigarray.length; + var siglength = sigarray.length; + var ab = new ArrayBuffer (length); + var code = new Uint8Array (ab); + var i = 0; + + code[i++] = 0x00; + code[i++] = 0x61; + code[i++] = 0x73; + code[i++] = 0x6d; + + code[i++] = 0x01; + code[i++] = 0x00; + code[i++] = 0x00; + code[i++] = 0x00; + + code[i++] = 0x01; // signature + code[i++] = (sigarray.length & 0x7f); // XXX > 127 args + while (sigarray.length) + code[i++] = sigarray.shift(); + + code[i++] = 0x02; // import + code[i++] = 9; // length + code[i++] = 0x01; // 1 import + code[i++] = 0x03; + code[i++] = 0x73; + code[i++] = 0x79; + code[i++] = 0x73; + + code[i++] = 0x01; + code[i++] = 0x54; + + code[i++] = 0x00; // function + code[i++] = 0x00; // signature + + + code[i++] = 0x07; // export + code[i++] = 5; // length + code[i++] = 0x01; // 1 export + code[i++] = 0x01; + code[i++] = 0x54; + code[i++] = 0x00; // function + code[i++] = 0x00; // func 0 + + if (i !== length) { + console.log(i + " != " + length); + throw "length mismatch"; + } + + return WebAssembly.compile(code).then(module => { + var imports = {}; + imports.sys = {}; + imports.sys.T = (...args) => { + var fnaddr = this.HEAPU32[mtramp+8>>2]; + var static_chain = this.HEAPU32[mtramp+16>>2]; + + this.set_rv(static_chain); + return this.vm.table.get(fnaddr)(...args); + }; + return WebAssembly.instantiate(module, imports); + }).then(instance => { + let index = this.vm.space_functions.alloc(1); + this.vm.table.set(index, instance.exports.T); + this.HEAPU32[mtramp+24>>2] = index; + return 0; + }).catch(e => { + console.log("error in build_trampoline: " + e); + return -%{EIO}; + }); +}; + +ThinThin.destroy_trampoline = function (mtramp) +{ + var index = this.HEAPU32[mtramp+24>>2]; + + this.vm.space_functions.free(index); + + return 0; +}; + +ThinThin.chdir = function (ptr) +{ + var path = CStringAt(this.HEAPU8, ptr); + if (path === "./") + this.pwd = "."; + else + this.pwd += "/" + path; + return Promise.resolve(0); +}; + +ThinThin.pipe2 = function (ptr, flags) +{ + var pipe = new ThinThinHalf(); + let fds = [new ThinThinHalfFD(this.process, pipe), + new ThinThinHalfFD(this.process, pipe)]; + fds[0].other = fds[1]; + fds[1].other = fds[0]; + this.HEAP32[ptr >> 2] = fds[0].fdno; + this.HEAP32[ptr + 4 >> 2] = fds[1].fdno; + return 0; +}; + +ThinThin.renameat2 = function (oldfdno, oldpathptr, newfdno, newpathptr, flags) +{ + var oldpath = CStringAt(this.HEAPU8, oldpathptr); + var newpath = CStringAt(this.HEAPU8, newpathptr); + + os.system(`mv ${oldpath} ${newpath}`); + return Promise.resolve(0); +}; + +ThinThin.openat = function (fdno, ptr, flags, mode) { + var path = CStringAt(this.HEAPU8, ptr); + + if (path.match(/^\/dev\/tty/)) + return 1; + + if (path.match(/^\/dev\/null/)) + return 1; + + if (path.match(/^\/dev\/urandom/)) + return 1; + + if (path.match(/\.pdmp$/)) + return -%{ENOENT}; + + if (path.match(/lisp$/) && + !path.match(/emacs-lisp.lisp/)) { + let ret = new ThinThinDD(this); + ret.path = "."; + return ret.fdno; + } + + if (path.match(/loadup\.el$/)) { + path = "/home/pip/g/wasm/build/wasm32/emacs/lisp/loadup.el"; + } + + if (path.match(/^\//)) { + try { + dd = this.process.ddroot; + let ret = dd.openat(path, flags, mode); + return ret.then(fd => (typeof fd === "object") ? fd.fdno : fd); + } catch (e) { + console.log("exception!"); + return -%{ENOENT}; + } + } + + if (this.pwd && path[0] !== "/") + path = this.pwd + "/" + path; + var dd = this.fds[fdno]; + if (!dd) + dd = this.process.ddcwd; + + if (path.length === 0 || path === ".") + return Promise.resolve(dd).then(fd => (typeof fd === "object") ? fd.fdno : fd); + + return dd.openat(path, flags, mode).then(fd => (typeof fd === "object") ? fd.fdno : fd); +}; + +ThinThin.faccessat = function (fdno, ptr, flags, mode) { + return 0; +}; + +var gExitStatus; +ThinThin.exit = function (code) { + gExitStatus = code; + if (this.main_thread) + try { + quit(code); + } catch (e) { + os.kill(os.getpid()); + } + //console.log("exiting: " + code); + return this.kport.req("exit", [code]).then(() => new Promise(() => {})) + + console.log("runtime: " + (Date.now() - lastdonetime)); + if (typeof quit !== "undefined") + quit(code); + else if (typeof self !== "undefined") { + self.close(); + return new Promise((resolve, reject) => { + }); + } else if (typeof process !== "undefined") + process.exit(code); + else if (typeof document !== "undefined") { + document.getElementById("output").textContent += "Exit Status " + code; + this.stop(0xffffffff); + } + //if (code != 0 && code !== undefined) + // throw "Exit Status " + code; + //else + // throw new SuccessException(); +}; + +ThinThin.isatty = function (fdno) { + return fdno < 3 ? 1 : 0; +}; + +ThinThin.readlinkat = function () +{ + return -%{ENOENT}; +}; + +ThinThin.unlinkat = function () +{ + return 0; +}; + +ThinThin.mkdirat = function () +{ + let path = CStringAt(this.HEAP8, pathptr); + os.spawn("mkdir " + path); + return 0; +}; + +ThinThin.fcntl_v = function () +{ + return %{O_RDWR}; +}; + +ThinThin.fcntl_i = function () { + return 0; +}; + +ThinThin.getcwd = function (addr, len) { + this.HEAP8[addr] = ".".charCodeAt(0); + this.HEAP8[addr+1] = "/".charCodeAt(0); + this.HEAP8[addr+2] = 0; + + return 0; +}; + +ThinThin.dup2 = function (oldfd, newfd) { + this.fds[newfd] = this.fds[oldfd]; + + return 0; +}; + +ThinThin.utimensat = function (fdno, pathptr, timesptr, flag) +{ + return 0; +}; + +ThinThin.fchmodat = function () +{ + return 0; +}; +var sizes = { + tablesize: 65536, + memsize: 512 * 1024 * 1024, + stacksize: 1 * 1024 * 1024, + stackbottom: 511 * 1024 * 1024, +}; +var kernel = new Wasm32Kernel(); +var vm = new Wasm32VM(sizes); +var system = new Wasm32System(); +var module; + +var gRetrigger; +var gExitStatus = 0; + +if (remote) { + fetch(path).then(response => { + if (response.ok) + return response.arrayBuffer(); + + return Promise.reject(response); + }).then((ab) => { + return remote_process(kernel, ab); + }).then(([root, cwd, stdin, stdout, stderr]) => { + console.log("got ports"); + var process = new Wasm32Process(system); + var dom = document.getElementById("output"); + + var pipe0 = new ThinThinHalf(); + var pipe1 = new ThinThinHalf(); + var pipe2 = new ThinThinHalf(); + + new HTMLHalf(pipe0.reverse(), dom); + new HTMLHalf(pipe1.reverse(), dom); + new HTMLHalf(pipe2.reverse(), dom); + + var rr0 = new RRPort(stdin); + var rr1 = new RRPort(stdout); + var rr2 = new RRPort(stderr); + + new RRPortHalf(pipe0, rr0, false); + new RRPortHalf(pipe1, rr1, false); + new RRPortHalf(pipe2, rr2, false); + + rr0.port.start(); + rr1.port.start(); + rr2.port.start(); + + var f; + f = () => document.getElementById("debug2").innerHTML = + `${pipe0.debug("pipe0", f)}
${pipe1.debug("pipe1", f)}
${pipe2.debug("pipe2", f)}`; + f(); + }).catch(e => { + console.log("e2:" + e + "\n" + e.stack); + }); +} else if (typeof os !== "undefined") { + module = new Wasm32Module(new Wasm32Process(system), os.file.readFile(path, "binary")); + module.sizes = sizes; + + system.instantiate(module, vm, args, environment_variables()).then(() => { + try { + run = () => { + try { + if (!system.step()) + Promise.resolve().then(run); + } catch (e) { + console.log(e); + console.log(e.stack); + } + }; + run(); + gRetrigger = run; + } catch (e) { + console.log("exception"); + console.log(e); + console.log(e.stack); + } + }).catch((e) => { + console.log("caught"); + console.log(e); + }); +} else if (typeof fetch !== "undefined") { + caches.open("v3").then((cache) => { + var req = new Request(path); + + return cache.match(req).then((response) => { + if (response === undefined) + return fetch(path); + + return response; + }).then(response => { + cache.put(path, response.clone()); + + if (response.ok) + return response.arrayBuffer(); + + return Promise.reject(response); + }); + }).then((ab) => { + module = new Wasm32Module(ab); + + return system.instantiate(module, vm, args, []); + }).then(() => { + try { + run = () => { + try { + system.step(); + Promise.resolve().then(run); + } catch (e) { + console.log(e); + console.log(e.stack); + } + }; + run(); + } catch (e) { + console.log("exception"); + console.log(e); + console.log(e.stack); + } + }).catch((e) => { + console.log("caught"); + console.log(e); + console.log(e.stack); + }); +} +