First attempt at safe virtual overrides
[lqt/mk.git] / generator / virtuals.lua
blobf424fd47ebbb3cd8ead5d210865b609a3fec3844
1 module('virtuals', package.seeall)
3 --- Retrieves the virtual method for each class. Also retrieves the virtual
4 -- methods for all superclasses.
5 function fill_virtuals(classes)
6 local byname = {}
7 for c in pairs(classes) do
8 byname[c.xarg.fullname] = c
9 end
10 local function get_virtuals(c, includePrivate)
11 local ret = {}
12 local virtual_index = 0
13 local function add_overload(name, func)
14 virtual_index = virtual_index + 1
15 func.virtual_index = virtual_index
16 ret[name] = func
17 end
19 -- add virtual methods declared in the class
20 for _, f in ipairs(c) do
21 if f.label=='Function' and f.xarg.virtual=='1' then
22 local n = string.match(f.xarg.name, '~') or f.xarg.name
23 if n~='~' and n~='metaObject' then
24 add_overload(n, f)
25 end
26 end
27 end
29 -- find virtual methods in base classes
30 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
31 local base = byname[b]
32 if type(base)=='table' then
33 local bv = get_virtuals(base, true)
34 for n, f in pairs(bv) do
35 if not ret[n] then add_overload(n, f) end
36 end
37 end
38 end
40 -- mark methods in class not explicitly marked as virtual, as C++
41 -- does not require that overriden methods are marked virtual
42 for _, f in ipairs(c) do
43 local n = string.match(f.xarg.name, '~') or f.xarg.name
44 if f.label=='Function'
45 and (includePrivate or f.xarg.access~='private')
46 and (ret[n])
47 then
48 f.xarg.virtual = '1'
49 end
50 end
51 return ret, virtual_index
52 end
53 for c in pairs(classes) do
54 c.virtuals, c.nvirtuals = get_virtuals(c)
55 end
56 end
59 --- Generates a virtual overload for function 'v'.
60 -- Returns nil if a parameter or return type is of unknown/ignored type. Normal
61 -- virtual methods call original virtual method if no corresponding Lua function is
62 -- found, pure virtual (abstract) methods throw Lua error.
63 function virtual_overload(v)
64 local ret = ''
65 if v.virtual_overload then return v end
66 -- make return type
67 if v.return_type and not typesystem[v.return_type] then
68 ignore(v.xarg.fullname, 'unknown return type', v.return_type)
69 return nil, 'return: '..v.return_type
70 end
71 local rget, rn, ret_as = '', 0
72 if v.return_type then rget, rn, ret_as = typesystem[v.return_type].get'oldtop+2' end
73 local retget = ''
74 if v.return_type then
75 local atest, an = typesystem[v.return_type].test('oldtop+2')
76 retget = [[if (!(]]..atest..[[)) {
77 luaL_error(L, "Unexpected virtual method return type: %s; expecting %s\nin: %s",
78 luaL_typename(L,oldtop+2), "]]..v.return_type..[[", lqtL_source(L,oldtop+1));
81 retget = retget .. argument_name(ret_as or v.return_type, 'ret') .. ' = ' .. rget .. ';\n '
82 end
83 retget = retget .. 'lua_settop(L, oldtop);\n return' .. (v.return_type and ' ret' or '')
84 -- make argument push
85 local pushlines, stack = make_pushlines(v.arguments)
86 if not pushlines then
87 ignore(v.xarg.fullname, 'unknown argument type', stack)
88 return nil, 'argument: '..stack
89 end
90 -- make lua call
91 local luacall = 'lqtL_pcall(L, '..(stack+1)..', '..rn..', 0)'
92 -- make prototype and fallback
93 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
94 local fallback = ''
95 for i, a in ipairs(v.arguments) do
96 proto = proto .. (i>1 and ', ' or '')
97 .. argument_name(a.xarg.type_name, 'arg'..i)
98 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
99 end
100 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
101 fallback = (v.return_type and 'return this->' or 'this->') .. v.xarg.fullname .. '(' .. fallback .. ');' ..
102 (v.return_type and '' or ' return;')
103 if v.xarg.abstract then
104 fallback = 'luaL_error(L, "Abstract method %s not implemented! In %s", "' .. v.xarg.name .. '", lqtL_source(L,oldtop+1));'
106 ret = proto .. ' {\n'
107 ret = ret .. ' int oldtop = lua_gettop(L);\n'
108 ret = ret .. ' printf("Checking for override idx: %d name: %s class: %s value: %d in thread: %lx\\n", ' ..
109 v.virtual_index .. ', "'..v.xarg.name..'", "'.. v.xarg.member_of_class.. '", ' ..
110 '(int)(bool)hasOverride[' .. v.virtual_index .. '], QThread::currentThreadId());\n'
111 ret = ret .. ' if (!hasOverride[' .. v.virtual_index .. ']) { \n'
112 ret = ret .. ' ' .. fallback .. '\n }\n'
113 ret = ret .. [[
114 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
115 lqtL_getoverload(L, -1, "]]..v.xarg.name..[[");
116 lua_pushvalue(L, -1); // copy of function
117 if (lua_isfunction(L, -1)) {
118 lua_insert(L, -3);
119 lua_insert(L, -3);
120 ]] .. pushlines .. [[
121 if (!]]..luacall..[[) {
122 ]]..retget..[[;
123 } else {
124 if (lqtL_is_super(L, lua_gettop(L))) {
125 lua_settop(L, oldtop);
126 ]]..fallback..[[
127 } else
128 lua_error(L);
130 } else {
131 lua_settop(L, oldtop);
132 ]]..fallback..[[
136 v.virtual_overload = ret
137 v.virtual_proto = string.gsub(proto, ';;', '', 1)
138 return v
143 function fill_virtual_overloads(classes)
144 for c in pairs(classes) do
145 if c.virtuals then
146 for i, v in pairs(c.virtuals) do
147 if v.xarg.access~='private' then
148 local vret, err = virtual_overload(v)
149 if not vret and v.xarg.abstract then
150 -- cannot create instance without implementation of an abstract method
151 c.abstract = true
161 function fill_shell_class(c)
162 local shellname = 'lqt_shell_'..c.xarg.cname
163 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\n'
164 shell = shell .. ' lua_State *L;\n'
165 shell = shell .. ' QBitArray hasOverride;\n'
166 shell = shell .. 'public:\n'
167 shell = shell .. ' static int lqtAddOverride(lua_State *L);\n'
168 for _, constr in ipairs(c.constructors) do
169 if constr.xarg.access~='private' then
170 local cline = ' '..shellname..' (lua_State *l'
171 local argline = ''
172 for i, a in ipairs(constr.arguments) do
173 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
174 argline = argline .. (i>1 and ', arg' or 'arg') .. i
176 cline = cline .. ') : ' .. c.xarg.fullname
177 .. '(' .. argline .. '), L(l), hasOverride(' .. c.nvirtuals .. ') '
178 .. '{\n lqtL_register(L, this);\n'
179 if c.protected_enums then
180 cline = cline .. ' registerEnums();\n'
182 cline = cline .. ' }\n'
183 shell = shell .. cline
186 if c.copy_constructor==nil and c.public_constr then
187 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
188 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
189 shell = shell .. cline
191 for i, v in pairs(c.virtuals) do
192 if v.xarg.access~='private' then
193 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
196 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
197 if c.shell and c.qobject then
198 shell = shell .. ' static QMetaObject staticMetaObject;\n'
199 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
200 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
201 shell = shell .. 'private:\n'
202 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
204 if c.protected_enums then
205 shell = shell .. ' void registerEnums() {\n'
206 for _,e in ipairs(c.protected_enums) do
207 shell = shell .. e.enum_table
208 shell = shell .. ' lqtL_createenum(L, lqt_enum'..e.xarg.id..', "'..string.gsub(e.xarg.fullname, "::", ".")..'");\n'
210 shell = shell .. ' }\n'
212 shell = shell .. '};\n'
213 c.shell_class = shell
214 return c
218 function fill_shell_classes(classes)
219 for c in pairs(classes) do
220 if c.shell then
221 local nc = fill_shell_class(c)
222 if not nc then
223 -- FIXME: useless, but may change
224 ignore(c.xarg.fullname, 'failed to generate shell class')
225 classes[c] = nil
231 ----------------------------------------------------------------------
233 function print_shell_classes(classes)
234 for c in pairs(classes) do
235 local n = c.xarg.cname
236 local fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
237 local print_head = function(...)
238 fhead:write(...)
239 fhead:write'\n'
241 print_head('#ifndef LQT_HEAD_'..n)
242 print_head('#define LQT_HEAD_'..n)
243 print_head(output_includes)
244 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
245 print_head''
246 if c.shell then
247 print_head('#include "'..module_name..'_slot.hpp'..'"\n\n')
248 if c.shell_class then
249 print_head(c.shell_class)
250 else
251 dump(c)
255 print_head('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *);')
256 print_head('\n\n#endif // LQT_HEAD_'..n)
257 fhead:close()
259 return classes
262 function print_virtual_overloads(classes)
263 for c in pairs(classes) do
264 if c.shell then
265 local vo = ''
266 local shellname = 'lqt_shell_'..c.xarg.cname
267 for _,v in pairs(c.virtuals) do
268 if v.virtual_overload then
269 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
272 c.virtual_overloads = vo
275 return classes
278 function sort_by_index(c)
279 local res = {}
280 for name, virt in pairs(c.virtuals) do
281 res[virt.virtual_index] = virt
283 return res