From c5ea789b869f6a637bf871c74930685a52cde6d0 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Mon, 2 Aug 2021 19:06:53 +0200 Subject: [PATCH] Resolved side effects from previous inheritance approach This is a follow-up for db23f8af33398c4851d6ba36436592f406b35a0d which introduced a flawed change. The iteration over the prototype chain caused the prototype itself being bound to an object on runtime, conflicting with other objects. The root cause was that some parts of the inherited functions were still bound to `constructed`, which was attempted to be fixed by poking the prototype chain. This new fix is a bit weird, unless one understands that the call to `Reflect.construct()` is a bit tricky because any bound call inside the constructor of `legacyClass` will be bound to `constructed`. This change is more of a sledge hammer approach, but it works all cases that I tested, including those that were initially the cause for the previous fix as well as new issues caused by the fix. --- ts/WoltLabSuite/Core/Core.ts | 17 ++++++++--------- .../install/files/js/WoltLabSuite/Core/Core.js | 15 +++++++-------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/ts/WoltLabSuite/Core/Core.ts b/ts/WoltLabSuite/Core/Core.ts index 9d5c0f5b26..d1a0cedaa4 100644 --- a/ts/WoltLabSuite/Core/Core.ts +++ b/ts/WoltLabSuite/Core/Core.ts @@ -270,8 +270,6 @@ export function debounce( }; } -const defaultFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf({})); - export function enableLegacyInheritance(legacyClass: T): void { (legacyClass as any).call = function (thisValue, ...args) { if (window.ENABLE_DEVELOPER_TOOLS) { @@ -280,16 +278,17 @@ export function enableLegacyInheritance(legacyClass: T): void { const constructed = Reflect.construct(legacyClass as any, args, thisValue.constructor); Object.entries(constructed).forEach(([key, value]) => { + if (typeof value === "function") { + value = value.bind(thisValue); + } + thisValue[key] = value; }); - let object = thisValue; - while ((object = Object.getPrototypeOf(object))) { - Object.getOwnPropertyNames(object).forEach((name) => { - if (typeof object[name] === "function" && !defaultFunctions.includes(name)) { - object[name] = object[name].bind(thisValue); - } - }); + for (const key in thisValue) { + if (typeof thisValue[key] === "function") { + constructed[key] = thisValue[key].bind(thisValue); + } } }; } diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Core.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Core.js index 64932b0c8f..262a4db796 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Core.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Core.js @@ -238,7 +238,6 @@ define(["require", "exports"], function (require, exports) { }; } exports.debounce = debounce; - const defaultFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf({})); function enableLegacyInheritance(legacyClass) { legacyClass.call = function (thisValue, ...args) { if (window.ENABLE_DEVELOPER_TOOLS) { @@ -246,15 +245,15 @@ define(["require", "exports"], function (require, exports) { } const constructed = Reflect.construct(legacyClass, args, thisValue.constructor); Object.entries(constructed).forEach(([key, value]) => { + if (typeof value === "function") { + value = value.bind(thisValue); + } thisValue[key] = value; }); - let object = thisValue; - while ((object = Object.getPrototypeOf(object))) { - Object.getOwnPropertyNames(object).forEach((name) => { - if (typeof object[name] === "function" && !defaultFunctions.includes(name)) { - object[name] = object[name].bind(thisValue); - } - }); + for (const key in thisValue) { + if (typeof thisValue[key] === "function") { + constructed[key] = thisValue[key].bind(thisValue); + } } }; } -- 2.20.1