wasm32-shim.js 4.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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

31 32 33 34
function viewstruct(data, fields) {
  return new Uint32Array(memory.buffer).subarray(data/4, data/4 + fields);
}

35
function copystr(a, b) {
36
  let view = new Uint8Array(memory.buffer).subarray(a, a + b);
37 38 39
  return String.fromCharCode.apply(null, view);
}

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
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);
}

101 102 103 104 105 106 107 108 109
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); },
110 111
  log10: Math.log10,
  log10f: Math.log10,
112

113 114 115 116 117 118 119 120 121 122
  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;
123 124
    }
  }
125
};
126 127

let instance = new WebAssembly.Instance(m, imports);
128 129 130 131 132 133 134
memory = instance.exports.memory;
try {
  instance.exports.main();
} catch (e) {
  console.error(e);
  process.exit(101);
}