From 0fe7eda5a66c9744ef6262da7ea5490f13d43280 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Thu, 28 Nov 2024 15:09:25 +0100 Subject: [PATCH] fix: Init WASM modules using Base64 (#2913) This PR changes the WASM loader slightly to package the WASM source code using Base64 and decode it at runtime. This has multiple effects: - Fixes an obscure OOM bug in Firefox that would only occur on Mac OS (possibly due to extremely large arrays?) - Reduces bundle size for Snaps that leverage WASM - Prevents bundler slowdown due to large arrays needing to be parsed by Babel This adds minor overhead to initializing WASM bundles, but this seems worth it given the effects above. --- .../examples/packages/wasm/snap.manifest.json | 2 +- .../snaps-cli/src/webpack/loaders/wasm.test.ts | 13 ++++++++++++- packages/snaps-cli/src/webpack/loaders/wasm.ts | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/examples/packages/wasm/snap.manifest.json b/packages/examples/packages/wasm/snap.manifest.json index 8c278e9d4e..bc7e47da6d 100644 --- a/packages/examples/packages/wasm/snap.manifest.json +++ b/packages/examples/packages/wasm/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "1LptzjDvA6o8jrzA9XsE7NYdhh7kx3alSd7pLwiUq0w=", + "shasum": "t4uK/pnTJODpftadTGKumNIEqmSCTP0zb87aMLMqFt8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-cli/src/webpack/loaders/wasm.test.ts b/packages/snaps-cli/src/webpack/loaders/wasm.test.ts index 38217725c9..feb80e48de 100644 --- a/packages/snaps-cli/src/webpack/loaders/wasm.test.ts +++ b/packages/snaps-cli/src/webpack/loaders/wasm.test.ts @@ -73,7 +73,18 @@ describe('loader', () => { " import { add } from "../src/bindings.ts"; - const bytes = new Uint8Array([0,97,115,109,1,0,0,0,1,12,2,96,2,127,127,1,127,96,1,127,1,127,2,26,1,18,46,46,47,115,114,99,47,98,105,110,100,105,110,103,115,46,116,115,3,97,100,100,0,0,3,2,1,1,5,3,1,0,0,7,22,2,9,102,105,98,111,110,97,99,99,105,0,1,6,109,101,109,111,114,121,2,0,10,54,1,52,1,3,127,65,1,33,1,32,0,65,0,74,4,64,3,64,32,0,65,1,107,34,0,4,64,32,2,32,1,16,0,33,3,32,1,33,2,32,3,33,1,12,1,11,11,32,1,15,11,65,0,11]); + const b64 = "AGFzbQEAAAABDAJgAn9/AX9gAX8BfwIaARIuLi9zcmMvYmluZGluZ3MudHMDYWRkAAADAgEBBQMBAAAHFgIJZmlib25hY2NpAAEGbWVtb3J5AgAKNgE0AQN/QQEhASAAQQBKBEADQCAAQQFrIgAEQCACIAEQACEDIAEhAiADIQEMAQsLIAEPC0EACw=="; + + function decode(encoded) { + const str = atob(encoded); + const bytes = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + bytes[i] = str.charCodeAt(i); + } + return bytes; + } + + const bytes = decode(b64); const module = new WebAssembly.Module(bytes); const instance = new WebAssembly.Instance(module, { "../src/bindings.ts": { add }, diff --git a/packages/snaps-cli/src/webpack/loaders/wasm.ts b/packages/snaps-cli/src/webpack/loaders/wasm.ts index 2b0bc15609..12961418a3 100644 --- a/packages/snaps-cli/src/webpack/loaders/wasm.ts +++ b/packages/snaps-cli/src/webpack/loaders/wasm.ts @@ -1,6 +1,6 @@ /* eslint-disable no-restricted-globals */ -import { assert } from '@metamask/utils'; +import { assert, bytesToBase64 } from '@metamask/utils'; import { dirname, resolve } from 'path'; import type { LoaderDefinitionFunction } from 'webpack'; @@ -80,8 +80,7 @@ const loader: LoaderDefinitionFunction = async function loader( ) { assert(source instanceof Uint8Array, 'Expected source to be a Uint8Array.'); - const bytes = new Uint8Array(source); - const wasmModule = await WebAssembly.compile(bytes); + const wasmModule = await WebAssembly.compile(source); // eslint-disable-next-line @typescript-eslint/no-shadow const exports = WebAssembly.Module.exports(wasmModule); @@ -104,7 +103,18 @@ const loader: LoaderDefinitionFunction = async function loader( return ` ${getImports(imports)} - const bytes = new Uint8Array(${JSON.stringify(Array.from(source))}); + const b64 = ${JSON.stringify(bytesToBase64(source))}; + + function decode(encoded) { + const str = atob(encoded); + const bytes = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + bytes[i] = str.charCodeAt(i); + } + return bytes; + } + + const bytes = decode(b64); const module = new WebAssembly.Module(bytes); const instance = new WebAssembly.Instance(module, { ${getModuleImports(imports)}