diff options
Diffstat (limited to 'packages/bun-wasm/index.ts')
-rw-r--r-- | packages/bun-wasm/index.ts | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/packages/bun-wasm/index.ts b/packages/bun-wasm/index.ts new file mode 100644 index 000000000..78582c84b --- /dev/null +++ b/packages/bun-wasm/index.ts @@ -0,0 +1,346 @@ +import { ByteBuffer } from "peechy/bb"; +import { + decodeScanResult, + decodeTransformResponse, + encodeScan, + encodeTransform, + Loader as BunLoader, + type ScanResult, + type TransformResponse, +} from "./schema"; +export enum Loader { + jsx = BunLoader.jsx, + js = BunLoader.js, + tsx = BunLoader.tsx, + ts = BunLoader.ts, +} + +export type { ScanResult, TransformResponse }; + +function normalizeLoader(file_name: string, loader?: Loader): BunLoader { + return ( + (loader + ? { + jsx: BunLoader.jsx, + tsx: BunLoader.tsx, + ts: BunLoader.ts, + js: BunLoader.js, + }[loader] + : null) || + { + ".jsx": BunLoader.jsx, + ".tsx": BunLoader.tsx, + ".ts": BunLoader.ts, + ".js": BunLoader.js, + }[file_name.substring(file_name.lastIndexOf("."))] || + BunLoader.js + ); +} + +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 = + //@ts-ignore + 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; + // @ts-ignore-line + static wasm_source: WebAssembly.WebAssemblyInstantiatedSource = null; + static get wasm_exports(): WebAssemblyModule { + return Bun.wasm_source.instance.exports as any; + } + // @ts-ignore-line + 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 = Bun._wasmPtrToSlice(slice); + return Bun._decoder.decode(region); + } + + // We don't want people to be calling these manually + // @ts-ignore-line + static [wasm_imports_sym as symbol] = { + 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)); + }, + // @ts-ignore-line + __indirect_function_table: new WebAssembly.Table({ + initial: 0, + element: "anyfunc", + }), + // @ts-ignore-line + __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, fetch = globalThis.fetch) { + // globalThis.sucraseTransform = sucraseTransform; + scratch = new Uint8Array(8096); + + if (Bun.has_initialized) { + return; + } + + if (globalThis?.WebAssembly?.instantiateStreaming) { + Bun.wasm_source = await globalThis.WebAssembly.instantiateStreaming( + fetch(url), + { env: Bun[wasm_imports_sym], wasi_snapshot_preview1: Wasi } + ); + } else if (typeof window !== "undefined") { + const resp = await fetch(url); + Bun.wasm_source = await globalThis.WebAssembly.instantiate( + await resp.arrayBuffer(), + { + env: Bun[wasm_imports_sym], + wasi_snapshot_preview1: Wasi, + } + ); + // is it node? + } else { + //@ts-ignore + const fs = await import("fs"); + + Bun.wasm_source = await globalThis.WebAssembly.instantiate( + fs.readFileSync(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}`; + } + + Bun.has_initialized = true; + } + + static transformSync( + content: Uint8Array | string, + file_name: string, + loader?: Loader + ): TransformResponse { + 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; + } + encodeTransform( + { + contents: contents_buffer, + path: file_name, + // @ts-ignore + loader: normalizeLoader(file_name, loader), + }, + bb + ); + const data = bb.toUint8Array(); + + const input_ptr = Bun.wasm_exports.bun_malloc(data.length); + var buffer = Bun._wasmPtrToSlice(input_ptr); + buffer.set(data); + + const resp_ptr = Bun.wasm_exports.transform(input_ptr); + + var _bb = new ByteBuffer(Bun._wasmPtrToSlice(resp_ptr)); + + const response = decodeTransformResponse(_bb); + Bun.wasm_exports.bun_free(input_ptr); + scratch = bb.data; + return response; + } + + static scan( + content: Uint8Array | string, + file_name: string, + loader?: Loader + ): ScanResult { + 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; + } + + encodeScan( + { + contents: contents_buffer, + path: file_name, + // @ts-ignore + loader: normalizeLoader(file_name, loader), + }, + bb + ); + const data = bb.toUint8Array(); + + const input_ptr = Bun.wasm_exports.bun_malloc(data.length); + var buffer = Bun._wasmPtrToSlice(input_ptr); + buffer.set(data); + + const resp_ptr = Bun.wasm_exports.scan(input_ptr); + var _bb = new ByteBuffer(Bun._wasmPtrToSlice(resp_ptr)); + const response = decodeScanResult(_bb); + Bun.wasm_exports.bun_free(input_ptr); + scratch = bb.data; + return response; + } +} + +export const transformSync = Bun.transformSync; +export const scan = Bun.scan; +export const init = Bun.init; +export default Bun; + +if ("window" in globalThis && !("Bun" in globalThis)) { + // @ts-ignore-line + globalThis.Bun = Bun; +} |