Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use asynchronous wasm instantiation methods where possible #174

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was rather thinking that returning this here would reduce the number of @ts-expect-errors we needed, but 🤷

}

/**
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();
richvdh marked this conversation as resolved.
Show resolved Hide resolved
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
Loading