Skip to content

Commit

Permalink
feat: add initSync() to initialize WASM synchronously (#100)
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
joyeecheung committed Aug 25, 2024
1 parent 7c74749 commit 5bb8cc3
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 7 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
Expand Down
1 change: 1 addition & 0 deletions lexer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export interface Exports {

export declare function parse(source: string, name?: string): Exports;
export declare function init(): Promise<void>;
export declare function initSync(): void;
1 change: 1 addition & 0 deletions lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1439,4 +1439,5 @@ function isExpressionTerminator (curPos) {
const initPromise = Promise.resolve();

module.exports.init = () => initPromise;
module.exports.initSync = () => {};
module.exports.parse = parseCJS;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
22 changes: 18 additions & 4 deletions src/lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 4 additions & 0 deletions test/_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion test/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 }) => {
Expand Down

0 comments on commit 5bb8cc3

Please sign in to comment.