From 18d16f3ced6396c00dfac707ff43827a664ad748 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 2 Nov 2026 12:10:52 -0800 Subject: [PATCH 1/2] [JS] Replace legacy `Array.prototype.slice.call` idioms Historically, `Array.prototype.slice.call` was used in two places in our JS runtime and library code: 1. On `TypedArray` instances (in `libglemu.js`, `libmemfs.js`, and `runtime_debug.js`). Before ES2015 incorporated `TypedArray` into the ECMAScript standard, the Khronos WebGL `TypedArray` specification lacked `%TypedArray%.prototype.slice()` for shallow copying as well as `%TypedArray%.prototype.toString()` for comma-separated formatting (calling `.toString()` inherited `Object.prototype.toString` and produced `"[object Float32Array]"`). Converting to a plain `Array` via `Array.prototype.slice.call` was therefore necessary both to clone matrices in `glPushMatrix` and to format typed arrays in debug logs. 2. On `arguments` objects (in `jsifier.mjs` and `libcore.js`) before `Array.from()` was introduced in ES2015. `%TypedArray%.prototype.slice()`, `%TypedArray%.prototype.toString()`, and `Array.from()` have all been supported since Chrome 45, Firefox 38, Safari 10, and Node.js 4.0 (2015-2016), well before Emscripten's minimum supported engine versions (Chrome 85, Firefox 79, Safari 15, Node 18.3). In `runtime_debug.js`, calling `arg.slice(0, 400)` directly also avoids copying the entire typed array before slicing to 400 elements, and in `glPushMatrix` it preserves the `Float32Array` type on the matrix stack. --- src/jsifier.mjs | 2 +- src/lib/libcore.js | 2 +- src/lib/libglemu.js | 4 ++-- src/lib/libmemfs.js | 2 +- src/runtime_debug.js | 4 ++-- test/codesize/test_codesize_cxx_ctors1.json | 8 ++++---- test/codesize/test_codesize_cxx_ctors2.json | 8 ++++---- test/codesize/test_codesize_cxx_except.json | 8 ++++---- test/codesize/test_codesize_cxx_except_wasm.json | 8 ++++---- test/codesize/test_codesize_cxx_except_wasm_legacy.json | 8 ++++---- test/codesize/test_codesize_cxx_lto.json | 8 ++++---- test/codesize/test_codesize_cxx_mangle.json | 8 ++++---- test/codesize/test_codesize_cxx_noexcept.json | 8 ++++---- test/codesize/test_codesize_file_preload.expected.js | 2 +- test/codesize/test_codesize_file_preload.json | 8 ++++---- test/codesize/test_codesize_files_js_fs.json | 8 ++++---- test/codesize/test_codesize_hello_dylink.json | 8 ++++---- test/codesize/test_codesize_hello_dylink_all.json | 4 ++-- test/codesize/test_no_closure_code_size.json | 8 ++++---- 19 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/jsifier.mjs b/src/jsifier.mjs index b0b2210408a47..86e177b0052bc 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -641,7 +641,7 @@ export async function runJSify(outputFile, symbolsOnly) { } return `\ function(${args}) { - dbg("[library call:${mangled}: " + Array.prototype.slice.call(arguments).map(prettyPrint) + "]"); + dbg("[library call:${mangled}: " + Array.from(arguments, prettyPrint) + "]"); ${run_func} dbg(" [ return:" + prettyPrint(ret)); return ret; diff --git a/src/lib/libcore.js b/src/lib/libcore.js index 8c8a7dae86adb..a588e2e55d017 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -2525,7 +2525,7 @@ function wrapSyscallFunction(x, library, isWasi) { post += 'SYSCALLS.varargs = undefined;\n'; } } - pre += `dbg('syscall! ${x}: [' + Array.prototype.slice.call(arguments) + ']');\n`; + pre += `dbg('syscall! ${x}: [' + Array.from(arguments) + ']');\n`; pre += 'var canWarn = true;\n'; pre += 'var ret = (() => {'; post += '})();\n'; diff --git a/src/lib/libglemu.js b/src/lib/libglemu.js index 2427ef606ab06..b17fee7e07683 100644 --- a/src/lib/libglemu.js +++ b/src/lib/libglemu.js @@ -3542,7 +3542,7 @@ var LibraryGLEmulation = { GLImmediate.matricesModified = true; GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; GLImmediate.matrixStack[GLImmediate.currentMatrix].push( - Array.prototype.slice.call(GLImmediate.matrix[GLImmediate.currentMatrix])); + GLImmediate.matrix[GLImmediate.currentMatrix].slice()); }, glPopMatrix: () => { @@ -3570,7 +3570,7 @@ var LibraryGLEmulation = { glLoadMatrixf: (matrix) => { #if GL_DEBUG - if (GL.debug) dbg('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16))); + if (GL.debug) dbg('glLoadMatrixf receiving: ' + HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)); #endif GLImmediate.matricesModified = true; GLImmediate.matrixVersion[GLImmediate.currentMatrix] = (GLImmediate.matrixVersion[GLImmediate.currentMatrix] + 1)|0; diff --git a/src/lib/libmemfs.js b/src/lib/libmemfs.js index 62c5c51ca66ab..553a4d3a39205 100644 --- a/src/lib/libmemfs.js +++ b/src/lib/libmemfs.js @@ -333,7 +333,7 @@ addToLibrary({ if (contents.subarray) { contents = contents.subarray(position, position + length); } else { - contents = Array.prototype.slice.call(contents, position, position + length); + contents = contents.slice(position, position + length); } } HEAP8.set(contents, ptr); diff --git a/src/runtime_debug.js b/src/runtime_debug.js index 8496b25968c0d..3b74030346081 100644 --- a/src/runtime_debug.js +++ b/src/runtime_debug.js @@ -275,8 +275,8 @@ function prettyPrint(arg) { if (arg.toString() == '[object HTMLImageElement]') { return arg + '\n\n'; } - if (arg.byteLength) { - return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; + if (arg.subarray) { + return '{' + arg.subarray(0, 400) + '}'; } if (typeof arg === 'function') { return ''; diff --git a/test/codesize/test_codesize_cxx_ctors1.json b/test/codesize/test_codesize_cxx_ctors1.json index cf8d98c61d79e..874d7756b88de 100644 --- a/test/codesize/test_codesize_cxx_ctors1.json +++ b/test/codesize/test_codesize_cxx_ctors1.json @@ -1,10 +1,10 @@ { - "a.out.js": 19093, - "a.out.js.gz": 8090, + "a.out.js": 19070, + "a.out.js.gz": 8074, "a.out.nodebug.wasm": 133990, "a.out.nodebug.wasm.gz": 51234, - "total": 153083, - "total_gz": 59324, + "total": 153060, + "total_gz": 59308, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_cxx_ctors2.json b/test/codesize/test_codesize_cxx_ctors2.json index 42ba60bb02546..39e73decc70a3 100644 --- a/test/codesize/test_codesize_cxx_ctors2.json +++ b/test/codesize/test_codesize_cxx_ctors2.json @@ -1,10 +1,10 @@ { - "a.out.js": 19070, - "a.out.js.gz": 8074, + "a.out.js": 19047, + "a.out.js.gz": 8058, "a.out.nodebug.wasm": 133418, "a.out.nodebug.wasm.gz": 50931, - "total": 152488, - "total_gz": 59005, + "total": 152465, + "total_gz": 58989, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_cxx_except.json b/test/codesize/test_codesize_cxx_except.json index aa0ced855224f..e6de6a94a73a2 100644 --- a/test/codesize/test_codesize_cxx_except.json +++ b/test/codesize/test_codesize_cxx_except.json @@ -1,10 +1,10 @@ { - "a.out.js": 22786, - "a.out.js.gz": 9044, + "a.out.js": 22763, + "a.out.js.gz": 9028, "a.out.nodebug.wasm": 174365, "a.out.nodebug.wasm.gz": 58527, - "total": 197151, - "total_gz": 67571, + "total": 197128, + "total_gz": 67555, "sent": [ "__cxa_begin_catch", "__cxa_end_catch", diff --git a/test/codesize/test_codesize_cxx_except_wasm.json b/test/codesize/test_codesize_cxx_except_wasm.json index 8bfae031e5ad7..d89f7efe16315 100644 --- a/test/codesize/test_codesize_cxx_except_wasm.json +++ b/test/codesize/test_codesize_cxx_except_wasm.json @@ -1,10 +1,10 @@ { - "a.out.js": 18892, - "a.out.js.gz": 8001, + "a.out.js": 18869, + "a.out.js.gz": 7984, "a.out.nodebug.wasm": 149635, "a.out.nodebug.wasm.gz": 56293, - "total": 168527, - "total_gz": 64294, + "total": 168504, + "total_gz": 64277, "sent": [ "_abort_js", "_tzset_js", diff --git a/test/codesize/test_codesize_cxx_except_wasm_legacy.json b/test/codesize/test_codesize_cxx_except_wasm_legacy.json index ddf7182dde2b9..a400f55c13528 100644 --- a/test/codesize/test_codesize_cxx_except_wasm_legacy.json +++ b/test/codesize/test_codesize_cxx_except_wasm_legacy.json @@ -1,10 +1,10 @@ { - "a.out.js": 18970, - "a.out.js.gz": 8024, + "a.out.js": 18947, + "a.out.js.gz": 8007, "a.out.nodebug.wasm": 147417, "a.out.nodebug.wasm.gz": 55962, - "total": 166387, - "total_gz": 63986, + "total": 166364, + "total_gz": 63969, "sent": [ "_abort_js", "_tzset_js", diff --git a/test/codesize/test_codesize_cxx_lto.json b/test/codesize/test_codesize_cxx_lto.json index 9491669f3612a..2cd723bd5d8a1 100644 --- a/test/codesize/test_codesize_cxx_lto.json +++ b/test/codesize/test_codesize_cxx_lto.json @@ -1,10 +1,10 @@ { - "a.out.js": 18432, - "a.out.js.gz": 7767, + "a.out.js": 18409, + "a.out.js.gz": 7753, "a.out.nodebug.wasm": 101025, "a.out.nodebug.wasm.gz": 38272, - "total": 119457, - "total_gz": 46039, + "total": 119434, + "total_gz": 46025, "sent": [ "a (emscripten_resize_heap)", "b (_setitimer_js)", diff --git a/test/codesize/test_codesize_cxx_mangle.json b/test/codesize/test_codesize_cxx_mangle.json index 10e6f5f4789c2..b978e003244b8 100644 --- a/test/codesize/test_codesize_cxx_mangle.json +++ b/test/codesize/test_codesize_cxx_mangle.json @@ -1,10 +1,10 @@ { - "a.out.js": 22836, - "a.out.js.gz": 9064, + "a.out.js": 22813, + "a.out.js.gz": 9048, "a.out.nodebug.wasm": 241374, "a.out.nodebug.wasm.gz": 80940, - "total": 264210, - "total_gz": 90004, + "total": 264187, + "total_gz": 89988, "sent": [ "__cxa_begin_catch", "__cxa_end_catch", diff --git a/test/codesize/test_codesize_cxx_noexcept.json b/test/codesize/test_codesize_cxx_noexcept.json index af56b36852aef..3fa0b95255bf6 100644 --- a/test/codesize/test_codesize_cxx_noexcept.json +++ b/test/codesize/test_codesize_cxx_noexcept.json @@ -1,10 +1,10 @@ { - "a.out.js": 19093, - "a.out.js.gz": 8090, + "a.out.js": 19070, + "a.out.js.gz": 8074, "a.out.nodebug.wasm": 135897, "a.out.nodebug.wasm.gz": 51846, - "total": 154990, - "total_gz": 59936, + "total": 154967, + "total_gz": 59920, "sent": [ "__cxa_throw", "_abort_js", diff --git a/test/codesize/test_codesize_file_preload.expected.js b/test/codesize/test_codesize_file_preload.expected.js index f466d6a7ac9e7..fe32194f300e7 100644 --- a/test/codesize/test_codesize_file_preload.expected.js +++ b/test/codesize/test_codesize_file_preload.expected.js @@ -1051,7 +1051,7 @@ var MEMFS = { if (contents.subarray) { contents = contents.subarray(position, position + length); } else { - contents = Array.prototype.slice.call(contents, position, position + length); + contents = contents.slice(position, position + length); } } HEAP8.set(contents, ptr); diff --git a/test/codesize/test_codesize_file_preload.json b/test/codesize/test_codesize_file_preload.json index 0992b207679d8..f9051d426611c 100644 --- a/test/codesize/test_codesize_file_preload.json +++ b/test/codesize/test_codesize_file_preload.json @@ -1,10 +1,10 @@ { - "a.out.js": 22145, - "a.out.js.gz": 9246, + "a.out.js": 22122, + "a.out.js.gz": 9229, "a.out.nodebug.wasm": 1198, "a.out.nodebug.wasm.gz": 730, - "total": 23343, - "total_gz": 9976, + "total": 23320, + "total_gz": 9959, "sent": [ "a (fd_write)" ], diff --git a/test/codesize/test_codesize_files_js_fs.json b/test/codesize/test_codesize_files_js_fs.json index 8c28895ce9f0e..697a2bfb46035 100644 --- a/test/codesize/test_codesize_files_js_fs.json +++ b/test/codesize/test_codesize_files_js_fs.json @@ -1,10 +1,10 @@ { - "a.out.js": 17744, - "a.out.js.gz": 7414, + "a.out.js": 17721, + "a.out.js.gz": 7396, "a.out.nodebug.wasm": 381, "a.out.nodebug.wasm.gz": 258, - "total": 18125, - "total_gz": 7672, + "total": 18102, + "total_gz": 7654, "sent": [ "a (fd_write)", "b (fd_read)", diff --git a/test/codesize/test_codesize_hello_dylink.json b/test/codesize/test_codesize_hello_dylink.json index a8d2adbd57069..3678270780d91 100644 --- a/test/codesize/test_codesize_hello_dylink.json +++ b/test/codesize/test_codesize_hello_dylink.json @@ -1,10 +1,10 @@ { - "a.out.js": 26205, - "a.out.js.gz": 11141, + "a.out.js": 26182, + "a.out.js.gz": 11126, "a.out.nodebug.wasm": 16986, "a.out.nodebug.wasm.gz": 8667, - "total": 43191, - "total_gz": 19808, + "total": 43168, + "total_gz": 19793, "sent": [ "__syscall_stat64", "emscripten_resize_heap", diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 6a81f365f8654..db38a3cbf1000 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 272573, + "a.out.js": 272550, "a.out.nodebug.wasm": 575827, - "total": 848400, + "total": 848377, "sent": [ "IMG_Init", "IMG_Load", diff --git a/test/codesize/test_no_closure_code_size.json b/test/codesize/test_no_closure_code_size.json index f326595f24fb2..e86184d04932c 100644 --- a/test/codesize/test_no_closure_code_size.json +++ b/test/codesize/test_no_closure_code_size.json @@ -11,10 +11,10 @@ "strict.js.gz": 2189, "strict.wasm": 1188, "strict.wasm.gz": 733, - "files.js": 53183, - "files.js.gz": 14862, + "files.js": 53162, + "files.js.gz": 14848, "files.wasm": 371, "files.wasm.gz": 264, - "total": 75737, - "total_gz": 24381 + "total": 75716, + "total_gz": 24367 } From 439eacc9066f9a2eec8112ab126625902511715d Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 2 Nov 2026 12:23:05 -0800 Subject: [PATCH 2/2] [JS] Adopt additional modern JS patterns and ESLint rules. NFC Follow-up to #28129: - Enable `prefer-exponentiation-operator`, `prefer-object-spread`, `prefer-rest-params`, `prefer-spread`, and `logical-assignment-operators` in `eslint.config.mjs`. - Use `Object.values`, direct integer indexing, or `??=` in places where `$hasOwn` is not needed. - Use `Map` instead of plain `{}` objects for lookup tables in `acorn-optimizer.mjs` and `libglfw.js`. - Replace legacy `.apply`, `.indexOf`, `Math.pow`, and `arr[arr.length - 1]` idioms with `.call` / spread syntax, `.includes`, `**` / `Math.hypot`, and `.at(-1)`. --- eslint.config.mjs | 5 ++ src/deterministic.js | 2 +- src/jsifier.mjs | 2 +- src/lib/libbrowser.js | 2 +- src/lib/libdylink.js | 8 +-- src/lib/libembind.js | 14 ++--- src/lib/libembind_shared.js | 11 ++-- src/lib/libemval.js | 2 +- src/lib/libglfw.js | 20 +++---- src/lib/liblegacy.js | 10 +--- src/lib/libsdl.js | 7 +-- src/lib/libwasmfs_fetch.js | 2 +- src/lib/libwebgl.js | 2 +- src/parseTools.mjs | 2 +- src/postamble_minimal.js | 2 +- src/preamble.js | 2 +- src/utility.mjs | 4 +- test/codesize/test_codesize_hello_dylink.json | 8 +-- .../test_codesize_hello_dylink_all.json | 4 +- ...inimal_runtime_code_size_hello_embind.json | 8 +-- ...al_runtime_code_size_hello_embind_val.json | 8 +-- tools/acorn-optimizer.mjs | 58 ++++++++----------- tools/js_manipulation.py | 2 +- tools/unsafe_optimizations.mjs | 4 +- 24 files changed, 84 insertions(+), 105 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9501ae1b5faac..cc9c477ef17da 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -79,6 +79,11 @@ export default [{ 'arrow-body-style': ['error', 'as-needed'], 'space-infix-ops': 'error', 'prefer-object-has-own': 'error', + 'prefer-exponentiation-operator': 'error', + 'prefer-object-spread': 'error', + 'prefer-rest-params': 'error', + 'prefer-spread': 'error', + 'logical-assignment-operators': ['error', 'always'], quotes: ['error', 'single', { avoidEscape: true, diff --git a/src/deterministic.js b/src/deterministic.js index 0282efa4909ca..c1e119beff73d 100644 --- a/src/deterministic.js +++ b/src/deterministic.js @@ -6,7 +6,7 @@ let MAGIC = 0; Math.random = () => { - MAGIC = Math.pow(MAGIC + 1.8912, 3) % 1; + MAGIC = ((MAGIC + 1.8912) ** 3) % 1; return MAGIC; }; diff --git a/src/jsifier.mjs b/src/jsifier.mjs index 86e177b0052bc..8eaf00c3fcb13 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -1023,7 +1023,7 @@ function(${args}) { // emits // 'var foo;[code here verbatim];' contentText = 'var ' + mangled + snippet; - if (snippet[snippet.length - 1] != ';' && snippet[snippet.length - 1] != '}') { + if (snippet.at(-1) != ';' && snippet.at(-1) != '}') { contentText += ';'; } } else if (typeof snippet === 'undefined') { diff --git a/src/lib/libbrowser.js b/src/lib/libbrowser.js index 59a16c6654c08..777bd6ebaedd5 100644 --- a/src/lib/libbrowser.js +++ b/src/lib/libbrowser.js @@ -285,7 +285,7 @@ var LibraryBrowser = { #if MIN_SAFARI_VERSION < 160400 var CFS = document.exitFullscreen ?? document['webkitCancelFullScreen']; - CFS.apply(document, []); + CFS.call(document); #else document.exitFullscreen(); #endif diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js index ce6c916607a68..56bd5c54ed8a8 100644 --- a/src/lib/libdylink.js +++ b/src/lib/libdylink.js @@ -537,7 +537,7 @@ var LibraryDylink = { } #if ASSERTIONS - var tableAlign = Math.pow(2, customSection.tableAlign); + var tableAlign = 2 ** customSection.tableAlign; assert(tableAlign === 1, `invalid tableAlign ${tableAlign}`); assert(offset == end); #endif @@ -554,9 +554,7 @@ var LibraryDylink = { for (var [sym, exp] of Object.entries(exports)) { if (sym.startsWith('dynCall_')) { var sig = sym.substring(8); - if (!hasOwn(dynCalls, sig)) { - dynCalls[sig] = exp; - } + dynCalls[sig] ??= exp; } } }, @@ -719,7 +717,7 @@ var LibraryDylink = { if (firstLoad) { #endif // alignments are powers of 2 - var memAlign = Math.pow(2, metadata.memoryAlign); + var memAlign = 2 ** metadata.memoryAlign; // prepare memory var memoryBase = metadata.memorySize ? alignMemory(getMemory(metadata.memorySize + memAlign), memAlign) : 0; // TODO: add to cleanups var tableBase = metadata.tableSize ? {{{ from64Expr('wasmTable.length') }}} : 0; diff --git a/src/lib/libembind.js b/src/lib/libembind.js index 6c33e7d7e3256..5924cbd8d6fd5 100644 --- a/src/lib/libembind.js +++ b/src/lib/libembind.js @@ -60,7 +60,7 @@ var LibraryEmbind = { // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments. proto[methodName] = function(...args) { // TODO This check can be removed in -O3 level "unsafe" optimizations. - if (!hasOwn(proto[methodName].overloadTable, args.length)) { + if (!proto[methodName].overloadTable[args.length]) { throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`); } return proto[methodName].overloadTable[args.length].apply(this, args); @@ -91,7 +91,7 @@ var LibraryEmbind = { // We are exposing a function with the same name as an existing function. Create an overload table and a function selector // that routes between the two. ensureOverloadTable(Module, name, name); - if (hasOwn(Module[name].overloadTable, numArguments)) { + if (Module[name].overloadTable[numArguments]) { throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`); } // Add the new function into the overload table. @@ -144,7 +144,7 @@ var LibraryEmbind = { $registerInheritedInstance: (class_, ptr, instance) => { ptr = getBasestPointer(class_, ptr); - if (hasOwn(registeredInstances, ptr)) { + if (registeredInstances[ptr]) { throwBindingError(`Tried to register registered instance: ${ptr}`); } else { registeredInstances[ptr] = instance; @@ -153,7 +153,7 @@ var LibraryEmbind = { $unregisterInheritedInstance: (class_, ptr) => { ptr = getBasestPointer(class_, ptr); - if (hasOwn(registeredInstances, ptr)) { + if (registeredInstances[ptr]) { delete registeredInstances[ptr]; } else { throwBindingError(`Tried to unregister unregistered instance: ${ptr}`); @@ -1972,10 +1972,8 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence if (classType.registeredClass.__derivedClasses) { for (const derivedClass of classType.registeredClass.__derivedClasses) { - if (!hasOwn(derivedClass.constructor, methodName)) { - // TODO: Add support for overloads - derivedClass.constructor[methodName] = func; - } + // TODO: Add support for overloads + derivedClass.constructor[methodName] ??= func; } } diff --git a/src/lib/libembind_shared.js b/src/lib/libembind_shared.js index 464d8c7a84ad2..197c34c1728b3 100644 --- a/src/lib/libembind_shared.js +++ b/src/lib/libembind_shared.js @@ -39,7 +39,7 @@ var LibraryEmbindShared = { if (!rawType) { throwBindingError(`type "${name}" must have a positive integer typeid pointer`); } - if (hasOwn(registeredTypes, rawType)) { + if (registeredTypes[rawType]) { if (options.ignoreDuplicateRegistrations) { return; } else { @@ -50,7 +50,7 @@ var LibraryEmbindShared = { registeredTypes[rawType] = registeredInstance; delete typeDependencies[rawType]; - if (hasOwn(awaitingDependencies, rawType)) { + if (awaitingDependencies[rawType]) { var callbacks = awaitingDependencies[rawType]; delete awaitingDependencies[rawType]; callbacks.forEach((cb) => cb()); @@ -74,14 +74,11 @@ var LibraryEmbindShared = { var unregisteredTypes = []; var registered = 0; for (let [i, dt] of dependentTypes.entries()) { - if (hasOwn(registeredTypes, dt)) { + if (registeredTypes[dt]) { typeConverters[i] = registeredTypes[dt]; } else { unregisteredTypes.push(dt); - if (!hasOwn(awaitingDependencies, dt)) { - awaitingDependencies[dt] = []; - } - awaitingDependencies[dt].push(() => { + (awaitingDependencies[dt] ??= []).push(() => { typeConverters[i] = registeredTypes[dt]; ++registered; if (registered === unregisteredTypes.length) { diff --git a/src/lib/libemval.js b/src/lib/libemval.js index 7d76f8d19797d..3cbd21755a23d 100644 --- a/src/lib/libemval.js +++ b/src/lib/libemval.js @@ -307,7 +307,7 @@ ${functionBody} var rv; switch (kind) { case {{{ cDefs['internal::EM_INVOKER_KIND::FUNCTION'] }}}: - rv = Emval.toValue(handle).apply(null, argN); + rv = Emval.toValue(handle)(...argN); break; case {{{ cDefs['internal::EM_INVOKER_KIND::CONSTRUCTOR'] }}}: rv = Reflect.construct(Emval.toValue(handle), argN); diff --git a/src/lib/libglfw.js b/src/lib/libglfw.js index 995aace574318..5979a8c67eb0f 100644 --- a/src/lib/libglfw.js +++ b/src/lib/libglfw.js @@ -884,28 +884,28 @@ var LibraryGLFW = { } if (DataTransferItem.prototype.webkitGetAsEntry) { - let entriesTree = {}; + let entriesTree = new Map(); function markDone(fullpath, recursive) { - if (entriesTree[fullpath].subpaths.length != 0) return; - delete entriesTree[fullpath]; + if (entriesTree.get(fullpath).subpaths.length != 0) return; + entriesTree.delete(fullpath); let parentpath = fullpath.substring(0, fullpath.lastIndexOf('/')); - if (!hasOwn(entriesTree, parentpath)) { - if (Object.keys(entriesTree).length == 0) finalize(); + if (!entriesTree.has(parentpath)) { + if (entriesTree.size == 0) finalize(); return; } - const fpIndex = entriesTree[parentpath].subpaths.indexOf(fullpath); - if (fpIndex > -1) entriesTree[parentpath].subpaths.splice(fpIndex, 1); + const fpIndex = entriesTree.get(parentpath).subpaths.indexOf(fullpath); + if (fpIndex > -1) entriesTree.get(parentpath).subpaths.splice(fpIndex, 1); if (recursive) markDone(parentpath, true); - if (Object.keys(entriesTree).length == 0) finalize(); + if (entriesTree.size == 0) finalize(); } function processEntry(entry) { let fp = entry.fullPath; let pp = fp.substring(0, fp.lastIndexOf('/')); - entriesTree[fp] = { subpaths: [] }; + entriesTree.set(fp, { subpaths: [] }); if (entry.isFile) { entry.file((f) => { filesQ.push({ file: f, path: pp }); markDone(fp, false); }) } else if (entry.isDirectory) { - if (hasOwn(entriesTree, pp)) entriesTree[pp].subpaths.push(fp); + if (entriesTree.has(pp)) entriesTree.get(pp).subpaths.push(fp); FS.createPath('/' + drop_dir + pp, entry.name); var reader = entry.createReader(); var rRead = function (dirEntries) { diff --git a/src/lib/liblegacy.js b/src/lib/liblegacy.js index 1ca587b72c08e..f9444b8bcadd8 100644 --- a/src/lib/liblegacy.js +++ b/src/lib/liblegacy.js @@ -106,15 +106,7 @@ legacyFuncs = { #if EMBIND $getInheritedInstanceCount: () => Object.keys(registeredInstances).length, - $getLiveInheritedInstances: () => { - var rv = []; - for (var k in registeredInstances) { - if (hasOwn(registeredInstances, k)) { - rv.push(registeredInstances[k]); - } - } - return rv; - }, + $getLiveInheritedInstances: () => Object.values(registeredInstances), #endif }; diff --git a/src/lib/libsdl.js b/src/lib/libsdl.js index e5b04e02936e3..fde7a506abffc 100644 --- a/src/lib/libsdl.js +++ b/src/lib/libsdl.js @@ -1914,7 +1914,7 @@ var LibrarySDL = { var srcData = SDL.surfaces[src]; var w = srcData.width * zoom; var h = srcData.height * zoom; - var diagonal = Math.ceil(Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2))); + var diagonal = Math.ceil(Math.hypot(w, h)); var ret = SDL.makeSurface(diagonal, diagonal, srcData.flags, false, 'rotozoomSurface'); var dstData = SDL.surfaces[ret]; dstData.ctx.translate(diagonal / 2, diagonal / 2); @@ -3354,10 +3354,7 @@ var LibrarySDL = { var gamepad = SDL.getGamepad(deviceIndex); if (gamepad) { var name = gamepad.id; - if (hasOwn(SDL.joystickNamePool, name)) { - return SDL.joystickNamePool[name]; - } - return SDL.joystickNamePool[name] = stringToNewUTF8(name); + return SDL.joystickNamePool[name] ??= stringToNewUTF8(name); } return 0; }, diff --git a/src/lib/libwasmfs_fetch.js b/src/lib/libwasmfs_fetch.js index 8082df949d697..208a067e7fabb 100644 --- a/src/lib/libwasmfs_fetch.js +++ b/src/lib/libwasmfs_fetch.js @@ -22,7 +22,7 @@ addToLibrary({ var url = ''; var fileUrl_p = __wasmfs_fetch_get_file_url(file); var fileUrl = UTF8ToString(fileUrl_p); - var isAbs = fileUrl.indexOf('://') !== -1; + var isAbs = fileUrl.includes('://'); if (isAbs) { url = fileUrl; } else { diff --git a/src/lib/libwebgl.js b/src/lib/libwebgl.js index a835b85abf779..0b36fb666559a 100644 --- a/src/lib/libwebgl.js +++ b/src/lib/libwebgl.js @@ -3149,7 +3149,7 @@ for (let i = 0; i <= GL_POOL_TEMP_BUFFERS_SIZE; ++i) { #if GL_TRACK_ERRORS var bindingsType = 0x8872/*GL_MAX_TEXTURE_IMAGE_UNITS*/; #endif - if (bindingMatch[3] && bindingMatch[2].indexOf('sampler') != -1) { + if (bindingMatch[3] && bindingMatch[2].includes('sampler')) { samplerBindings[bindingMatch[3]] = [binding, arrayLength]; } else { #if GL_TRACK_ERRORS diff --git a/src/parseTools.mjs b/src/parseTools.mjs index 092091971dbc6..a1d88c1361f1b 100644 --- a/src/parseTools.mjs +++ b/src/parseTools.mjs @@ -120,7 +120,7 @@ export function preprocess(filename) { let inStyle = false; const lines = text.split('\n'); // text.split yields an extra empty element at the end if text itself ends with a newline. - if (!lines[lines.length - 1]) { + if (!lines.at(-1)) { lines.pop(); } diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index 10dbf7a265878..0e59ea3965136 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -328,7 +328,7 @@ null; #endif // WebAssembly compilation failed, try running the JS fallback instead. var search = location.search; - if (search.indexOf('_rwasm=0') < 0) { + if (!search.includes('_rwasm=0')) { location.href += (search ? search + '&' : '?') + '_rwasm=0'; } #if ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_MAY_BE_SHELL diff --git a/src/preamble.js b/src/preamble.js index 600664696be08..7f5d17868b610 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -555,7 +555,7 @@ async function instantiateArrayBuffer(binaryFile, imports) { #endif // WebAssembly compilation failed, try running the JS fallback instead. var search = location.search; - if (search.indexOf('_rwasm=0') < 0) { + if (!search.includes('_rwasm=0')) { // Reload the page with the `_rwasm=0` argument location.href += (search ? search + '&' : '?') + '_rwasm=0'; // Return a promise that never resolves. We don't want to diff --git a/src/utility.mjs b/src/utility.mjs index 2d78a41bc1f9f..9706f5077cfaf 100644 --- a/src/utility.mjs +++ b/src/utility.mjs @@ -58,7 +58,7 @@ export function popCurrentFile() { function errorPrefix(lineNo) { if (!currentFile.length) return ''; - const filename = currentFile[currentFile.length - 1]; + const filename = currentFile.at(-1); if (lineNo) { return `${filename}:${lineNo}: `; } else { @@ -313,7 +313,7 @@ class Profiler { terminate() { while (this.ids.length) { - const lastID = this.ids[this.ids.length - 1][0]; + const lastID = this.ids.at(-1)[0]; this.stop(lastID); } // const overall = performance.now() - this.startTime diff --git a/test/codesize/test_codesize_hello_dylink.json b/test/codesize/test_codesize_hello_dylink.json index 3678270780d91..69a216e9b257a 100644 --- a/test/codesize/test_codesize_hello_dylink.json +++ b/test/codesize/test_codesize_hello_dylink.json @@ -1,10 +1,10 @@ { - "a.out.js": 26182, - "a.out.js.gz": 11126, + "a.out.js": 26173, + "a.out.js.gz": 11122, "a.out.nodebug.wasm": 16986, "a.out.nodebug.wasm.gz": 8667, - "total": 43168, - "total_gz": 19793, + "total": 43159, + "total_gz": 19789, "sent": [ "__syscall_stat64", "emscripten_resize_heap", diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index db38a3cbf1000..5d6aac2668c72 100644 --- a/test/codesize/test_codesize_hello_dylink_all.json +++ b/test/codesize/test_codesize_hello_dylink_all.json @@ -1,7 +1,7 @@ { - "a.out.js": 272550, + "a.out.js": 272528, "a.out.nodebug.wasm": 575827, - "total": 848377, + "total": 848355, "sent": [ "IMG_Init", "IMG_Load", diff --git a/test/codesize/test_minimal_runtime_code_size_hello_embind.json b/test/codesize/test_minimal_runtime_code_size_hello_embind.json index f5ea26d375f34..10c19ad4c0ce8 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_embind.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_embind.json @@ -1,10 +1,10 @@ { "a.html": 540, "a.html.gz": 367, - "a.js": 7405, - "a.js.gz": 3416, + "a.js": 7404, + "a.js.gz": 3406, "a.wasm": 7201, "a.wasm.gz": 3305, - "total": 15146, - "total_gz": 7088 + "total": 15145, + "total_gz": 7078 } diff --git a/test/codesize/test_minimal_runtime_code_size_hello_embind_val.json b/test/codesize/test_minimal_runtime_code_size_hello_embind_val.json index 078c567c17a14..ff90e7b0d3b3f 100644 --- a/test/codesize/test_minimal_runtime_code_size_hello_embind_val.json +++ b/test/codesize/test_minimal_runtime_code_size_hello_embind_val.json @@ -1,10 +1,10 @@ { "a.html": 540, "a.html.gz": 367, - "a.js": 5370, - "a.js.gz": 2518, + "a.js": 5305, + "a.js.gz": 2482, "a.wasm": 5817, "a.wasm.gz": 2736, - "total": 11727, - "total_gz": 5621 + "total": 11662, + "total_gz": 5585 } diff --git a/tools/acorn-optimizer.mjs b/tools/acorn-optimizer.mjs index 61e1b15c1f1e3..5b019adb9e0f1 100755 --- a/tools/acorn-optimizer.mjs +++ b/tools/acorn-optimizer.mjs @@ -292,7 +292,7 @@ function JSDCE(ast, aggressive) { function handleFunction(node, c, defun) { // defun names matter - function names (the y in var x = function y() {..}) are just for stack traces. if (defun) { - ensureData(scopes[scopes.length - 1], node.id.name).def = 1; + ensureData(scopes.at(-1), node.id.name).def = 1; } const scope = {}; scopes.push(scope); @@ -307,12 +307,11 @@ function JSDCE(ast, aggressive) { // ourselves, for named defined (defun) functions const ownName = defun ? node.id.name : ''; const names = new Set(); - for (const name in scopes.pop()) { + for (const [name, data] of Object.entries(scopes.pop())) { if (name === ownName) continue; - const data = scope[name]; if (data.use && !data.def) { // this is used from a higher scope, propagate the use down - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; continue; } if (data.def && !data.use && !data.param) { @@ -326,7 +325,7 @@ function JSDCE(ast, aggressive) { recursiveWalk(ast, { VariableDeclarator(node, c) { walkPattern(node.id, c, (name) => { - ensureData(scopes[scopes.length - 1], name).def = 1; + ensureData(scopes.at(-1), name).def = 1; }); if (node.init) c(node.init); }, @@ -358,30 +357,30 @@ function JSDCE(ast, aggressive) { }, Identifier(node, _c) { const name = node.name; - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; }, ExportDefaultDeclaration(node, c) { const name = node.declaration.id.name; - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; c(node.declaration); }, ExportNamedDeclaration(node, c) { if (node.declaration) { if (node.declaration.type == 'FunctionDeclaration') { const name = node.declaration.id.name; - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; } else { assert(node.declaration.type == 'VariableDeclaration'); for (const decl of node.declaration.declarations) { const name = decl.id.name; - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; } } c(node.declaration); } else { for (const specifier of node.specifiers) { const name = specifier.local.name; - ensureData(scopes[scopes.length - 1], name).use = 1; + ensureData(scopes.at(-1), name).use = 1; } } }, @@ -649,9 +648,9 @@ function emitDCEGraph(ast) { const imports = []; const defuns = []; const dynCallNames = []; - const nameToGraphName = {}; - const modulePropertyToGraphName = {}; - const exportNameToGraphName = {}; // identical to wasmExports['..'] nameToGraphName + const nameToGraphName = new Map(); + const modulePropertyToGraphName = new Map(); + const exportNameToGraphName = new Map(); // identical to wasmExports['..'] nameToGraphName let foundWasmImportsAssign = false; let foundMinimalRuntimeExports = false; // Under WASM_ESM_INTEGRATION, JS names bound to wasm exports via an ES import @@ -663,9 +662,9 @@ function emitDCEGraph(ast) { // the asmName is what the wasm provides directly; the outside JS // name may be slightly different (extra "_" in wasm backend) const graphName = getGraphName(name, 'export'); - nameToGraphName[name] = graphName; - modulePropertyToGraphName[name] = graphName; - exportNameToGraphName[asmName] = graphName; + nameToGraphName.set(name, graphName); + modulePropertyToGraphName.set(name, graphName); + exportNameToGraphName.set(asmName, graphName); if (/^dynCall_/.test(name)) { dynCallNames.push(graphName); } @@ -707,11 +706,11 @@ function emitDCEGraph(ast) { node.specifiers.forEach((spec) => { const jsName = spec.local.name; // JS-side name const asmName = spec.imported.name; // wasm-provided name - if (Object.hasOwn(exportNameToGraphName, asmName)) { + if (exportNameToGraphName.has(asmName)) { // Another local already binds this wasm export (e.g. both `memory` // and `memory as wasmMemory`): point this local at the same node so // a use of either roots the one underlying export. - nameToGraphName[jsName] = exportNameToGraphName[asmName]; + nameToGraphName.set(jsName, exportNameToGraphName.get(asmName)); } else { saveAsmExport(jsName, asmName); } @@ -835,7 +834,7 @@ function emitDCEGraph(ast) { } } else if (!specialScopes) { defuns.push(node); - nameToGraphName[name] = getGraphName(name, 'defun'); + nameToGraphName.set(name, getGraphName(name, 'defun')); emptyOut(node); // ignore this in the second pass; we scan defuns separately } } else if (node.type === 'ArrowFunctionExpression') { @@ -880,12 +879,11 @@ function emitDCEGraph(ast) { import: ['env', nativeName], reaches: new Set(), }); - if (Object.hasOwn(nameToGraphName, jsName)) { - info.reaches.add(nameToGraphName[jsName]); + if (nameToGraphName.has(jsName)) { + info.reaches.add(nameToGraphName.get(jsName)); } // otherwise, it's a number, ignore } - for (const [e, _] of Object.entries(exportNameToGraphName)) { - const name = exportNameToGraphName[e]; + for (const [e, name] of exportNameToGraphName) { infos[name] = { name: name, export: e, @@ -899,15 +897,9 @@ function emitDCEGraph(ast) { // from the top scope, which might create more uses than needed let reached; if (node.type === 'Identifier') { - const name = node.name; - if (Object.hasOwn(nameToGraphName, name)) { - reached = nameToGraphName[name]; - } + reached = nameToGraphName.get(node.name); } else if (isModuleUse(node)) { - const name = getExportOrModuleUseName(node); - if (Object.hasOwn(modulePropertyToGraphName, name)) { - reached = modulePropertyToGraphName[name]; - } + reached = modulePropertyToGraphName.get(getExportOrModuleUseName(node)); } else if (isStaticDynCall(node)) { reached = getGraphName(getStaticDynCallName(node), 'export'); } else if (isDynamicDynCall(node)) { @@ -916,8 +908,8 @@ function emitDCEGraph(ast) { } else if (isExportUse(node)) { // any remaining asm uses are always rooted in any case const name = getExportOrModuleUseName(node); - if (Object.hasOwn(exportNameToGraphName, name)) { - infos[exportNameToGraphName[name]].root = true; + if (exportNameToGraphName.has(name)) { + infos[exportNameToGraphName.get(name)].root = true; } return; } diff --git a/tools/js_manipulation.py b/tools/js_manipulation.py index 6dddc53da8b10..6d95932ca2368 100644 --- a/tools/js_manipulation.py +++ b/tools/js_manipulation.py @@ -50,7 +50,7 @@ def add_files_pre_js(pre_js_list, files_pre_js): utils.write_file(post, ''' if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?'; necessaryPreJSTasks.forEach((task) => { - if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; + if (!Module['preRun'].includes(task)) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; }); ''') diff --git a/tools/unsafe_optimizations.mjs b/tools/unsafe_optimizations.mjs index a3b392d0aef52..9c3f1a31d912f 100755 --- a/tools/unsafe_optimizations.mjs +++ b/tools/unsafe_optimizations.mjs @@ -217,8 +217,8 @@ function runOnJsText(js, pretty = false) { let progress = true; while (progress) { progress = optPassMergeVarDeclarations(ast); - progress = progress || optPassMergeVarInitializationAssignments(ast); - progress = progress || optPassMergeEmptyVarDeclarators(ast); + progress ||= optPassMergeVarInitializationAssignments(ast); + progress ||= optPassMergeEmptyVarDeclarators(ast); } optPassSimplifyModularizeFunction(ast);