| // 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); |
| } |