// This is a small "shim" program which is used when wasm32 unit tests are run // in this repository. This program is intended to be run in node.js and will // load a wasm module into memory, instantiate it with a set of imports, and // then run it. // // There's a bunch of helper functions defined here in `imports.env`, but note // that most of them aren't actually needed to execute most programs. Many of // these are just intended for completeness or debugging. Hopefully over time // nothing here is needed for completeness. const fs = require('fs'); const process = require('process'); const buffer = fs.readFileSync(process.argv[2]); Error.stackTraceLimit = 20; let m = new WebAssembly.Module(buffer); let memory = null; function viewstruct(data, fields) { return new Uint32Array(memory.buffer).subarray(data/4, data/4 + fields); } function copystr(a, b) { let view = new Uint8Array(memory.buffer).subarray(a, a + b); return String.fromCharCode.apply(null, view); } function syscall_write([fd, ptr, len]) { let s = copystr(ptr, len); switch (fd) { case 1: process.stdout.write(s); break; case 2: process.stderr.write(s); break; } } function syscall_exit([code]) { process.exit(code); } function syscall_args(params) { let [ptr, len] = params; // Calculate total required buffer size let totalLen = -1; for (let i = 2; i < process.argv.length; ++i) { totalLen += Buffer.byteLength(process.argv[i]) + 1; } if (totalLen < 0) { totalLen = 0; } params[2] = totalLen; // If buffer is large enough, copy data if (len >= totalLen) { let view = new Uint8Array(memory.buffer); for (let i = 2; i < process.argv.length; ++i) { let value = process.argv[i]; Buffer.from(value).copy(view, ptr); ptr += Buffer.byteLength(process.argv[i]) + 1; } } } function syscall_getenv(params) { let [keyPtr, keyLen, valuePtr, valueLen] = params; let key = copystr(keyPtr, keyLen); let value = process.env[key]; if (value == null) { params[4] = 0xFFFFFFFF; } else { let view = new Uint8Array(memory.buffer); let totalLen = Buffer.byteLength(value); params[4] = totalLen; if (valueLen >= totalLen) { Buffer.from(value).copy(view, valuePtr); } } } function syscall_time(params) { let t = Date.now(); let secs = Math.floor(t / 1000); let millis = t % 1000; params[1] = Math.floor(secs / 0x100000000); params[2] = secs % 0x100000000; params[3] = Math.floor(millis * 1000000); } let imports = {}; imports.env = { // These are generated by LLVM itself for various intrinsic calls. Hopefully // one day this is not necessary and something will automatically do this. fmod: function(x, y) { return x % y; }, exp2: function(x) { return Math.pow(2, x); }, exp2f: function(x) { return Math.pow(2, x); }, ldexp: function(x, y) { return x * Math.pow(2, y); }, ldexpf: function(x, y) { return x * Math.pow(2, y); }, sin: Math.sin, sinf: Math.sin, cos: Math.cos, cosf: Math.cos, log: Math.log, log2: Math.log2, log10: Math.log10, log10f: Math.log10, rust_wasm_syscall: function(index, data) { switch (index) { case 1: syscall_write(viewstruct(data, 3)); return true; case 2: syscall_exit(viewstruct(data, 1)); return true; case 3: syscall_args(viewstruct(data, 3)); return true; case 4: syscall_getenv(viewstruct(data, 5)); return true; case 6: syscall_time(viewstruct(data, 4)); return true; default: console.log("Unsupported syscall: " + index); return false; } } }; let instance = new WebAssembly.Instance(m, imports); memory = instance.exports.memory; try { instance.exports.main(); } catch (e) { console.error(e); process.exit(101); }