luci-base: luci.js: further nested Class.super() call fixes 3364/head
authorJo-Philipp Wich <jo@mein.io>
Tue, 26 Nov 2019 10:35:23 +0000 (11:35 +0100)
committerJo-Philipp Wich <jo@mein.io>
Tue, 26 Nov 2019 10:39:50 +0000 (11:39 +0100)
Use stacks keyed by class id + symbol internally to not clobber the
prototype scope pointer when repeatedly calling super() in invoked
methods.

Ref: https://github.com/openwrt/luci/issues/3316#issuecomment-558531111
Fixes: 374c23cda ("luci-base: luci.js: properly handle nested Class.super() calls")
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(cherry picked from commit 4a08fdd2d374083366787c4124e6b4de48bf7b77)

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

index 738a5b4cb4f6cb97dcbc320e41e74410d3e96cf0..808ab0c5cb2a3e25020539125fde4ba190890547 100644 (file)
                                if (key == null)
                                        return null;
 
-                               var slotIdx = this.__id__ + '.' + key;
+                               var slotIdx = this.__id__ + '.' + key,
+                                   symStack = superContext[slotIdx],
+                                   protoCtx = null;
 
-                               for (superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx] ||
-                                                                         Object.getPrototypeOf(this));
-                                    superContext[slotIdx] && !superContext[slotIdx].hasOwnProperty(key);
-                                    superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx])) {}
+                               for (protoCtx = Object.getPrototypeOf(symStack ? symStack[0] : Object.getPrototypeOf(this));
+                                    protoCtx != null && !protoCtx.hasOwnProperty(key);
+                                    protoCtx = Object.getPrototypeOf(protoCtx)) {}
 
-                               if (!superContext[slotIdx]) {
-                                       delete superContext[slotIdx];
+                               if (protoCtx == null)
                                        return null;
-                               }
 
-                               var res = superContext[slotIdx][key];
+                               var res = protoCtx[key];
 
                                if (arguments.length > 1) {
-                                       if (typeof(res) != 'function') {
-                                               delete superContext[slotIdx];
+                                       if (typeof(res) != 'function')
                                                throw new ReferenceError(key + ' is not a function in base class');
-                                       }
 
                                        if (typeof(callArgs) != 'object')
                                                callArgs = this.varargs(arguments, 1);
 
+                                       if (symStack)
+                                               symStack.unshift(protoCtx);
+                                       else
+                                               superContext[slotIdx] = [ protoCtx ];
+
                                        res = res.apply(this, callArgs);
+
+                                       if (symStack && symStack.length > 1)
+                                               symStack.shift(protoCtx);
+                                       else
+                                               delete superContext[slotIdx];
                                }
 
-                               delete superContext[slotIdx];
                                return res;
                        },