[fixed] super call in case where subclass called from doesn't have implementation - hurts runtime performance but the previous impl was a potential infinite loop!

This commit is contained in:
Max Cahill 2020-05-22 15:50:23 +10:00
parent 9d0a7d6e86
commit fff1474c37

View File

@ -7,22 +7,24 @@
local function class(inherits) local function class(inherits)
local c = {} local c = {}
c.__mt = { --class metatable
__index = c,
}
setmetatable(c, { setmetatable(c, {
--wire up call as ctor --wire up call as ctor
__call = function(self, ...) __call = function(self, ...)
return self:new(...) return self:new(...)
end, end,
--handle single inheritence --handle single inheritence chain
__index = inherits, __index = inherits,
}) })
--instance metatable
c.__mt = {
__index = c,
}
--common class functions --common class functions
--internal initialisation --internal initialisation
--sets up an initialised object with a default value table --sets up an initialised object with a default value table
--performing a super construction if necessary and assigning the right metatable --performing a super construction if necessary, and (re-)assigning the right metatable
function c:init(t, ...) function c:init(t, ...)
if inherits and inherits.new then if inherits and inherits.new then
--construct superclass instance, then overlay args table --construct superclass instance, then overlay args table
@ -45,17 +47,38 @@ local function class(inherits)
--get the inherited class for super calls if/as needed --get the inherited class for super calls if/as needed
--allows overrides that still refer to superclass behaviour --allows overrides that still refer to superclass behaviour
function c:super() function c:super()
return inherits or c return inherits
end end
--delegate a call to the super class, by name --delegate a call to the superclass, by name
--still a bit clumsy but cleaner than the inline equivalent --still a bit clumsy but much cleaner than the inline equivalent,
--plus handles heirarchical complications, and detects various mistakes
function c:super_call(func_name, ...) function c:super_call(func_name, ...)
local f = self:super()[func_name] --
if f then assert(type(func_name) == "string", "super_call requires a string function name to look up")
return f(self, ...) --todo: memoize the below :)
local first_impl = c
--find the first superclass that actually has the method
while first_impl and not rawget(first_impl, func_name) do
first_impl = first_impl:super()
end end
error("failed super call - missing function "..tostring(func_name).." in superclass") if not first_impl then
error("failed super call - no superclass in the chain has an implementation of "..func_name)
end
--get the superclass of that
local super = first_impl:super()
if not super then
error("failed super call - no superclass to call from")
end
local f = super[func_name]
if not f then
error("failed super call - missing function "..func_name.." in superclass")
end
if f == self[func_name] then
error("failed super call - function "..func_name.." is same in superclass as in derived; this will be a infinite recursion!")
end
return f(self, ...)
end end
--done --done