Stream: general

Topic: Is there a WASM to JavaScript transpiler supporting BigInt?


view this post on Zulip guest271314 (Apr 03 2026 at 13:46):

I'm trying to transpile a WASM file to JavaScript. wasm2js can't handle BigInt imports to WASM from JavaScript. jco transpile expects a WebAssembly Component. wasm-tools doesn't convert the core WASM module into a component due to env import from JavaScript.

Is there a WASM to JavaScript transpiler that supports BigInt imports from JavaScript?

view this post on Zulip Andy Wingo (Apr 03 2026 at 14:09):

i do not know. may i ask the very ignorant question, why compile wasm to js? where is there js but no wasm

view this post on Zulip Andy Wingo (Apr 03 2026 at 14:11):

also if i may ask, what toolchain are you using that uses bigint imports? i would like to prepare a proposal à la js string builtins for bigints at some point, and i would like to collect users :)

view this post on Zulip Andy Wingo (Apr 03 2026 at 14:11):

(i realize that neither of these questions bring you closer to answering your own question; apologies :sweat_smile: )

view this post on Zulip guest271314 (Apr 03 2026 at 14:15):

I'm trying to port code originally written in Zig to JavaScript.

Currently the WASM looks like this. There are a couple other places in the code that uses BigInt.

  const { instance } = await WebAssembly.instantiate(wasmBytes, {
    env: {
      get_time_ns: () => BigInt(Math.round(performance.now() * 1_000_000)),
      console_log: (ptr, len) => {
        const bytes = new Uint8Array(inst.exports.memory.buffer, ptr, len);
        console.log("[wasm]", new TextDecoder().decode(bytes));
      },
      random_fill: (ptr, len) => {
        crypto.getRandomValues(
          new Uint8Array(inst.exports.memory.buffer, ptr, len),
        );
      },
    },
  });

view this post on Zulip bjorn3 (Apr 03 2026 at 15:25):

Why do you need to have plain js as output rather than wasm + js glue? Do you want to run it on some old js engine that doesn't support wasm yet or something?

view this post on Zulip guest271314 (Apr 04 2026 at 15:40):

bjorn3 said:

Why do you need to have plain js as output rather than wasm + js glue? Do you want to run it on some old js engine that doesn't support wasm yet or something?

Because I'm trying to port QUIC, TLS, and WebTransport server implementation to JavaScript proper.

These each of these

use Node.js Addons for C++ and Rust source code execution.

This is a JavaScript implementation of QUIC/HTTP3 doesn't work as expected; doesn't send messages back to client

This _does_ work as expected using WebAssembly

The target is not just the browser, it's the JavaScript programming language at large. AFAIK nobody has created a QUIC/HTTP/3/WebTransport implementation in only JavaScript that works as expected.

view this post on Zulip guest271314 (Apr 05 2026 at 16:55):

I figured it out

  const env = {
    // 1. Move the variable inside the object
    tempRet0: 0,
    // 2. Use method shorthand to allow 'this' access
    setTempRet0(val) {
      this.tempRet0 = val | 0;
    },
    getTempRet0() {
      return this.tempRet0 | 0;
    },
    get_time_ns() {
      const nowNs = BigInt(Math.round(performance.now() * 1e6));
      const low = Number(nowNs & 0xFFFFFFFFn) | 0;
      // Update the internal state
      this.tempRet0 = Number(nowNs >> 32n) | 0;
      return low;
    },
    console_log(ptr, len) {
      const bytes = new Uint8Array(mem.buffer, ptr, len);
      const msg = decoder.decode(bytes);
      console.log(msg);
    },
    random_fill(ptr, len) {
      const bytes = new Uint8Array(mem.buffer, ptr, len);
      crypto.getRandomValues(bytes);
    },
  };

  const retasmFunc = asmFunc({
    env,
  });

  const mem = retasmFunc.memory;

  function bigIntToi32(bigInt) {
    const bId = BigInt(bigInt);
    const low = Number(bId & 0xFFFFFFFFn) | 0;
    const high = Number(bId >> 32n) | 0;
    return {
      low,
      high,
    };
  }

  const instance = {
    exports: {
      ...retasmFunc,
      qz_wt_accept_session: (sessionId) => {
        const {
          low,
          high,
        } = bigIntToi32(sessionId);
        return retasmFunc.qz_wt_accept_session(low, high);
      },
      qz_wt_read_stream: (streamId, outPtr, outLen) => {
        const {
          low,
          high,
        } = bigIntToi32(streamId);
        return retasmFunc.qz_wt_read_stream(low, high, outPtr, outLen);
      },
      qz_wt_send_stream: (streamId, dataPtr, len) => {
        const {
          low,
          high,
        } = bigIntToi32(streamId);
        return retasmFunc.qz_wt_send_stream(low, high, dataPtr, len);
      },
      qz_wt_close_stream: (streamId) => {
        const {
          low,
          high,
        } = bigIntToi32(streamId);
        return retasmFunc.qz_wt_close_stream(low, high);
      },
      qz_wt_send_datagram: (sessionId, dataPtr, len) => {
        const {
          low,
          high,
        } = bigIntToi32(sessionId);
        return retasmFunc.qz_wt_send_datagram(low, high, dataPtr, len);
      },
      qz_wt_close_session: (sessionId, errorCode, reasonPtr, reasonLen) => {
        const {
          low,
          high,
        } = bigIntToi32(sessionId);
        return retasmFunc.qz_wt_close_session(
          low,
          high,
          errorCode,
          reasonPtr,
          reasonLen,
        );
      },
    },
  };

Last updated: Apr 13 2026 at 00:25 UTC