luci-base: luci.js: properly handle nested Class.super() calls
authorJo-Philipp Wich <jo@mein.io>
Tue, 26 Nov 2019 08:37:27 +0000 (09:37 +0100)
committerJo-Philipp Wich <jo@mein.io>
Tue, 26 Nov 2019 08:42:28 +0000 (09:42 +0100)
Introduce a unique __id__ attribute per class and use that, together with
the symbol name being looked up to, to form a unique key which is used to
store the prototype scope pointer.

This fixes cases where a call to super() invokes a procedure which is
calling super() as well on a different class or symbol.

Fixes: #3316
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(cherry picked from commit 374c23cdab53c5681acafb561977d927c361408a)

modules/luci-base/htdocs/luci-static/resources/luci.js

index 7c1831376f35d67fb0e3a617a0abe9044693312d..738a5b4cb4f6cb97dcbc320e41e74410d3e96cf0 100644 (file)
@@ -67,7 +67,7 @@
         * It provides simple means to create subclasses of given classes and
         * implements prototypal inheritance.
         */
-       var superContext = null, Class = Object.assign(function() {}, {
+       var superContext = {}, classIndex = 0, Class = Object.assign(function() {}, {
                /**
                 * Extends this base class with the properties described in
                 * `properties` and returns a new subclassed Class instance
@@ -86,8 +86,9 @@
                 */
                extend: function(properties) {
                        var props = {
+                               __id__: { value: classIndex },
                                __base__: { value: this.prototype },
-                               __name__: { value: properties.__name__ || 'anonymous' }
+                               __name__: { value: properties.__name__ || 'anonymous' + classIndex++ }
                        };
 
                        var ClassConstructor = function() {
                         * superclass method returned `null`.
                         */
                        super: function(key, callArgs) {
-                               for (superContext = Object.getPrototypeOf(superContext ||
+                               if (key == null)
+                                       return null;
+
+                               var slotIdx = this.__id__ + '.' + key;
+
+                               for (superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx] ||
                                                                          Object.getPrototypeOf(this));
-                                    superContext && !superContext.hasOwnProperty(key);
-                                    superContext = Object.getPrototypeOf(superContext)) { }
+                                    superContext[slotIdx] && !superContext[slotIdx].hasOwnProperty(key);
+                                    superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx])) {}
 
-                               if (!superContext)
+                               if (!superContext[slotIdx]) {
+                                       delete superContext[slotIdx];
                                        return null;
+                               }
 
-                               var res = superContext[key];
+                               var res = superContext[slotIdx][key];
 
                                if (arguments.length > 1) {
-                                       if (typeof(res) != 'function')
+                                       if (typeof(res) != 'function') {
+                                               delete superContext[slotIdx];
                                                throw new ReferenceError(key + ' is not a function in base class');
+                                       }
 
                                        if (typeof(callArgs) != 'object')
                                                callArgs = this.varargs(arguments, 1);
                                        res = res.apply(this, callArgs);
                                }
 
-                               superContext = null;
-
+                               delete superContext[slotIdx];
                                return res;
                        },