From a61eac425ca445decb1f4ea3e760c68c424b06d4 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Sun, 1 Nov 2026 12:02:29 -0800 Subject: [PATCH] [JS] Prefer `Object.hasOwn` over `hasOwnProperty`. NFC In compile-time JS code (`src/`, `tools/`, and compile-time helpers in `libwebgl.js`), use `Object.hasOwn` directly instead of `hasOwnProperty`. In runtime JS library code, introduce an internal `$hasOwn` helper in `libcore.js` that aliases `Object.hasOwn` when targeting modern browser versions (`MIN_FIREFOX_VERSION >= 92`, `MIN_CHROME_VERSION >= 93`, and `MIN_SAFARI_VERSION >= 150400`) and falls back to `Object.prototype.hasOwnProperty.call` otherwise. Note that this change comes with minor codesize change, in some cases an improvement and in some a regression. Note that if we were to bump our default min browser versions we would always see an improvement (perhaps we should always target modern browser in our codesize tests?) See #28125 --- eslint.config.mjs | 2 +- src/jsifier.mjs | 4 ++-- src/lib/libc_preprocessor.js | 7 +------ src/lib/libcore.js | 17 +++++++++++++++-- src/lib/libdylink.js | 2 +- src/lib/libembind.js | 14 +++++++------- src/lib/libembind_shared.js | 8 ++++---- src/lib/libglfw.js | 4 ++-- src/lib/liblegacy.js | 2 +- src/lib/libsdl.js | 4 ++-- src/lib/libwebgl.js | 6 +++--- src/modules.mjs | 2 +- src/utility.mjs | 6 +++--- .../test_codesize_hello_dylink_all.json | 4 ++-- ..._minimal_runtime_code_size_hello_embind.json | 8 ++++---- ...imal_runtime_code_size_hello_embind_val.json | 8 ++++---- test/embind/embind.test.js | 12 ++++++------ tools/acorn-optimizer.mjs | 14 +++++++------- tools/webidl_binder.py | 4 ++-- 19 files changed, 68 insertions(+), 60 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 424c1cce5e62d..9501ae1b5faac 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -78,7 +78,7 @@ export default [{ 'require-jsdoc': 'off', 'arrow-body-style': ['error', 'as-needed'], 'space-infix-ops': 'error', - 'no-prototype-builtins': 'off', + 'prefer-object-has-own': 'error', quotes: ['error', 'single', { avoidEscape: true, diff --git a/src/jsifier.mjs b/src/jsifier.mjs index 43f8c51a5be4a..d3cb2fe244c8e 100644 --- a/src/jsifier.mjs +++ b/src/jsifier.mjs @@ -802,7 +802,7 @@ function(${args}) { const postset = LibraryManager.library[postsetId]; if (symbolsOnly) { - if (LibraryManager.library.hasOwnProperty(symbol)) { + if (Object.hasOwn(LibraryManager.library, symbol)) { let snippet = LibraryManager.library[symbol]; if (!LibraryManager.isAlias(snippet)) { if (typeof snippet === 'object') { @@ -831,7 +831,7 @@ function(${args}) { const mangled = mangleCSymbolName(symbol); let snippet = LibraryManager.library[symbol]; - if (!LibraryManager.library.hasOwnProperty(symbol)) { + if (!Object.hasOwn(LibraryManager.library, symbol)) { const isWeakImport = WEAK_IMPORTS.has(symbol); if (!isDefined(symbol) && !isWeakImport) { if (PROXY_TO_PTHREAD && !MAIN_MODULE && symbol == '__main_argc_argv') { diff --git a/src/lib/libc_preprocessor.js b/src/lib/libc_preprocessor.js index f0bcc7900e58b..16be00dca6b2c 100644 --- a/src/lib/libc_preprocessor.js +++ b/src/lib/libc_preprocessor.js @@ -127,12 +127,7 @@ addToLibrary({ var kind2 = classifyChar(str, j); if (kind2 != 2/*0-9*/ && kind2 != 3/*a-z*/) { var symbol = str.substring(i, j); -#if MIN_FIREFOX_VERSION < 92 - // Firefox only introduced Object.hasOwn() in Firefox 92. - if (defs.hasOwnProperty(symbol)) { -#else - if (Object.hasOwn(defs, symbol)) { -#endif + if (hasOwn(defs, symbol)) { var pp = defs[symbol], expanded; if (typeof pp === 'function') { // definition is a function? if (pp.length) { // Expanding a macro? (#define FOO(X) ...) diff --git a/src/lib/libcore.js b/src/lib/libcore.js index 740825141d21d..e1594e1e57010 100644 --- a/src/lib/libcore.js +++ b/src/lib/libcore.js @@ -1629,11 +1629,24 @@ addToLibrary({ return readEmAsmArgsArray; }, + $hasOwn__internal: true, +#if MIN_FIREFOX_VERSION < 92 || MIN_CHROME_VERSION < 93 || MIN_SAFARI_VERSION < 150400 + // https://caniuse.com/mdn-javascript_builtins_object_hasown + $hasOwn__docs: '/** @noinline */', + $hasOwn: (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop), +#else + // Closure's built-in extern for `Object.hasOwn` is typed as + // `function(!Object, (string|symbol)): boolean`, which rejects nullable + // `Object` types (such as `Module`) and numeric property keys. + $hasOwn__docs: '/** @type {function(?, ?): boolean} */', + $hasOwn: 'Object.hasOwn', +#endif + #if HAVE_EM_ASM $runEmAsmFunction: (code, sigPtr, argbuf) => { var args = readEmAsmArgs(sigPtr, argbuf); #if ASSERTIONS - assert(ASM_CONSTS.hasOwnProperty(code), `No EM_ASM constant found at address ${code}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); + assert(hasOwn(ASM_CONSTS, code), `No EM_ASM constant found at address ${code}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); #endif return ASM_CONSTS[code](...args); }, @@ -1666,7 +1679,7 @@ addToLibrary({ } #endif #if ASSERTIONS - assert(ASM_CONSTS.hasOwnProperty(emAsmAddr), `No EM_ASM constant found at address ${emAsmAddr}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); + assert(hasOwn(ASM_CONSTS, emAsmAddr), `No EM_ASM constant found at address ${emAsmAddr}. The loaded WebAssembly file is likely out of sync with the generated JavaScript.`); #endif return ASM_CONSTS[emAsmAddr](...args); }, diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js index 7437339180719..3d1c63d0362cd 100644 --- a/src/lib/libdylink.js +++ b/src/lib/libdylink.js @@ -554,7 +554,7 @@ var LibraryDylink = { for (var [sym, exp] of Object.entries(exports)) { if (sym.startsWith('dynCall_')) { var sig = sym.substring(8); - if (!dynCalls.hasOwnProperty(sig)) { + if (!hasOwn(dynCalls, sig)) { dynCalls[sig] = exp; } } diff --git a/src/lib/libembind.js b/src/lib/libembind.js index 8cef630f84dff..4030c6320d37d 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 (!proto[methodName].overloadTable.hasOwnProperty(args.length)) { + if (!hasOwn(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); @@ -83,7 +83,7 @@ var LibraryEmbind = { actually registered, since it carries a slight performance penalty. */ $exposePublicSymbol__docs: '/** @param {number=} numArguments */', $exposePublicSymbol: (name, value, numArguments) => { - if (Module.hasOwnProperty(name)) { + if (hasOwn(Module, name)) { if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) { throwBindingError(`Cannot register public name '${name}' twice`); } @@ -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 (Module[name].overloadTable.hasOwnProperty(numArguments)) { + if (hasOwn(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. @@ -104,7 +104,7 @@ var LibraryEmbind = { $replacePublicSymbol__docs: '/** @param {number=} numArguments */', $replacePublicSymbol: (name, value, numArguments) => { - if (!Module.hasOwnProperty(name)) { + if (!hasOwn(Module, name)) { throwInternalError('Replacing nonexistent public symbol'); } // If there's an overload table for this symbol, replace the symbol in the overload table instead. @@ -144,7 +144,7 @@ var LibraryEmbind = { $registerInheritedInstance: (class_, ptr, instance) => { ptr = getBasestPointer(class_, ptr); - if (registeredInstances.hasOwnProperty(ptr)) { + if (hasOwn(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 (registeredInstances.hasOwnProperty(ptr)) { + if (hasOwn(registeredInstances, ptr)) { delete registeredInstances[ptr]; } else { throwBindingError(`Tried to unregister unregistered instance: ${ptr}`); @@ -1972,7 +1972,7 @@ Originally allocated`); // `.stack` will add "at ..." after this sentence if (classType.registeredClass.__derivedClasses) { for (const derivedClass of classType.registeredClass.__derivedClasses) { - if (!derivedClass.constructor.hasOwnProperty(methodName)) { + if (!hasOwn(derivedClass.constructor, methodName)) { // TODO: Add support for overloads derivedClass.constructor[methodName] = func; } diff --git a/src/lib/libembind_shared.js b/src/lib/libembind_shared.js index e3ddca12b22c7..464d8c7a84ad2 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 (registeredTypes.hasOwnProperty(rawType)) { + if (hasOwn(registeredTypes, rawType)) { if (options.ignoreDuplicateRegistrations) { return; } else { @@ -50,7 +50,7 @@ var LibraryEmbindShared = { registeredTypes[rawType] = registeredInstance; delete typeDependencies[rawType]; - if (awaitingDependencies.hasOwnProperty(rawType)) { + if (hasOwn(awaitingDependencies, rawType)) { var callbacks = awaitingDependencies[rawType]; delete awaitingDependencies[rawType]; callbacks.forEach((cb) => cb()); @@ -74,11 +74,11 @@ var LibraryEmbindShared = { var unregisteredTypes = []; var registered = 0; for (let [i, dt] of dependentTypes.entries()) { - if (registeredTypes.hasOwnProperty(dt)) { + if (hasOwn(registeredTypes, dt)) { typeConverters[i] = registeredTypes[dt]; } else { unregisteredTypes.push(dt); - if (!awaitingDependencies.hasOwnProperty(dt)) { + if (!hasOwn(awaitingDependencies, dt)) { awaitingDependencies[dt] = []; } awaitingDependencies[dt].push(() => { diff --git a/src/lib/libglfw.js b/src/lib/libglfw.js index 6917de6830a88..995aace574318 100644 --- a/src/lib/libglfw.js +++ b/src/lib/libglfw.js @@ -889,7 +889,7 @@ var LibraryGLFW = { if (entriesTree[fullpath].subpaths.length != 0) return; delete entriesTree[fullpath]; let parentpath = fullpath.substring(0, fullpath.lastIndexOf('/')); - if (!entriesTree.hasOwnProperty(parentpath)) { + if (!hasOwn(entriesTree, parentpath)) { if (Object.keys(entriesTree).length == 0) finalize(); return; } @@ -905,7 +905,7 @@ var LibraryGLFW = { if (entry.isFile) { entry.file((f) => { filesQ.push({ file: f, path: pp }); markDone(fp, false); }) } else if (entry.isDirectory) { - if (entriesTree.hasOwnProperty(pp)) entriesTree[pp].subpaths.push(fp); + if (hasOwn(entriesTree, pp)) entriesTree[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 dc4f6e3dfdcca..1ca587b72c08e 100644 --- a/src/lib/liblegacy.js +++ b/src/lib/liblegacy.js @@ -109,7 +109,7 @@ legacyFuncs = { $getLiveInheritedInstances: () => { var rv = []; for (var k in registeredInstances) { - if (registeredInstances.hasOwnProperty(k)) { + if (hasOwn(registeredInstances, k)) { rv.push(registeredInstances[k]); } } diff --git a/src/lib/libsdl.js b/src/lib/libsdl.js index c70aa1ba0107f..e5b04e02936e3 100644 --- a/src/lib/libsdl.js +++ b/src/lib/libsdl.js @@ -3354,7 +3354,7 @@ var LibrarySDL = { var gamepad = SDL.getGamepad(deviceIndex); if (gamepad) { var name = gamepad.id; - if (SDL.joystickNamePool.hasOwnProperty(name)) { + if (hasOwn(SDL.joystickNamePool, name)) { return SDL.joystickNamePool[name]; } return SDL.joystickNamePool[name] = stringToNewUTF8(name); @@ -3375,7 +3375,7 @@ var LibrarySDL = { }, SDL_JoystickOpened__proxy: 'sync', - SDL_JoystickOpened: (deviceIndex) => SDL.lastJoystickState.hasOwnProperty(deviceIndex+1) ? 1 : 0, + SDL_JoystickOpened: (deviceIndex) => hasOwn(SDL.lastJoystickState, deviceIndex+1) ? 1 : 0, // joystick pointers are simply the deviceIndex+1. SDL_JoystickIndex: (joystick) => joystick - 1, diff --git a/src/lib/libwebgl.js b/src/lib/libwebgl.js index c93b7b685d210..01b1961ed1fc7 100644 --- a/src/lib/libwebgl.js +++ b/src/lib/libwebgl.js @@ -4200,7 +4200,7 @@ function renameSymbol(lib, oldName, newName) { delete lib[oldName]; for (const suffix of decoratorSuffixes) { const oldDecorator = oldName + suffix; - if (lib.hasOwnProperty(oldDecorator)) { + if (Object.hasOwn(lib, oldDecorator)) { const newDecorator = newName + suffix; lib[newDecorator] = lib[oldDecorator]; delete lib[oldDecorator]; @@ -4236,8 +4236,8 @@ function recordGLProcAddressGet(lib) { // are looked up in LibraryManager.library as well. for (const [sym, target] of Object.entries(lib)) { if (sym.startsWith('emscripten_gl') && typeof target === 'string' && - (lib.hasOwnProperty('emscripten_' + target) || - LibraryManager.library.hasOwnProperty('emscripten_' + target))) { + (Object.hasOwn(lib, 'emscripten_' + target) || + Object.hasOwn(LibraryManager.library, 'emscripten_' + target))) { lib[sym] = 'emscripten_' + target; } } diff --git a/src/modules.mjs b/src/modules.mjs index a0ab61dbc9930..dc014c8f70a19 100644 --- a/src/modules.mjs +++ b/src/modules.mjs @@ -293,7 +293,7 @@ export const LibraryManager = { }, isAlias(entry) { - return (typeof entry === 'string' && entry[0] != '=' && (this.library.hasOwnProperty(entry) || WASM_EXPORTS.has(entry))); + return (typeof entry === 'string' && entry[0] != '=' && (Object.hasOwn(this.library, entry) || WASM_EXPORTS.has(entry))); }, /** diff --git a/src/utility.mjs b/src/utility.mjs index f3670f7065333..dc34ddbfc74e6 100644 --- a/src/utility.mjs +++ b/src/utility.mjs @@ -103,7 +103,7 @@ export function mergeInto(obj, other, options = null) { // check for unintended symbol redefinition if (options.noOverride) { for (const key of Object.keys(other)) { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj, key)) { error(`Symbol re-definition in JavaScript library: ${key}. Do not use noOverride if this is intended`); return; } @@ -113,7 +113,7 @@ export function mergeInto(obj, other, options = null) { // check if sig is missing for added functions if (options.checkSig) { for (const [key, value] of Object.entries(other)) { - if (typeof value === 'function' && !other.hasOwnProperty(key + '__sig')) { + if (typeof value === 'function' && !Object.hasOwn(other, key + '__sig')) { error(`__sig is missing for function: ${key}. Do not use checkSig if this is intended`); return; } @@ -136,7 +136,7 @@ export function mergeInto(obj, other, options = null) { for (const key of Object.keys(other)) { if (isDecorator(key)) { if (key.endsWith('__sig')) { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj, key)) { const oldsig = obj[key]; const newsig = other[key]; if (oldsig == newsig) { diff --git a/test/codesize/test_codesize_hello_dylink_all.json b/test/codesize/test_codesize_hello_dylink_all.json index 8f2744dd18d0b..5a5ce128409eb 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": 272893, + "a.out.js": 272921, "a.out.nodebug.wasm": 589512, - "total": 862405, + "total": 862433, "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 76a868b37cd48..f5ea26d375f34 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": 7455, - "a.js.gz": 3399, + "a.js": 7405, + "a.js.gz": 3416, "a.wasm": 7201, "a.wasm.gz": 3305, - "total": 15196, - "total_gz": 7071 + "total": 15146, + "total_gz": 7088 } 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 169bcaa71351b..078c567c17a14 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": 5343, - "a.js.gz": 2500, + "a.js": 5370, + "a.js.gz": 2518, "a.wasm": 5817, "a.wasm.gz": 2736, - "total": 11700, - "total_gz": 5603 + "total": 11727, + "total_gz": 5621 } diff --git a/test/embind/embind.test.js b/test/embind/embind.test.js index 41ca7398b1687..70f7abc0bc54e 100644 --- a/test/embind/embind.test.js +++ b/test/embind/embind.test.js @@ -988,15 +988,15 @@ module({ var dummy_overloads = cm.MultipleOverloadsDependingOnDummy.prototype.dummy; // check if the overloadTable is correctly named // it can be minimized if using closure compiler - if (dummy_overloads.hasOwnProperty('overloadTable')) { - assert.false(dummy_overloads.overloadTable.hasOwnProperty('undefined')); + if (Object.hasOwn(dummy_overloads, 'overloadTable')) { + assert.false(Object.hasOwn(dummy_overloads.overloadTable, 'undefined')); } var dummy_static_overloads = cm.MultipleOverloadsDependingOnDummy.staticDummy; // check if the overloadTable is correctly named // it can be minimized if using closure compiler - if (dummy_static_overloads.hasOwnProperty('overloadTable')) { - assert.false(dummy_static_overloads.overloadTable.hasOwnProperty('undefined')); + if (Object.hasOwn(dummy_static_overloads, 'overloadTable')) { + assert.false(Object.hasOwn(dummy_static_overloads.overloadTable, 'undefined')); } // this part should fail anyway if there is no overloadTable @@ -1016,8 +1016,8 @@ module({ var dummy_free_func = cm.getDummy; console.log(dummy_free_func); - if (dummy_free_func.hasOwnProperty('overloadTable')) { - assert.false(dummy_free_func.overloadTable.hasOwnProperty('undefined')); + if (Object.hasOwn(dummy_free_func, 'overloadTable')) { + assert.false(Object.hasOwn(dummy_free_func.overloadTable, 'undefined')); } var dummy = cm.getDummy(); diff --git a/tools/acorn-optimizer.mjs b/tools/acorn-optimizer.mjs index b084a54adb378..61e1b15c1f1e3 100755 --- a/tools/acorn-optimizer.mjs +++ b/tools/acorn-optimizer.mjs @@ -220,7 +220,7 @@ function JSDCE(ast, aggressive) { let removed = 0; const scopes = [{}]; // begin with empty toplevel scope function ensureData(scope, name) { - if (Object.prototype.hasOwnProperty.call(scope, name)) return scope[name]; + if (Object.hasOwn(scope, name)) return scope[name]; scope[name] = { def: 0, use: 0, @@ -707,7 +707,7 @@ function emitDCEGraph(ast) { node.specifiers.forEach((spec) => { const jsName = spec.local.name; // JS-side name const asmName = spec.imported.name; // wasm-provided name - if (exportNameToGraphName.hasOwnProperty(asmName)) { + if (Object.hasOwn(exportNameToGraphName, 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. @@ -880,7 +880,7 @@ function emitDCEGraph(ast) { import: ['env', nativeName], reaches: new Set(), }); - if (nameToGraphName.hasOwnProperty(jsName)) { + if (Object.hasOwn(nameToGraphName, jsName)) { info.reaches.add(nameToGraphName[jsName]); } // otherwise, it's a number, ignore } @@ -900,12 +900,12 @@ function emitDCEGraph(ast) { let reached; if (node.type === 'Identifier') { const name = node.name; - if (nameToGraphName.hasOwnProperty(name)) { + if (Object.hasOwn(nameToGraphName, name)) { reached = nameToGraphName[name]; } } else if (isModuleUse(node)) { const name = getExportOrModuleUseName(node); - if (modulePropertyToGraphName.hasOwnProperty(name)) { + if (Object.hasOwn(modulePropertyToGraphName, name)) { reached = modulePropertyToGraphName[name]; } } else if (isStaticDynCall(node)) { @@ -916,7 +916,7 @@ function emitDCEGraph(ast) { } else if (isExportUse(node)) { // any remaining asm uses are always rooted in any case const name = getExportOrModuleUseName(node); - if (exportNameToGraphName.hasOwnProperty(name)) { + if (Object.hasOwn(exportNameToGraphName, name)) { infos[exportNameToGraphName[name]].root = true; } return; @@ -1602,7 +1602,7 @@ function minifyLocals(ast) { // Finally, the function name, after restoring it. fun.id = funId; - assert(extraInfo.globals.hasOwnProperty(fun.id.name)); + assert(Object.hasOwn(extraInfo.globals, fun.id.name)); fun.id.name = extraInfo.globals[fun.id.name]; } } diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py index 00774cf9fe16f..8f29cebe76fdf 100644 --- a/tools/webidl_binder.py +++ b/tools/webidl_binder.py @@ -96,7 +96,7 @@ def getExtendedAttribute(self, _name): #include #include -EM_JS_DEPS(webidl_binder, "$intArrayFromString,$UTF8ToString,$alignMemory,$addOnInit"); +EM_JS_DEPS(webidl_binder, "$intArrayFromString,$UTF8ToString,$alignMemory,$addOnInit,$hasOwn"); '''] mid_c = [''' @@ -669,7 +669,7 @@ def make_call_args(i): js_impl_methods.append(r''' %s %s(%s) %s { %s (%s) %s({ var self = Module['getCache'](Module['%s'])[$0]; - if (!self.hasOwnProperty('%s')) throw 'a JSImplementation must implement all functions, you forgot %s::%s.'; + if (!hasOwn(self, '%s')) throw 'a JSImplementation must implement all functions, you forgot %s::%s.'; %sself['%s'](%s)%s; }, (ptrdiff_t)this%s); }''' % (c_return_type, func_name, dec_args, maybe_const,