From 2ee33afd395890c8cc60ae2d0d43f894baa72f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 6 Dec 2025 17:11:10 +0200 Subject: [PATCH 1/4] Implement a FNV1-a style checksum to the binary decoded Wasm data, and display a help notice about needing to serve the HTML/JS file with UTF-8 encoding. This helps developers identify if their own shell files might not have the necessary encoding. --- src/postamble_minimal.js | 3 +++ src/preamble.js | 4 ++++ tools/link.py | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index 9472ec752e9f8..f6d4981265891 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -151,6 +151,9 @@ function initRuntime(wasmExports) { #if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS Module['wasm'] = binaryDecode("<<< WASM_BINARY_DATA >>>"); +#if ASSERTIONS +assert(Module['wasm'].reduce((x,y) => ((x^y)*65599) >>> 0, 2166136261) == "<<< WASM_BINARY_DATA_CHECKSUM >>>", "The checksum of the binary decoded WebAssembly Module code does not match the original data. Double check that this .html/.js file is interpreted with UTF-8 encoding, e.g. by setting inside of the HTML file, or by passing 'Content-Type: application/javascript; charset=utf-8' HTTP header."); +#endif #elif SINGLE_FILE && WASM == 1 && !WASM2JS Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>'); #endif diff --git a/src/preamble.js b/src/preamble.js index 1df464281ee21..151ef0bee0b69 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -876,6 +876,10 @@ function getWasmImports() { return exports; #else wasmBinaryFile ??= findWasmBinary(); +#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS && ASSERTIONS + assert(wasmBinaryFile.reduce((x,y) => ((x^y)*65599) >>> 0, 2166136261) == "<<< WASM_BINARY_DATA_CHECKSUM >>>", "The checksum of the binary decoded WebAssembly Module code does not match the original data. Double check that this .html/.js file is interpreted with UTF-8 encoding, e.g. by setting inside of the HTML file, or by passing 'Content-Type: application/javascript; charset=utf-8' HTTP header."); +#endif + #if WASM_ASYNC_COMPILATION #if RUNTIME_DEBUG dbg('asynchronously preparing wasm'); diff --git a/tools/link.py b/tools/link.py index ecb2df25adaa6..9b42eefb36834 100644 --- a/tools/link.py +++ b/tools/link.py @@ -2436,6 +2436,12 @@ def phase_binaryen(target, options, wasm_target): if settings.SINGLE_FILE_BINARY_ENCODE: js = do_replace(js, '"<<< WASM_BINARY_DATA >>>"', binary_encode(wasm_target)) + if settings.ASSERTIONS: + def checksum(a): + h = 2166136261 + for b in a: h = (h ^ b) * 65599 & 0xffffffff + return h + js = do_replace(js, '"<<< WASM_BINARY_DATA_CHECKSUM >>>"', str(checksum(utils.read_binary(wasm_target)))) else: js = do_replace(js, '<<< WASM_BINARY_DATA >>>', base64_encode(wasm_target)) delete_file(wasm_target) From 82ea1fc683b19ca07d123bf3c8d1270ddd87029b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Sat, 6 Dec 2025 17:30:51 +0200 Subject: [PATCH 2/4] ruff --- tools/link.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/link.py b/tools/link.py index 9b42eefb36834..b1d03f1d23494 100644 --- a/tools/link.py +++ b/tools/link.py @@ -2439,7 +2439,8 @@ def phase_binaryen(target, options, wasm_target): if settings.ASSERTIONS: def checksum(a): h = 2166136261 - for b in a: h = (h ^ b) * 65599 & 0xffffffff + for b in a: + h = (h ^ b) * 65599 & 0xffffffff return h js = do_replace(js, '"<<< WASM_BINARY_DATA_CHECKSUM >>>"', str(checksum(utils.read_binary(wasm_target)))) else: From 2279bcf0fb72423d8c1c3058aa6bc5c8db17b675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 9 Dec 2025 21:05:34 +0200 Subject: [PATCH 3/4] review --- src/postamble_minimal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index f6d4981265891..fe332b458057a 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -152,7 +152,7 @@ function initRuntime(wasmExports) { #if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS Module['wasm'] = binaryDecode("<<< WASM_BINARY_DATA >>>"); #if ASSERTIONS -assert(Module['wasm'].reduce((x,y) => ((x^y)*65599) >>> 0, 2166136261) == "<<< WASM_BINARY_DATA_CHECKSUM >>>", "The checksum of the binary decoded WebAssembly Module code does not match the original data. Double check that this .html/.js file is interpreted with UTF-8 encoding, e.g. by setting inside of the HTML file, or by passing 'Content-Type: application/javascript; charset=utf-8' HTTP header."); +assert(Module['wasm'].reduce((x,y) => ((x^y)*65599) >>> 0, 2166136261) == "<<< WASM_BINARY_DATA_CHECKSUM >>>", "Invalid checksum on embedded WebAssembly module. Check that this .html/.js file is interpreted with UTF-8 encoding, e.g. by setting inside of the HTML file, or by passing 'Content-Type: application/javascript; charset=utf-8' HTTP header."); #endif #elif SINGLE_FILE && WASM == 1 && !WASM2JS Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>'); From ab8f793ca96012b54a10e17de46a65f75eb4b1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 11 Dec 2025 01:50:35 +0200 Subject: [PATCH 4/4] Automatic rebaseline of codesize expectations. NFC This is an automatic change generated by tools/maint/rebaseline_tests.py. The following (4) test expectation files were updated by running the tests with `--rebaseline`: ``` codesize/test_codesize_hello_O0.json: 39332 => 39358 [+26 bytes / +0.07%] test/codesize/test_codesize_minimal_O0.expected.js updated codesize/test_codesize_minimal_O0.json: 20603 => 20629 [+26 bytes / +0.13%] codesize/test_unoptimized_code_size.json: 180926 => 180997 [+71 bytes / +0.04%] Average change: +0.08% (+0.04% - +0.13%) ``` --- test/codesize/test_codesize_hello_O0.json | 8 ++++---- .../test_codesize_minimal_O0.expected.js | 3 ++- test/codesize/test_codesize_minimal_O0.json | 8 ++++---- test/codesize/test_unoptimized_code_size.json | 16 ++++++++-------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/test/codesize/test_codesize_hello_O0.json b/test/codesize/test_codesize_hello_O0.json index 9352de3d1e4b3..1937b4808b565 100644 --- a/test/codesize/test_codesize_hello_O0.json +++ b/test/codesize/test_codesize_hello_O0.json @@ -1,10 +1,10 @@ { - "a.out.js": 24194, - "a.out.js.gz": 8693, + "a.out.js": 24220, + "a.out.js.gz": 8700, "a.out.nodebug.wasm": 15138, "a.out.nodebug.wasm.gz": 7455, - "total": 39332, - "total_gz": 16148, + "total": 39358, + "total_gz": 16155, "sent": [ "fd_write" ], diff --git a/test/codesize/test_codesize_minimal_O0.expected.js b/test/codesize/test_codesize_minimal_O0.expected.js index 958ea174584a4..c813b8d277360 100644 --- a/test/codesize/test_codesize_minimal_O0.expected.js +++ b/test/codesize/test_codesize_minimal_O0.expected.js @@ -28,7 +28,7 @@ return; } - var currentSafariVersion = userAgent.includes("Safari/") && userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED; + var currentSafariVersion = userAgent.includes("Safari/") && !userAgent.includes("Chrome/") && userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED; if (currentSafariVersion < 150000) { throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable(150000) } (detected v${currentSafariVersion})`); } @@ -709,6 +709,7 @@ async function createWasm() { var info = getWasmImports(); wasmBinaryFile ??= findWasmBinary(); + var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); var exports = receiveInstantiationResult(result); return exports; diff --git a/test/codesize/test_codesize_minimal_O0.json b/test/codesize/test_codesize_minimal_O0.json index 86252b57baafc..f8a4d00f4cb4e 100644 --- a/test/codesize/test_codesize_minimal_O0.json +++ b/test/codesize/test_codesize_minimal_O0.json @@ -1,10 +1,10 @@ { - "a.out.js": 19467, - "a.out.js.gz": 7003, + "a.out.js": 19493, + "a.out.js.gz": 7010, "a.out.nodebug.wasm": 1136, "a.out.nodebug.wasm.gz": 659, - "total": 20603, - "total_gz": 7662, + "total": 20629, + "total_gz": 7669, "sent": [], "imports": [], "exports": [ diff --git a/test/codesize/test_unoptimized_code_size.json b/test/codesize/test_unoptimized_code_size.json index 08e0200a6d05f..4ab9c75ec827c 100644 --- a/test/codesize/test_unoptimized_code_size.json +++ b/test/codesize/test_unoptimized_code_size.json @@ -1,16 +1,16 @@ { - "hello_world.js": 56928, - "hello_world.js.gz": 17707, + "hello_world.js": 56963, + "hello_world.js.gz": 17715, "hello_world.wasm": 15138, "hello_world.wasm.gz": 7455, - "no_asserts.js": 26632, - "no_asserts.js.gz": 8884, + "no_asserts.js": 26633, + "no_asserts.js.gz": 8887, "no_asserts.wasm": 12187, "no_asserts.wasm.gz": 5984, - "strict.js": 54903, - "strict.js.gz": 17041, + "strict.js": 54938, + "strict.js.gz": 17049, "strict.wasm": 15138, "strict.wasm.gz": 7450, - "total": 180926, - "total_gz": 64521 + "total": 180997, + "total_gz": 64540 }