From fff1474c37b929a74c294b98643163e4104c4ab2 Mon Sep 17 00:00:00 2001 From: Max Cahill <1bardesign@gmail.com> Date: Fri, 22 May 2020 15:50:23 +1000 Subject: [PATCH] [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! --- class.lua | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/class.lua b/class.lua index d394e7a..aec8df2 100644 --- a/class.lua +++ b/class.lua @@ -7,22 +7,24 @@ local function class(inherits) local c = {} - c.__mt = { - __index = c, - } + --class metatable setmetatable(c, { --wire up call as ctor __call = function(self, ...) return self:new(...) end, - --handle single inheritence + --handle single inheritence chain __index = inherits, }) + --instance metatable + c.__mt = { + __index = c, + } --common class functions --internal initialisation --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, ...) if inherits and inherits.new then --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 --allows overrides that still refer to superclass behaviour function c:super() - return inherits or c + return inherits end - --delegate a call to the super class, by name - --still a bit clumsy but cleaner than the inline equivalent + --delegate a call to the superclass, by name + --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, ...) - local f = self:super()[func_name] - if f then - return f(self, ...) + -- + assert(type(func_name) == "string", "super_call requires a string function name to look up") + --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 - 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 --done