Fix `this` value for `Core.enableLegacyInheritance()` (#4380)
authorMatthias Schmidt <gravatronics@live.com>
Wed, 7 Jul 2021 10:48:12 +0000 (12:48 +0200)
committerGitHub <noreply@github.com>
Wed, 7 Jul 2021 10:48:12 +0000 (12:48 +0200)
Without explicitly binding `this` to `thisValue`, it will be `constructed` resulting in missing data only present in `thisValue`.

ts/WoltLabSuite/Core/Core.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Core.js

index d5635967c3623f758019032ef0ded8edf3f64c37..9d5c0f5b2654ebbf5530b3efbabb94d6d5134e7f 100644 (file)
@@ -270,11 +270,26 @@ export function debounce<F extends DebounceCallback>(
   };
 }
 
+const defaultFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf({}));
+
 export function enableLegacyInheritance<T>(legacyClass: T): void {
   (legacyClass as any).call = function (thisValue, ...args) {
+    if (window.ENABLE_DEVELOPER_TOOLS) {
+      console.log("Relying on legacy inheritance for ", legacyClass, thisValue);
+    }
+
     const constructed = Reflect.construct(legacyClass as any, args, thisValue.constructor);
     Object.entries(constructed).forEach(([key, value]) => {
       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);
+        }
+      });
+    }
   };
 }
index 5256fff96bc298ae6ce851262dc1d04a3c01813f..64932b0c8f78a05f7191c922b2829bbb6df1ff14 100644 (file)
@@ -238,12 +238,24 @@ 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) {
+                console.log("Relying on legacy inheritance for ", legacyClass, thisValue);
+            }
             const constructed = Reflect.construct(legacyClass, args, thisValue.constructor);
             Object.entries(constructed).forEach(([key, value]) => {
                 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);
+                    }
+                });
+            }
         };
     }
     exports.enableLegacyInheritance = enableLegacyInheritance;