From 2aa806da6c065b7c22fb2358989bddd5606fba0d Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 21 Aug 2024 17:37:18 +0200 Subject: [PATCH] feat: add initSync() to initialize WASM synchronously This uses the synchronous WebAssembly APIs to compile the WASM part synchronously, which allows users to initialize the library and parse synchronously with WASM without having to resort to the JS version. This would be useful for Node.js core to use the WASM version in paths that require synchronous initialization (it currently always uses the JS version for that). --- README.md | 3 ++- lexer.d.ts | 1 + lexer.js | 1 + package.json | 3 ++- src/lexer.js | 22 ++++++++++++++++++---- test/_unit.js | 4 ++++ test/integration.js | 6 +++++- 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 10fe998..2716709 100755 --- a/README.md +++ b/README.md @@ -59,7 +59,8 @@ When using the ESM version, Wasm is supported instead: ```js import { parse, init } from 'cjs-module-lexer'; -// init needs to be called and waited upon +// init() needs to be called and waited upon, or use initSync() to compile +// Wasm blockingly and synchronously. await init(); const { exports, reexports } = parse(source); ``` diff --git a/lexer.d.ts b/lexer.d.ts index 99aa913..cda07d8 100755 --- a/lexer.d.ts +++ b/lexer.d.ts @@ -5,3 +5,4 @@ export interface Exports { export declare function parse(source: string, name?: string): Exports; export declare function init(): Promise; +export declare function initSync(): void; diff --git a/lexer.js b/lexer.js index 07955c5..aaf7dde 100755 --- a/lexer.js +++ b/lexer.js @@ -1439,4 +1439,5 @@ function isExpressionTerminator (curPos) { const initPromise = Promise.resolve(); module.exports.init = () => initPromise; +module.exports.initSync = () => {}; module.exports.parse = parseCJS; diff --git a/package.json b/package.json index 7e150f3..23d9623 100755 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "scripts": { "test-js": "mocha -b -u tdd test/*.js", "test-wasm": "cross-env WASM=1 mocha -b -u tdd test/*.js", - "test": "npm run test-wasm && npm run test-js", + "test-wasm-sync": "cross-env WASM_SYNC=1 mocha -b -u tdd test/*.js", + "test": "npm run test-wasm && npm run test-wasm-sync && npm run test-js", "bench": "node --expose-gc bench/index.mjs", "build": "node build.js ; babel dist/lexer.mjs -o dist/lexer.js ; terser dist/lexer.js -o dist/lexer.js", "build-wasm": "make lib/lexer.wasm && node build.js", diff --git a/src/lexer.js b/src/lexer.js index 3143fe5..2b3ea42 100755 --- a/src/lexer.js +++ b/src/lexer.js @@ -90,16 +90,30 @@ function copyLE (src, outBuf16) { outBuf16[i] = src.charCodeAt(i++); } +function getWasmBytes() { + const binary = 'WASM_BINARY'; // This string will be replaced by build.js. + if (typeof window !== 'undefined' && typeof atob === 'function') + return Uint8Array.from(atob(binary), x => x.charCodeAt(0)); + return Buffer.from(binary, 'base64'); +} + let initPromise; export function init () { if (initPromise) return initPromise; return initPromise = (async () => { - const compiled = await WebAssembly.compile( - (binary => typeof window !== 'undefined' && typeof atob === 'function' ? Uint8Array.from(atob(binary), x => x.charCodeAt(0)) : Buffer.from(binary, 'base64')) - ('WASM_BINARY') - ) + const compiled = await WebAssembly.compile(getWasmBytes()); const { exports } = await WebAssembly.instantiate(compiled); wasm = exports; })(); } + +export function initSync () { + if (wasm) { + return; + } + const compiled = new WebAssembly.Module(getWasmBytes()); + const { exports } = new WebAssembly.Instance(compiled); + wasm = exports; + return; +} diff --git a/test/_unit.js b/test/_unit.js index 36b27eb..d855559 100755 --- a/test/_unit.js +++ b/test/_unit.js @@ -7,6 +7,10 @@ async function loadParser () { const m = await import('../dist/lexer.mjs'); await m.init(); parse = m.parse; + } else if (process.env.WASM_SYNC) { + const m = require('../dist/lexer.js'); + m.initSync(); + parse = m.parse; } else { parse = require('../lexer.js').parse; diff --git a/test/integration.js b/test/integration.js index a14ea43..1a99de9 100755 --- a/test/integration.js +++ b/test/integration.js @@ -8,6 +8,10 @@ async function loadParser () { const m = await import('../dist/lexer.mjs'); await m.init(); parse = m.parse; + } else if (process.env.WASM_SYNC) { + const m = require('../dist/lexer.js'); + m.initSync(); + parse = m.parse; } else { parse = require('../lexer.js').parse; @@ -31,7 +35,7 @@ suite('Samples', () => { const selfSource = fs.readFileSync(process.cwd() + '/lexer.js').toString(); test('Self test', async () => { const { exports } = parse(selfSource); - assert.deepStrictEqual(exports, ['init', 'parse']); + assert.deepStrictEqual(exports, ['init', 'initSync', 'parse']); }); files.forEach(({ file, code }) => {