From 1f1972d444463df8a451baae57ea1d21ab791939 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 29 Oct 2026 10:45:23 -0700 Subject: [PATCH] [FS] Only generate `FS_` library aliases for functions In #24116, a loop was added at the end of `src/lib/libfs.js` to generate `$FS_` wrapper functions (`(...args) => FS.foo(...)`) for `FS` API methods. While the comment noted that non-functions should be skipped, the condition only checked `key[0] !== key[0].toLowerCase()` (which filters out uppercase classes like `ErrnoError`, `FSStream`, and `FSNode`), assuming all lowercase properties on `LibraryFS.$FS` were functions. In practice, `LibraryFS.$FS` also contains lowercase state and object properties (`root`, `mounts`, `devices`, `streams`, `nextInode`, `nameTable`, `currentPath`, `initialized`, `ignorePermissions`, `syncFSRequests`, and `chrdev_stream_ops`). Wrapping these in `(...args) => FS.(...args)` is broken: - Calling any of these generated aliases at runtime would unconditionally throw `TypeError: FS. is not a function`. - When compiling with `-sINCLUDE_FULL_LIBRARY` and `--closure=1` (such as with `-sEXPORT_ES6`), Closure Compiler rejects calls to these non-callable properties on `FS`. - In unoptimized (`-O0` / `ASSERTIONS`) builds, these bogus symbols pollute `missingLibrarySymbols`, unnecessarily increasing JS code size. Check `typeof LibraryFS.$FS[key] !== 'function'` alongside the existing lowercase check so that only actual methods on `FS` receive function aliases. See: #27747 --- src/lib/libfs.js | 2 +- test/codesize/test_codesize_hello_O0.json | 8 ++++---- test/codesize/test_codesize_minimal_O0.expected.js | 11 ----------- test/codesize/test_codesize_minimal_O0.json | 8 ++++---- test/codesize/test_unoptimized_code_size.json | 12 ++++++------ 5 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/lib/libfs.js b/src/lib/libfs.js index af6f6f6728d43..a2da5eafcc6fb 100644 --- a/src/lib/libfs.js +++ b/src/lib/libfs.js @@ -1954,7 +1954,7 @@ FS.staticInit();`; for (let key in LibraryFS.$FS) { const alias = `$FS_${key}`; // Skip defining the alias if it already exists or if it's not an API function. - if (LibraryFS[alias] || key[0] !== key[0].toLowerCase()) { + if (LibraryFS[alias] || typeof LibraryFS.$FS[key] !== 'function' || key[0] !== key[0].toLowerCase()) { continue; } LibraryFS[alias] = `(...args) => FS.${key}(...args)`; diff --git a/test/codesize/test_codesize_hello_O0.json b/test/codesize/test_codesize_hello_O0.json index bb883c8e5b440..e4ee584222637 100644 --- a/test/codesize/test_codesize_hello_O0.json +++ b/test/codesize/test_codesize_hello_O0.json @@ -1,10 +1,10 @@ { - "a.out.js": 23551, - "a.out.js.gz": 8559, + "a.out.js": 23395, + "a.out.js.gz": 8502, "a.out.nodebug.wasm": 14275, "a.out.nodebug.wasm.gz": 7098, - "total": 37826, - "total_gz": 15657, + "total": 37670, + "total_gz": 15600, "sent": [ "fd_write" ], diff --git a/test/codesize/test_codesize_minimal_O0.expected.js b/test/codesize/test_codesize_minimal_O0.expected.js index 797b9905a86a5..e82c6c5015078 100644 --- a/test/codesize/test_codesize_minimal_O0.expected.js +++ b/test/codesize/test_codesize_minimal_O0.expected.js @@ -1023,16 +1023,6 @@ missingLibrarySymbols.forEach(missingLibrarySymbol) 'FS_createDevice', 'FS_readFile', 'FS', - 'FS_root', - 'FS_mounts', - 'FS_devices', - 'FS_streams', - 'FS_nextInode', - 'FS_nameTable', - 'FS_currentPath', - 'FS_initialized', - 'FS_ignorePermissions', - 'FS_syncFSRequests', 'FS_lookupPath', 'FS_getPath', 'FS_hashName', @@ -1064,7 +1054,6 @@ missingLibrarySymbols.forEach(missingLibrarySymbol) 'FS_closeStream', 'FS_dupStream', 'FS_doSetAttr', - 'FS_chrdev_stream_ops', 'FS_major', 'FS_minor', 'FS_makedev', diff --git a/test/codesize/test_codesize_minimal_O0.json b/test/codesize/test_codesize_minimal_O0.json index e52559434d575..497e2d8deb801 100644 --- a/test/codesize/test_codesize_minimal_O0.json +++ b/test/codesize/test_codesize_minimal_O0.json @@ -1,10 +1,10 @@ { - "a.out.js": 18738, - "a.out.js.gz": 6730, + "a.out.js": 18582, + "a.out.js.gz": 6672, "a.out.nodebug.wasm": 1071, "a.out.nodebug.wasm.gz": 636, - "total": 19809, - "total_gz": 7366, + "total": 19653, + "total_gz": 7308, "sent": [], "imports": [], "exports": [ diff --git a/test/codesize/test_unoptimized_code_size.json b/test/codesize/test_unoptimized_code_size.json index 20b14290502c3..49629a20e7232 100644 --- a/test/codesize/test_unoptimized_code_size.json +++ b/test/codesize/test_unoptimized_code_size.json @@ -1,16 +1,16 @@ { - "hello_world.js": 53025, - "hello_world.js.gz": 16866, + "hello_world.js": 52814, + "hello_world.js.gz": 16807, "hello_world.wasm": 14275, "hello_world.wasm.gz": 7098, "no_asserts.js": 22074, "no_asserts.js.gz": 7847, "no_asserts.wasm": 11389, "no_asserts.wasm.gz": 5645, - "strict.js": 50155, - "strict.js.gz": 15884, + "strict.js": 49944, + "strict.js.gz": 15826, "strict.wasm": 14275, "strict.wasm.gz": 7096, - "total": 165193, - "total_gz": 60436 + "total": 164771, + "total_gz": 60319 }