0

I am trying to load sql-wasm.wasm from https://github.com/sql-js/sql.js such that it does not need a server. Otherwise, trying to load wasm from HTML gives CORS error. I converted it to base64 (~800KB) which looks like this

const SQLBASE64 = "AGFzbQEAAAABrQRHYAJ/fwF/YAF/AX....AAJ4BAEHwogQLA4AWUQ==";

(link to full base64 string)

WebAssembly.Instance only works with very small binaries. WebAssembly.instantiate() and WebAssembly.instantiateStreaming() both end up with errors about incorrect object, format, or headers etc.

How do I do it properly?

SMUsamaShah
  • 7,677
  • 22
  • 88
  • 131
  • 1
    [.instantiate](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiate) takes an arraybuffer, you have a base64 string, check https://stackoverflow.com/questions/21797299/convert-base64-string-to-arraybuffer – James Mar 25 '23 at 02:34
  • @James using accepted answer from linked post when I call `WebAssembly.instantiate(_base64ToArrayBuffer(SQLBASE64), {}).then(r => {r.instance});` I get `Uncaught (in promise) TypeError: WebAssembly.instantiate(): Import #0 module="a" error: module is not an object or function` – SMUsamaShah Mar 25 '23 at 23:04
  • Awesome it worked then. Now you have some different issue. Check https://stackoverflow.com/questions/59726374/running-a-wasm-file-in-node-js – James Mar 26 '23 at 04:05

2 Answers2

1

You can try using the answer from this question (with your const it would be):

const SQLBASE64 = "...";

function asciiToBinary(str) {
  if (typeof atob === 'function') {
    return atob(str)
  } else {
    return new Buffer(str, 'base64').toString('binary');
  }
}

function decode(encoded) {
  var binaryString =  asciiToBinary(encoded);
  var bytes = new Uint8Array(binaryString.length);
  for (var i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}

var instance = WebAssembly.instantiate(decode(SQLBASE64), {})
      .then(r => r.instance);

Edit:

I investigated more, and you need the sql-wasm.js file in order to instantiate the sql-wasm.wasm file. Your wasm module needs imports that sql-wasm.js would give, but the config it takes seems to only take a path, for now. Meaning, there is no way to tell it to load a binary module already in the html file.

I think this can be changed though, with either a pull request or an issue on the official repo. I am, of course, hopeful to be proven wrong by somebody more knowledgeable.

TachyonicBytes
  • 700
  • 1
  • 8
  • I ran this as `WebAssembly.instantiate(decode(SQLBASE64), {}).then(r => {r.instance})` but it throughs error `Uncaught (in promise) TypeError: WebAssembly.instantiate(): Import #0 module="a" error: module is not an object or function` – SMUsamaShah Mar 25 '23 at 23:00
  • I edited the response with what I think is the explanation for that. – TachyonicBytes Mar 26 '23 at 14:53
1

So there were two problems.

  1. base64 string was incorrect. Used https://github.com/BillKek/wasm2js_let_static to convert sql-wasm.wasm v1.8.0 to base64 correctly.
  2. Don't need to call WebAssembly.instantiate directly, use initSqlJs() from sql.js instead.

Then loaded it like this

    const wasm_strbuffer = atob(SQLBASE64);
    let wasm_codearray = new Uint8Array(wasm_strbuffer.length);
    for (var i in wasm_strbuffer) wasm_codearray[i] = wasm_strbuffer.charCodeAt(i);

    const sqlPromise = initSqlJs({
        locateFile: filename => URL.createObjectURL(new Blob([wasm_codearray], { type: 'application/wasm' }))
    });
    const dataPromise = fetch("mysqlite.db").then(res => res.arrayBuffer());
    Promise.all([sqlPromise, dataPromise]).then((data) => {
        const SQL = data[0];
        const buf = data[1];
        const db = new SQL.Database(new Uint8Array(buf));
        dbLoaded(db);
    });

Base64 wasm gist https://gist.github.com/SMUsamaShah/358fba159cb41fe469fc61e7db444c0e

SMUsamaShah
  • 7,677
  • 22
  • 88
  • 131