luci-base: luci.js: properly handle nested Class.super() calls
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>
This commit is contained in:
parent
666ad2df14
commit
374c23cdab
1 changed files with 21 additions and 12 deletions
|
@ -67,7 +67,7 @@
|
||||||
* It provides simple means to create subclasses of given classes and
|
* It provides simple means to create subclasses of given classes and
|
||||||
* implements prototypal inheritance.
|
* 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
|
* Extends this base class with the properties described in
|
||||||
* `properties` and returns a new subclassed Class instance
|
* `properties` and returns a new subclassed Class instance
|
||||||
|
@ -86,8 +86,9 @@
|
||||||
*/
|
*/
|
||||||
extend: function(properties) {
|
extend: function(properties) {
|
||||||
var props = {
|
var props = {
|
||||||
|
__id__: { value: classIndex },
|
||||||
__base__: { value: this.prototype },
|
__base__: { value: this.prototype },
|
||||||
__name__: { value: properties.__name__ || 'anonymous' }
|
__name__: { value: properties.__name__ || 'anonymous' + classIndex++ }
|
||||||
};
|
};
|
||||||
|
|
||||||
var ClassConstructor = function() {
|
var ClassConstructor = function() {
|
||||||
|
@ -265,19 +266,28 @@
|
||||||
* superclass method returned `null`.
|
* superclass method returned `null`.
|
||||||
*/
|
*/
|
||||||
super: function(key, callArgs) {
|
super: function(key, callArgs) {
|
||||||
for (superContext = Object.getPrototypeOf(superContext ||
|
if (key == null)
|
||||||
Object.getPrototypeOf(this));
|
|
||||||
superContext && !superContext.hasOwnProperty(key);
|
|
||||||
superContext = Object.getPrototypeOf(superContext)) { }
|
|
||||||
|
|
||||||
if (!superContext)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var res = superContext[key];
|
var slotIdx = this.__id__ + '.' + key;
|
||||||
|
|
||||||
|
for (superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx] ||
|
||||||
|
Object.getPrototypeOf(this));
|
||||||
|
superContext[slotIdx] && !superContext[slotIdx].hasOwnProperty(key);
|
||||||
|
superContext[slotIdx] = Object.getPrototypeOf(superContext[slotIdx])) {}
|
||||||
|
|
||||||
|
if (!superContext[slotIdx]) {
|
||||||
|
delete superContext[slotIdx];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = superContext[slotIdx][key];
|
||||||
|
|
||||||
if (arguments.length > 1) {
|
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');
|
throw new ReferenceError(key + ' is not a function in base class');
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof(callArgs) != 'object')
|
if (typeof(callArgs) != 'object')
|
||||||
callArgs = this.varargs(arguments, 1);
|
callArgs = this.varargs(arguments, 1);
|
||||||
|
@ -285,8 +295,7 @@
|
||||||
res = res.apply(this, callArgs);
|
res = res.apply(this, callArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
superContext = null;
|
delete superContext[slotIdx];
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue