Skip to content

Commit

Permalink
Use asynchronous wasm instantiation methods where possible (#174)
Browse files Browse the repository at this point in the history
This fixes a problem (introduced in #167) on Chrome, which would complain about the module being too big. It also significantly simplifies the code on the browser entry points, because instantiateStreaming is widely supported.
  • Loading branch information
richvdh authored Dec 10, 2024
1 parent 74b73ae commit 68c75e5
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 88 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- The WebAssembly module is now loaded using `fetch` on Web platforms, reducing
the bundle size significantly, as well as the time it takes to compile it.
([#167](https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/167)),
([#174](https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/174)),
([#175](https://github.com/matrix-org/matrix-rust-sdk-crypto-wasm/pull/175))

**BREAKING CHANGES**
Expand Down
24 changes: 5 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,18 @@ bindings.__wbg_set_wasm(
let modPromise = null;

/**
* Loads the WASM module asynchronously
* Loads and instantiates the WASM module asynchronously
*
* @returns {Promise<void>}
*/
async function loadModule() {
let mod;
if (typeof WebAssembly.compileStreaming === "function") {
mod = await WebAssembly.compileStreaming(fetch(moduleUrl));
} else {
// Fallback to fetch and compile
const response = await fetch(moduleUrl);
if (!response.ok) {
throw new Error(`Failed to fetch wasm module: ${moduleUrl}`);
}
const bytes = await response.arrayBuffer();
mod = await WebAssembly.compile(bytes);
}

/** @type {{exports: typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = new WebAssembly.Instance(mod, {
async function loadModuleAsync() {
const { instance } = await WebAssembly.instantiateStreaming(fetch(moduleUrl), {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

bindings.__wbg_set_wasm(instance.exports);
// @ts-expect-error: Typescript doesn't know what the module exports are
instance.exports.__wbindgen_start();
}

Expand All @@ -82,7 +68,7 @@ async function loadModule() {
* @returns {Promise<void>}
*/
async function initAsync() {
if (!modPromise) modPromise = loadModule();
if (!modPromise) modPromise = loadModuleAsync();
await modPromise;
}

Expand Down
24 changes: 5 additions & 19 deletions index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,18 @@ bindings.__wbg_set_wasm(
let modPromise = null;

/**
* Loads the WASM module asynchronously
* Loads and instantiates the WASM module asynchronously
*
* @returns {Promise<void>}
*/
async function loadModule() {
let mod;
if (typeof WebAssembly.compileStreaming === "function") {
mod = await WebAssembly.compileStreaming(fetch(moduleUrl));
} else {
// Fallback to fetch and compile
const response = await fetch(moduleUrl);
if (!response.ok) {
throw new Error(`Failed to fetch wasm module: ${moduleUrl}`);
}
const bytes = await response.arrayBuffer();
mod = await WebAssembly.compile(bytes);
}

/** @type {{exports: typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = new WebAssembly.Instance(mod, {
async function loadModuleAsync() {
const { instance } = await WebAssembly.instantiateStreaming(fetch(moduleUrl), {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

bindings.__wbg_set_wasm(instance.exports);
// @ts-expect-error: Typescript doesn't know what the module exports are
instance.exports.__wbindgen_start();
}

Expand All @@ -82,7 +68,7 @@ async function loadModule() {
* @returns {Promise<void>}
*/
export async function initAsync() {
if (!modPromise) modPromise = loadModule();
if (!modPromise) modPromise = loadModuleAsync();
await modPromise;
}

Expand Down
59 changes: 34 additions & 25 deletions node.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ bindings.__wbg_set_wasm(
{},
{
get(_target, prop) {
const mod = loadModuleSync();
// @ts-expect-error: This results to an `any` type, which is fine
return initInstance(mod)[prop];
const instance = loadModuleSync();
// @ts-expect-error: This results in an `any` type, which is fine
return instance[prop];
},
},
),
Expand All @@ -55,48 +55,57 @@ let modPromise = null;
let initialised = false;

/**
* Loads the WASM module synchronously
* Loads and instantiates the WASM module synchronously
*
* It will throw if there is an attempt to laod the module asynchronously running
* It will throw if there is an attempt to load the module asynchronously running
*
* @returns {WebAssembly.Module}
* @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")}
*/
function loadModuleSync() {
if (modPromise) throw new Error("The WASM module is being loadded asynchronously but hasn't finished");
if (modPromise) throw new Error("The WASM module is being loaded asynchronously but hasn't finished");
const bytes = readFileSync(filename);
return new WebAssembly.Module(bytes);
const mod = new WebAssembly.Module(bytes);

const instance = new WebAssembly.Instance(mod, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

initInstance(instance);

// @ts-expect-error: Typescript doesn't know what the instance exports exactly
return instance.exports;
}

/**
* Loads the WASM module asynchronously
* Loads and instantiates the WASM module asynchronously
*
* @returns {Promise<WebAssembly.Module>}
* @returns {Promise<typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")>}
*/
async function loadModule() {
async function loadModuleAsync() {
const bytes = await readFile(filename);
return await WebAssembly.compile(bytes);
const { instance } = await WebAssembly.instantiate(bytes, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

initInstance(instance);

// @ts-expect-error: Typescript doesn't know what the instance exports exactly
return instance.exports;
}

/**
* Initializes the WASM module and returns the exports from the WASM module.
*
* @param {WebAssembly.Module} mod
* @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d")}
* @param {WebAssembly.Instance} instance
*/
function initInstance(mod) {
function initInstance(instance) {
if (initialised) throw new Error("initInstance called twice");

/** @type {{exports: typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = new WebAssembly.Instance(mod, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

bindings.__wbg_set_wasm(instance.exports);
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
instance.exports.__wbindgen_start();
initialised = true;
return instance.exports;
}

/**
Expand All @@ -108,7 +117,7 @@ function initInstance(mod) {
*/
async function initAsync() {
if (initialised) return;
if (!modPromise) modPromise = loadModule().then(initInstance);
if (!modPromise) modPromise = loadModuleAsync();
await modPromise;
}

Expand Down
59 changes: 34 additions & 25 deletions node.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ bindings.__wbg_set_wasm(
{},
{
get(_target, prop) {
const mod = loadModuleSync();
// @ts-expect-error: This results to an `any` type, which is fine
return initInstance(mod)[prop];
const instance = loadModuleSync();
// @ts-expect-error: This results in an `any` type, which is fine
return instance[prop];
},
},
),
Expand All @@ -55,48 +55,57 @@ let modPromise = null;
let initialised = false;

/**
* Loads the WASM module synchronously
* Loads and instantiates the WASM module synchronously
*
* It will throw if there is an attempt to laod the module asynchronously running
* It will throw if there is an attempt to load the module asynchronously running
*
* @returns {WebAssembly.Module}
* @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}
*/
function loadModuleSync() {
if (modPromise) throw new Error("The WASM module is being loadded asynchronously but hasn't finished");
if (modPromise) throw new Error("The WASM module is being loaded asynchronously but hasn't finished");
const bytes = readFileSync(filename);
return new WebAssembly.Module(bytes);
const mod = new WebAssembly.Module(bytes);

const instance = new WebAssembly.Instance(mod, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

initInstance(instance);

// @ts-expect-error: Typescript doesn't know what the module exports are
return instance.exports;
}

/**
* Loads the WASM module asynchronously
* Loads and instantiates the WASM module asynchronously
*
* @returns {Promise<WebAssembly.Module>}
* @returns {Promise<typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")>}
*/
async function loadModule() {
async function loadModuleAsync() {
const bytes = await readFile(filename);
return await WebAssembly.compile(bytes);
const { instance } = await WebAssembly.instantiate(bytes, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

initInstance(instance);

// @ts-expect-error: Typescript doesn't know what the module exports are
return instance.exports;
}

/**
* Initializes the WASM module and returns the exports from the WASM module.
*
* @param {WebAssembly.Module} mod
* @returns {typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}
* @param {WebAssembly.Instance} instance
*/
function initInstance(mod) {
function initInstance(instance) {
if (initialised) throw new Error("initInstance called twice");

/** @type {{exports: typeof import("./pkg/matrix_sdk_crypto_wasm_bg.wasm.d.ts")}} */
// @ts-expect-error: Typescript doesn't know what the instance exports exactly
const instance = new WebAssembly.Instance(mod, {
// @ts-expect-error: The bindings don't exactly match the 'ExportValue' type
"./matrix_sdk_crypto_wasm_bg.js": bindings,
});

bindings.__wbg_set_wasm(instance.exports);
// @ts-expect-error: Typescript doesn't know what the module exports are
instance.exports.__wbindgen_start();
initialised = true;
return instance.exports;
}

/**
Expand All @@ -108,7 +117,7 @@ function initInstance(mod) {
*/
export async function initAsync() {
if (initialised) return;
if (!modPromise) modPromise = loadModule().then(initInstance);
if (!modPromise) modPromise = loadModuleAsync();
await modPromise;
}

Expand Down

0 comments on commit 68c75e5

Please sign in to comment.