import * as Schema from "../../schema"; import { ByteBuffer } from "peechy"; import path from "path"; import { Loader } from "../schema"; // import { transform as sucraseTransform } from "sucrase"; export interface WebAssemblyModule { init(): number; transform(a: number): number; bun_malloc(a: number): number; bun_free(a: number): number; scan(a: number): number; } const wasm_imports_sym: symbol | string = process.env.NODE_ENV === "development" ? "wasm_imports" : Symbol("wasm_imports"); const ptr_converter = new ArrayBuffer(16); const ptr_float = new BigUint64Array(ptr_converter); const slice = new Uint32Array(ptr_converter); const Wasi = { clock_time_get(clk_id, tp) { return Date.now(); }, environ_sizes_get() { debugger; return 0; }, environ_get(__environ, environ_buf) { debugger; return 0; }, fd_close(fd) { debugger; return 0; }, proc_exit() {}, fd_seek(fd, offset_bigint, whence, newOffset) { debugger; }, fd_write(fd, iov, iovcnt, pnum) { debugger; }, }; var scratch: Uint8Array; var scratch2: Uint8Array; export class Bun { static has_initialized = false; static wasm_source: WebAssembly.WebAssemblyInstantiatedSource = null; static get wasm_exports(): WebAssemblyModule { return Bun.wasm_source.instance.exports as any; } static get memory(): WebAssembly.Memory { return Bun.wasm_source.instance.exports.memory as any; } static memory_array: Uint8Array; static _decoder: TextDecoder; static _wasmPtrToSlice(offset: number | bigint) { ptr_float[0] = typeof offset === "number" ? BigInt(offset) : offset; return new Uint8Array(Bun.memory.buffer, slice[0], slice[1]); } static _wasmPtrLenToString(slice: number) { if (!Bun._decoder) { Bun._decoder = new TextDecoder("utf8"); } const region = this._wasmPtrToSlice(slice); return Bun._decoder.decode(region); } // We don't want people to be calling these manually static [wasm_imports_sym] = { console_log(slice: number) { console.log(Bun._wasmPtrLenToString(slice)); }, console_error(slice: number) { console.error(Bun._wasmPtrLenToString(slice)); }, console_warn(slice: number) { console.warn(Bun._wasmPtrLenToString(slice)); }, console_info(slice: number) { console.info(Bun._wasmPtrLenToString(slice)); }, __indirect_function_table: new WebAssembly.Table({ initial: 0, element: "anyfunc", }), __stack_pointer: new WebAssembly.Global({ mutable: true, value: "i32", }), __multi3(one: number, two: number) { return Math.imul(one | 0, two | 0); }, fmod(one: number, two: number) { return one % two; }, memset(ptr: number, value: number, len: number) { Bun.memory_array.fill(value, ptr, ptr + len); }, memcpy(ptr: number, value: number, len: number) { Bun.memory_array.copyWithin(ptr, value, value + len); }, // These functions convert a to an unsigned long long, rounding toward zero. Negative values all become zero. __fixunsdfti(a: number) { return Math.floor(a); }, // These functions return the remainder of the unsigned division of a and b. __umodti3(a: number, b: number) { return (a | 0) % (b | 0); }, // These functions return the quotient of the unsigned division of a and b. __udivti3(a: number, b: number) { return (a | 0) / (b | 0); }, // These functions return the result of shifting a left by b bits. __ashlti3(a: number, b: number) { return (a | 0) >> (b | 0); }, /* Returns: convert a to a double, rounding toward even. */ __floatuntidf(a: number) { const mod = a % 2; if (mod === 0) { return Math.ceil(a); } else if (mod === 1) { return Math.floor(a); } }, emscripten_notify_memory_growth() {}, }; static async init(url) { // globalThis.sucraseTransform = sucraseTransform; scratch = new Uint8Array(8096); if (Bun.has_initialized) { return; } Bun.wasm_source = await globalThis.WebAssembly.instantiateStreaming( fetch(url), { env: Bun[wasm_imports_sym], wasi_snapshot_preview1: Wasi }, ); const res = Bun.wasm_exports.init(); if (res < 0) { throw `[Bun] Failed to initialize WASM module: code ${res}`; } else { console.log("WASM loaded."); } Bun.has_initialized = true; } static transformSync(content: Uint8Array | string, file_name: string) { if (!Bun.has_initialized) { throw "Please run await Bun.init(wasm_url) before using this."; } // if (process.env.NODE_ENV === "development") { // console.time("[Bun] Transform " + file_name); // } const bb = new ByteBuffer(scratch); bb.length = 0; bb.index = 0; var contents_buffer; if (typeof content === "string") { if (!scratch2) { scratch2 = new Uint8Array(content.length * 2); } let i = 0; for (; i < content.length; i++) { if (i > scratch2.length) { var scratch3 = new Uint8Array(scratch2.length * 2); scratch3.set(scratch2); scratch2 = scratch3; } scratch2[i] = content.charCodeAt(i); } contents_buffer = scratch2.subarray(0, i); } else { contents_buffer = content; } Schema.encodeTransform( { contents: contents_buffer, path: file_name, loader: { ".jsx": Loader.jsx, ".tsx": Loader.tsx, ".ts": Loader.ts, ".js": Loader.js, ".json": Loader.json, }[path.extname(file_name)], }, bb, ); const data = bb.toUint8Array(); const input_ptr = Bun.wasm_exports.bun_malloc(data.length); var buffer = this._wasmPtrToSlice(input_ptr); buffer.set(data); const resp_ptr = Bun.wasm_exports.transform(input_ptr); var _bb = new ByteBuffer(this._wasmPtrToSlice(resp_ptr)); const response = Schema.decodeTransformResponse(_bb); Bun.wasm_exports.bun_free(input_ptr); scratch = bb.data; return response; } static scan( content: Uint8Array | string, file_name: string, loader?: Loader, ) { if (!Bun.has_initialized) { throw "Please run await Bun.init(wasm_url) before using this."; } // if (process.env.NODE_ENV === "development") { // console.time("[Bun] Transform " + file_name); // } scratch.fill(0); const bb = new ByteBuffer(scratch); bb.length = 0; bb.index = 0; var contents_buffer; if (typeof content === "string") { if (!scratch2) { scratch2 = new Uint8Array(content.length * 2); } const encode_into = new TextEncoder().encodeInto(content, scratch2); contents_buffer = scratch2.subarray(0, encode_into.written); } else { contents_buffer = content; } Schema.encodeScan( { contents: contents_buffer, path: file_name, loader: loader || { ".jsx": Loader.jsx, ".tsx": Loader.tsx, ".ts": Loader.ts, ".js": Loader.js, ".json": Loader.json, }[path.extname(file_name)], }, bb, ); const data = bb.toUint8Array(); const input_ptr = Bun.wasm_exports.bun_malloc(data.length); var buffer = this._wasmPtrToSlice(input_ptr); buffer.set(data); const resp_ptr = Bun.wasm_exports.scan(input_ptr); var _bb = new ByteBuffer(this._wasmPtrToSlice(resp_ptr)); const response = Schema.decodeScanResult(_bb); Bun.wasm_exports.bun_free(input_ptr); scratch = bb.data; return response; } } globalThis.Bun = Bun;