I have this class module I am working on and the problem is that whenever the __index metamethod in the metatable in the class function runs it freezes everything and then produces like 5000 errors and then stops freezing
The error line is here local new_class = t:__call(...)
The code I try to use with my module
local new, class, extends = require(module)()
class "foo" {
constructor = function(a, b, c)
print(a + b + c)
end
}
new "foo"(1, 2, 3) -- prints 6
class "bar" [extends "foo"] { -- freezes somewhere about here
a = 1
}
print(new "bar" (1,2,3).a) -- does not even run
The module
type ClassIdentifier = string | {}
-- deep copy
local function copy_t(t)
local result = {}
for k, v in pairs(t) do
result[k] = (type(v) == "table") and copy_t(v) or v
end
return result
end
-- set variable
local function setv(v, n, l)
getfenv(l or 3)[n] = v
return v, n, l
end
local function get_class(class)
local class = type(class) == "string" and getfenv(3)[class] or class
assert(type(class) == "table", `unexpected '{type(class)}'`)
return class
end
function extends(class : ClassIdentifier)
return get_class(class)
end
function class<T>(a : ClassIdentifier & T): (<A>(a: A) -> A) & T
local checkconstructor = function(k, v, c)
if k == "constructor" then
assert(type(v) == "function", "class field uses reserved keyword 'constructor'")
assert(c.constructor == nil, "classes may only have a singular constructor")
end
end
local new_class = a and setv({}, a) or {}
local result = {
__call = function(_, ...)
assert(#{...} == 1, "unexpected syntax")
local body = ({...})[1]
assert(type(body) == "table", `unexpected '{type(body)}'`)
for k, v in pairs(body) do
checkconstructor(k, v, new_class)
new_class[k] = v
end
return new_class
end,
__index = function(t, extended_class)
return function(...)
local new_class = t:__call(...)
for k, v in pairs(extended_class) do
new_class[k] = new_class[k] or v
end
if new_class.constructor then
setv(function(...)
assert(extended_class.constructor, "unexpected 'super'")
return extended_class.constructor(...)
end, "super", new_class.constructor)
end
end
end
}
return type(a) == "table" and result:__call(a) or setmetatable({}, result)
end
function new<T>(class : ClassIdentifier & T)
local class = get_class(class)
return function(...) : T
local instance = copy_t(class)
for k, v in pairs(instance) do
if type(v) == "function" then
setv(instance, "this", v)
if k == "constructor" then
v(...)
instance[k] = nil
end
end
end
return instance
end
end
return
function(): (typeof(new), typeof(class), typeof(extends))
return new, class, extends
end