-
Notifications
You must be signed in to change notification settings - Fork 0
/
inheritance.t
295 lines (249 loc) · 8.07 KB
/
inheritance.t
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
local util = require("util")
local C = terralib.includecstring [[
#include <stdio.h>
#include <stdlib.h>
]]
-- Really simple single inheritance
local Inheritance = {}
-- metadata for class system
local metadata = {}
local function issubclass(child,parent)
if child == parent then
return true
else
local par = metadata[child].parent
return par and issubclass(par,parent)
end
end
Inheritance.issubclass = issubclass
local function setParent(child, parent)
local md = metadata[child]
if md then
if md.parent then
error(string.format("'%s' already inherits from some type -- multiple inheritance not allowed.", child.name))
end
md.parent = parent
else
metadata[child] = {parent = parent}
end
end
local function castoperator(from, to, exp)
if from:ispointer() and to:ispointer() and issubclass(from.type, to.type) then
return `[to](exp)
else
error(string.format("'%s' does not inherit from '%s'", from.type.name, to.type.name))
end
end
local function lookupParentStaticMethod(class, methodname)
local cls = class
while cls ~= nil do
if cls.methods[methodname] ~= nil then
return cls.methods[methodname]
else
if metadata[cls] and metadata[cls].parent then
cls = metadata[cls].parent
else
cls = nil
end
end
end
return nil
end
local function copyparentlayoutStatic(class)
local parent = metadata[class].parent
for i,e in ipairs(parent.entries) do table.insert(class.entries, i, e) end
return class.entries
end
local function addstaticmetamethods(class)
class.metamethods.__cast = castoperator
class.metamethods.__getentries = copyparentlayoutStatic
class.metamethods.__getmethod = lookupParentStaticMethod
end
-- child inherits data layout and method table from parent
function Inheritance.staticExtend(parent, child)
setParent(child, parent)
addstaticmetamethods(child)
end
------------------------------------------
-- Create the function which will initialize the __vtable field
-- in each struct instance.
local function initvtable(class)
local md = metadata[class]
-- global, because otherwise it would be GC'ed.
md.vtable = global(md.vtabletype)
-- Add the vtable initializer (or augment an existing one)
-- (e.g. a virtual template could have already set up this method)
local oldinit = class.methods.__initvtable
class.methods.__initvtable = terra(self: &class)
[oldinit and (quote oldinit(self) end) or (quote end)]
self.__vtable = &md.vtable
end
assert(class.methods.__initvtable)
end
-- Finalize the vtable after the class has been compiled
local function finalizeVtable(class)
local md = metadata[class]
local vtbl = md.vtable:get()
for methodname,impl in pairs(md.methodimpl) do
impl:compile(function()
vtbl[methodname] = impl:getpointer()
end)
end
end
-- Create a 'stub' method which refers to the method of the same
-- name in the class's vtable
local function createstub(methodname,typ)
local symbols = typ.parameters:map(symbol)
local obj = symbols[1]
local terra wrapper([symbols]) : typ.returntype
return obj.__vtable.[methodname]([symbols])
end
return wrapper
end
local function getdefinitionandtype(impl)
if #impl:getdefinitions() ~= 1 then
error(string.format("Overloaded function '%s' cannot be virtual.", method.name))
end
local impldef = impl:getdefinitions()[1]
local success, typ = impldef:peektype()
if not success then
error(string.format("virtual method '%s' must have explicit return type", impl.name))
end
return impldef,typ
end
-- Finalize the layout of the struct
local function finalizeStructLayoutDynamic(class)
local md = metadata[class]
-- Start up the vtable data
struct md.vtabletype {}
md.methodimpl = {}
-- Create __vtable field
class.entries:insert(1, { field = "__vtable", type = &md.vtabletype})
-- Copy data from parent
local parent = md.parent
if parent then
-- Must do this to make sure the parent's layout has been finalized first
parent:getentries()
-- Static members (except the __vtable field)
for i=2,#parent.entries do
class.entries:insert(i, parent.entries[i])
end
-- vtable entries
local pmd = metadata[parent]
for i,m in ipairs(pmd.vtabletype.entries) do
md.vtabletype.entries:insert(m)
md.methodimpl[m.field] = pmd.methodimpl[m.field]
end
end
-- Copy all my virtual methods into the vtable staging area
for methodname, impl in pairs(class.methods) do
if md.vmethods and md.vmethods[methodname] then
local def, typ = getdefinitionandtype(impl)
if md.methodimpl[methodname] == nil then
md.vtabletype.entries:insert({field = methodname, type = &typ})
end
md.methodimpl[methodname] = def
end
end
-- Create method stubs (overwriting any methods marked virtual)
for methodname, impl in pairs(md.methodimpl) do
local _,typ = impl:peektype()
class.methods[methodname] = createstub(methodname, typ)
end
-- Make __vtable field initializer
initvtable(class)
return class.entries
end
-- Add metamethods necessary for dynamic dispatch
local function adddynamicmetamethods(class)
class.metamethods.__cast = castoperator
class.metamethods.__staticinitialize = finalizeVtable
class.metamethods.__getentries = finalizeStructLayoutDynamic
class.metamethods.__getmethod = lookupParentStaticMethod
end
-- Ensure that a struct is equipped for dynamic dispatch
-- (i.e. has a vtable, has the requisite metamethods)
local function ensuredynamic(class)
if not metadata[class] then
metadata[class] = {}
end
adddynamicmetamethods(class)
end
-- Mark a method as virtual
function Inheritance.virtual(class, methodname)
ensuredynamic(class)
local md = metadata[class]
if not md.vmethods then
md.vmethods = {}
end
md.vmethods[methodname] = true
-- If we're marking the destructor as virtual, then
-- provide a way to access the original, nonvirtual
-- destructor
if methodname == "__destruct" then
class.methods.__rawdestruct = class.methods.__destruct
end
end
-- Create a 'stub' method of type typ which throws a
-- 'not implemented' error.
local function createunimplementedstub(class, methodname, typ)
local symbols = typ.parameters:map(symbol)
local obj = symbols[1]
local terra wrapper([symbols]) : typ.returntype
util.fatalError("Pure virtual function '%s' not implemented in class '%s'\n", methodname, [class.name])
end
return wrapper
end
-- Declare a pure virtual function (no implementation)
function Inheritance.purevirtual(class, methodname, typ)
-- Expand the type to include the pointer to self
local params = util.copytable(typ.type.parameters)
local returntype = typ.type.returntype
table.insert(params, 1, &class)
typ = terralib.types.funcpointer(params, returntype)
-- Add an 'unimplemented' method with this name to the class
class.methods[methodname] = createunimplementedstub(class, methodname, typ.type)
-- Now do all the stuff we usually do for virtual methods.
Inheritance.virtual(class, methodname)
end
-- child inherits data layout and method table from parent
-- child also inherits vtable from parent
function Inheritance.dynamicExtend(parent, child)
ensuredynamic(parent)
ensuredynamic(child)
setParent(child, parent)
end
function Inheritance.isInstanceOf(T)
-- Lua callback that first finds the dynamic type associated with
-- a vtable, then checks if that type is a descendant of T
local function isDynamicSubtype(vptr)
local dyntyp = nil
for t,md in pairs(metadata) do
if md.vtable and md.vtable:getpointer() == vptr then
dyntyp = t
break
end
end
if dyntyp == nil then return false end
while dyntyp do
if dyntyp == T then return true end
dyntyp = metadata[dyntyp] and metadata[dyntyp].parent
end
return false
end
isDynamicSubtype = terralib.cast({&opaque}->{bool}, isDynamicSubtype)
return macro(function(inst)
local t = inst:gettype()
if t:ispointertostruct() then t = t.type end
-- First check: is t a subtype of T?
if issubclass(t, T) then return true end
-- Otherwise, we need to compare vtable pointers
-- (Are these getentries() calls safe???)
T:getentries()
t:getentries()
-- Not possible if t doesn't have a vtable
if not (metadata[t] and metadata[t].vtable) then return false end
return `isDynamicSubtype([&opaque](inst.__vtable))
end)
end
return Inheritance