Skip to content
Open
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
9 changes: 9 additions & 0 deletions src/binaryDecode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
// Closure may analyze through the WASM_BINARY_DATA placeholder string into this
// function, leading into incorrect results.
/** @noinline */
#if ASSERTIONS
function binaryDecode(bin, expectedLength) {
// Each byte of the binary is one character of the string, so if the lengths
// differ, the file was not read as UTF-8 (for example a page without
// <meta charset="utf-8"> loading it from file://), and the bytes are wrong.
// Say so here, rather than let it fail as a confusing wasm CompileError.
assert(bin.length === expectedLength, `The embedded Wasm binary is ${bin.length} characters long, but should be ${expectedLength}: this file was not read as UTF-8 (SINGLE_FILE_BINARY_ENCODE). Serve it with charset=utf-8, add <meta charset="utf-8"> to the page, or build with -sSINGLE_FILE_BINARY_ENCODE=0`);
#else
function binaryDecode(bin) {
#endif
for (var i = 0, l = bin.length, o = new Uint8Array(l), c; i < l; ++i) {
c = bin.charCodeAt(i);
o[i] = ~c >> 8 & c; // Recover the null byte in a manner that is compatible with https://crbug.com/453961758
Expand Down
4 changes: 4 additions & 0 deletions src/postamble_minimal.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@ var instantiatePromise;
#endif

#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS
#if ASSERTIONS
Module['wasm'] = binaryDecode("<<< WASM_BINARY_DATA >>>", "<<< WASM_BINARY_SIZE >>>");
#else
Module['wasm'] = binaryDecode("<<< WASM_BINARY_DATA >>>");
#endif
#elif SINGLE_FILE && WASM == 1 && !WASM2JS
Module['wasm'] = base64Decode('<<< WASM_BINARY_DATA >>>');
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,11 @@ function getWasmBinary(file) {}

function findWasmBinary() {
#if SINGLE_FILE && SINGLE_FILE_BINARY_ENCODE && !WASM2JS
#if ASSERTIONS
return binaryDecode("<<< WASM_BINARY_DATA >>>", "<<< WASM_BINARY_SIZE >>>");
#else
return binaryDecode("<<< WASM_BINARY_DATA >>>");
#endif
#elif SINGLE_FILE
return base64Decode('<<< WASM_BINARY_DATA >>>');
#elif AUDIO_WORKLET || !EXPORT_ES6 // For an Audio Worklet, we cannot use `new URL()`.
Expand Down
12 changes: 12 additions & 0 deletions test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4727,6 +4727,18 @@ def test_single_file_locate_file(self):

self.run_browser('test.html', '/report_result?0')

# Tests that a SINGLE_FILE build loaded from a page whose charset is not UTF-8
# reports that, rather than failing to compile the mangled Wasm binary.
def test_single_file_not_utf8(self):
self.compile_btest('browser_test_hello_world.c', ['-o', 'test.js', '-sSINGLE_FILE', '-sASSERTIONS'])

create_file('test.html', '''
<meta charset="windows-1252">
<script src="test.js"></script>
''')

self.run_browser('test.html', 'this file was not read as UTF-8')

# Tests that SINGLE_FILE works as intended in a Worker in JS output
def test_single_file_worker_js(self):
self.btest_exit('browser_test_hello_world.c', cflags=['-sSINGLE_FILE'], run_in_worker=True)
Expand Down
13 changes: 12 additions & 1 deletion test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -9445,12 +9445,23 @@ def test_single_file_instantiate_failure(self):
self.run_process([EMCC, test_file('hello_world.c'), '-sSINGLE_FILE', '-sASSERTIONS=1', '-o', 'out.js'])
code = read_file('out.js')
# Stub out binaryDecode so that it returns an invalid wasm binary.
code = code.replace('function binaryDecode(bin) {', 'function binaryDecode(bin) { return new Uint8Array([0]);')
code = code.replace('function binaryDecode(bin, expectedLength) {', 'function binaryDecode(bin, expectedLength) { return new Uint8Array([0]);')
write_file('out_bad.js', code)
out = self.run_js('out_bad.js', assert_returncode=1)
self.assertContained('Aborted(CompileError', out)
self.assertNotContained('startsWith is not a function', out)

def test_single_file_not_utf8(self):
# A SINGLE_FILE_BINARY_ENCODE build read in an encoding other than UTF-8
# (as a browser does from a page with no charset) gets a mangled Wasm
# binary. With ASSERTIONS it says so instead of failing to compile it.
self.run_process([EMCC, test_file('hello_world.c'), '-sSINGLE_FILE', '-sASSERTIONS', '-o', 'out.js'])
# Each byte read as one Latin-1 character, as a Windows-1252 read does.
write_file('out_latin1.js', read_binary('out.js').decode('latin-1'))
out = self.run_js('out_latin1.js', assert_returncode=1)
self.assertContained('this file was not read as UTF-8', out)
self.assertNotContained('CompileError', out)

def test_single_file_disables_source_map(self):
cmd = [EMCC, test_file('hello_world.c'), '-sSINGLE_FILE', '-gsource-map']
stderr = self.run_process(cmd, stderr=PIPE).stderr
Expand Down
2 changes: 2 additions & 0 deletions tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2453,6 +2453,8 @@ def phase_binaryen(target, wasm_target):

if settings.SINGLE_FILE_BINARY_ENCODE:
js = do_replace(js, '"<<< WASM_BINARY_DATA >>>"', binary_encode(wasm_target))
if settings.ASSERTIONS:
js = do_replace(js, '"<<< WASM_BINARY_SIZE >>>"', str(os.path.getsize(wasm_target)))
else:
js = do_replace(js, '<<< WASM_BINARY_DATA >>>', base64_encode(wasm_target))
delete_file(wasm_target)
Expand Down