-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathclass.lua
259 lines (233 loc) · 7.09 KB
/
class.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
local TrackClassInstances = false
ClassRegistry = {}
if TrackClassInstances == true then
global("ClassTrackingTable")
global("ClassTrackingInterval")
ClassTrackingInterval = 100
end
local function _is_a(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
end
return false
end
local function _is_class(self)
return rawget(self, "is_instance") ~= nil
end
local function __index(t, k)
local p = rawget(t, "_")[k]
if p ~= nil then
return p[1]
end
return getmetatable(t)[k]
end
local function __newindex(t, k, v)
local p = rawget(t, "_")[k]
if p == nil then
rawset(t, k, v)
else
local old = p[1]
p[1] = v
p[2](t, v, old)
end
end
local function onreadonly(t, v, old)
assert(v == old, "Cannot change read only property")
end
function makereadonly(t, k)
local _ = rawget(t, "_")
assert(_ ~= nil, "Class does not support read only properties")
local p = _[k]
if p == nil then
_[k] = { t[k], onreadonly }
rawset(t, k, nil)
else
p[2] = onreadonly
end
end
function addsetter(t, k, fn)
local _ = rawget(t, "_")
assert(_ ~= nil, "Class does not support property setters")
local p = _[k]
if p == nil then
_[k] = { t[k], fn }
rawset(t, k, nil)
else
p[2] = fn
end
end
function removesetter(t, k)
local _ = rawget(t, "_")
if _ ~= nil and _[k] ~= nil then
rawset(t, k, _[k][1])
_[k] = nil
end
end
function Class(base, _ctor, props)
local c = {} -- a new class instance
local c_inherited = {}
if not _ctor and type(base) == 'function' then
_ctor = base
base = nil
elseif type(base) == 'table' then
-- our new class is a shallow copy of the base class!
-- while at it also store our inherited members so we can get rid of them
-- while monkey patching for the hot reload
-- if our class redefined a function peronally the function pointed to by our member is not the in in our inherited
-- table
for i,v in pairs(base) do
c[i] = v
c_inherited[i] = v
end
c._base = base
end
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
if props ~= nil then
c.__index = __index
c.__newindex = __newindex
else
c.__index = c
end
-- expose a constructor which can be called by <classname>(<args>)
local mt = {}
if TrackClassInstances == true and CWD~=nil then
if ClassTrackingTable == nil then
ClassTrackingTable = {}
end
ClassTrackingTable[mt] = {}
local dataroot = "@"..CWD.."\\"
local tablemt = {}
setmetatable(ClassTrackingTable[mt], tablemt)
tablemt.__mode = "k" -- now the instancetracker has weak keys
local source = "**unknown**"
if _ctor then
-- what is the file this ctor was created in?
local info = debug.getinfo(_ctor, "S")
-- strip the drive letter
-- convert / to \\
source = info.source
source = string.gsub(source, "/", "\\")
source = string.gsub(source, dataroot, "")
local path = source
local file = io.open(path, "r")
if file ~= nil then
local count = 1
for i in file:lines() do
if count == info.linedefined then
source = i
-- okay, this line is a class definition
-- so it's [local] name = Class etc
-- take everything before the =
local equalsPos = string.find(source,"=")
if equalsPos then
source = string.sub(source,1,equalsPos-1)
end
-- remove trailing and leading whitespace
source = source:gsub("^%s*(.-)%s*$", "%1")
-- do we start with local? if so, strip it
if string.find(source,"local ") ~= nil then
source = string.sub(source,7)
end
-- trim again, because there may be multiple spaces
source = source:gsub("^%s*(.-)%s*$", "%1")
break
end
count = count + 1
end
file:close()
end
end
mt.__call = function(class_tbl, ...)
local obj = {}
if props ~= nil then
obj._ = {}
for k, v in pairs(props) do
obj._[k] = { nil, v }
end
end
setmetatable(obj, c)
ClassTrackingTable[mt][obj] = source
if c._ctor then
c._ctor(obj, ...)
end
return obj
end
else
mt.__call = function(class_tbl, ...)
local obj = {}
if props ~= nil then
obj._ = {}
for k, v in pairs(props) do
obj._[k] = { nil, v }
end
end
setmetatable(obj, c)
if c._ctor then
c._ctor(obj, ...)
end
return obj
end
end
c._ctor = _ctor
c.is_a = _is_a -- is_a: is descendent of this class
c.is_class = _is_class -- is_class: is self a class instead of an instance
c.is_instance = function(obj) -- is_instance: is obj an instance of this class
return type(obj) == "table" and _is_a(obj, c)
end
setmetatable(c, mt)
ClassRegistry[c] = c_inherited
-- local count = 0
-- for i,v in pairs(ClassRegistry) do
-- count = count + 1
-- end
-- if string.split then
-- print("ClassRegistry size : "..tostring(count))
-- end
return c
end
function ReloadedClass(mt)
ClassRegistry[mt] = nil
end
local lastClassTrackingDumpTick = 0
function HandleClassInstanceTracking()
if TrackClassInstances and CWD~=nil then
lastClassTrackingDumpTick = lastClassTrackingDumpTick + 1
if lastClassTrackingDumpTick >= ClassTrackingInterval then
collectgarbage()
print("------------------------------------------------------------------------------------------------------------")
lastClassTrackingDumpTick = 0
if ClassTrackingTable then
local sorted = {}
local index = 1
for i,v in pairs(ClassTrackingTable) do
local count = 0
local first = nil
for j,k in pairs(v) do
if count == 1 then
first = k
end
count = count + 1
end
if count>1 then
sorted[#sorted+1] = {first, count-1}
end
index = index + 1
end
-- get the top 10
table.sort(sorted, function(a,b) return a[2] > b[2] end )
for i=1,10 do
local entry = sorted[i]
if entry then
print(tostring(i).." : "..tostring(sorted[i][1]).." - "..tostring(sorted[i][2]))
end
end
print("------------------------------------------------------------------------------------------------------------")
end
end
end
end