Re-enable access to protected functions
[lqt/mk.git] / generator / virtuals.lua
blob0ea36a37656185d472dc39ed3cc55792e365a8f0
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 methods = {}
12 for _, f in ipairs(c) do
13 if f.label=='Function' and f.xarg.virtual ~= '1' then
14 methods[f.xarg.name] = f
15 end
16 end
18 local ret = {}
19 local function add_overload(name, func)
20 ret[name] = func
21 end
23 -- add virtual methods declared in the class
24 for _, f in ipairs(c) do
25 if f.label=='Function' and f.xarg.virtual=='1' then
26 local n = string.match(f.xarg.name, '~') or f.xarg.name
27 if n~='~' and n~='metaObject' then
28 add_overload(n, f)
29 end
30 end
31 end
33 -- find virtual methods in base classes
34 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
35 local base = byname[b]
36 if type(base)=='table' then
37 local bv = get_virtuals(base, true)
38 for n, f in pairs(bv) do
39 -- print('found', n, 'in', b, 'for', c.xarg.name, 'have', not not ret[n])
40 if not ret[n] then
41 if methods[n] then
42 -- print('has', n, 'which is not marked virtual')
43 methods[n].xarg.virtual = '1'
44 add_overload(n, methods[n])
45 else
46 -- print('does not have', n)
47 add_overload(n, f)
48 end
49 end
50 end
51 end
52 end
54 -- mark methods in class not explicitly marked as virtual, as C++
55 -- does not require that overriden methods are marked virtual
56 for _, f in ipairs(c) do
57 local n = string.match(f.xarg.name, '~') or f.xarg.name
58 if f.label=='Function'
59 and (includePrivate or f.xarg.access~='private')
60 and ret[n]
61 then
62 -- print('adding', c.xarg.name, n)
63 f.xarg.virtual = '1'
64 add_overload(n, f)
65 end
66 end
68 local virtual_index = 0
69 for n, f in pairs(ret) do
70 f.virtual_index = virtual_index
71 virtual_index = virtual_index + 1
72 end
74 return ret, virtual_index
75 end
76 for c in pairs(classes) do
77 c.virtuals, c.nvirtuals = get_virtuals(c)
78 end
79 end
82 --- Generates a virtual overload for function 'v'.
83 -- Returns nil if a parameter or return type is of unknown/ignored type. Normal
84 -- virtual methods call original virtual method if no corresponding Lua function is
85 -- found, pure virtual (abstract) methods throw Lua error.
86 function virtual_overload(v)
87 if v.virtual_overload then return v end
88 -- make return type
89 if v.return_type and not typesystem[v.return_type] then
90 ignore(v.xarg.fullname, 'unknown return type', v.return_type)
91 return nil, 'return: '..v.return_type
92 end
93 local ret = ''
94 local rget, rn, ret_as = '', 0
95 if v.return_type then rget, rn, ret_as = typesystem[v.return_type].get'oldtop+2' end
96 local retget = ''
97 if v.return_type then
98 local atest, an = typesystem[v.return_type].test('oldtop+2')
99 retget = [[if (!(]]..atest..[[)) {
100 luaL_error(L, "Unexpected virtual method return type: %s; expecting %s\nin: %s",
101 luaL_typename(L,oldtop+2), "]]..v.return_type..[[", lqtL_source(L,oldtop+1));
104 retget = retget .. argument_name(ret_as or v.return_type, 'ret') .. ' = ' .. rget .. ';\n '
106 retget = retget .. 'lua_settop(L, oldtop);\n return' .. (v.return_type and ' ret' or '')
107 -- make argument push
108 local pushlines, stack = make_pushlines(v.arguments)
109 if not pushlines then
110 ignore(v.xarg.fullname, 'unknown argument type', stack)
111 return nil, 'argument: '..stack
113 -- make lua call
114 local luacall = 'lqtL_pcall(L, '..(stack+1)..', '..rn..', 0)'
115 -- make prototype and fallback
116 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
117 local fallback = ''
118 for i, a in ipairs(v.arguments) do
119 proto = proto .. (i>1 and ', ' or '')
120 .. argument_name(a.xarg.type_name, 'arg'..i)
121 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
123 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
124 fallback = (v.return_type and 'return this->' or 'this->') .. v.xarg.fullname .. '(' .. fallback .. ');' ..
125 (v.return_type and '' or ' return;')
126 if v.xarg.abstract then
127 fallback = 'luaL_error(L, "Abstract method %s for %s not implemented! In %s", "' .. v.xarg.name .. '", lqtL_source(L,oldtop+1));'
129 ret = proto .. ' {\n'
130 ret = ret .. ' int oldtop = lua_gettop(L);\n'
131 if VERBOSE_BUILD then
132 ret = ret .. ' printf("[%lx; %p] virtual %s :: %s (%d) => %d\\n", ' ..
133 'QThread::currentThreadId(), this, ' ..
134 '"'..v.xarg.member_of_class.. '", ' ..
135 '"'..v.xarg.name..'", '..
136 'VIRTUAL_INDEX, '..
137 '(int)(bool)hasOverride[VIRTUAL_INDEX]'..
138 ');\n'
140 ret = ret .. ' if (!hasOverride[VIRTUAL_INDEX]) { \n'
141 ret = ret .. ' ' .. fallback .. '\n }\n'
142 ret = ret .. [[
143 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
144 lqtL_getoverload(L, -1, "]]..v.xarg.name..[[");
145 lua_pushvalue(L, -1); // copy of function
146 if (lua_isfunction(L, -1)) {
147 lua_insert(L, -3);
148 lua_insert(L, -3);
149 ]] .. pushlines .. [[
150 if (!]]..luacall..[[) {
151 ]]..retget..[[;
152 } else {
153 if (lqtL_is_super(L, lua_gettop(L))) {
154 lua_settop(L, oldtop);
155 ]]..fallback..[[
156 } else
157 lua_error(L);
159 } else {
160 lua_settop(L, oldtop);
161 ]]..fallback..[[
165 v.virtual_overload = ret
166 v.virtual_proto = string.gsub(proto, ';;', '', 1)
167 return v
172 function fill_virtual_overloads(classes)
173 for c in pairs(classes) do
174 if c.virtuals then
175 local vidx = 0
176 for i, v in pairs(c.virtuals) do
177 if v.xarg.access~='private' then
178 local vret, err = virtual_overload(v)
179 if not vret and v.xarg.abstract then
180 -- cannot create instance without implementation of an abstract method
181 c.abstract = true
191 function fill_shell_class(c)
192 local shellname = 'lqt_shell_'..c.xarg.cname
193 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\n'
194 shell = shell .. ' lua_State *L;\n'
195 shell = shell .. ' QBitArray hasOverride;\n'
196 shell = shell .. 'public:\n'
197 shell = shell .. ' static int lqtAddOverride(lua_State *L);\n'
198 for _, constr in ipairs(c.constructors) do
199 if constr.xarg.access~='private' then
200 local cline = ' '..shellname..' (lua_State *l'
201 local argline = ''
202 for i, a in ipairs(constr.arguments) do
203 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
204 argline = argline .. (i>1 and ', arg' or 'arg') .. i
206 cline = cline .. ') : ' .. c.xarg.fullname
207 .. '(' .. argline .. '), L(l), hasOverride(' .. c.nvirtuals .. ') '
208 .. '{\n lqtL_register(L, this);\n'
209 if c.protected_enums then
210 cline = cline .. ' registerEnums();\n'
212 cline = cline .. ' }\n'
213 shell = shell .. cline
216 if c.copy_constructor==nil and c.public_constr then
217 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
218 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
219 shell = shell .. cline
221 for i, v in pairs(c.virtuals) do
222 if v.xarg.access~='private' then
223 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
226 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
227 if c.shell and c.qobject then
228 shell = shell .. ' static QMetaObject staticMetaObject;\n'
229 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
230 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
231 shell = shell .. 'private:\n'
232 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
234 if c.protected_enums then
235 shell = shell .. ' void registerEnums() {\n'
236 for _,e in ipairs(c.protected_enums) do
237 shell = shell .. e.enum_table
238 shell = shell .. ' lqtL_createenum(L, lqt_enum'..e.xarg.id..', "'..string.gsub(e.xarg.fullname, "::", ".")..'");\n'
240 shell = shell .. ' }\n'
242 shell = shell .. '};\n'
243 c.shell_class = shell
244 return c
248 function fill_shell_classes(classes)
249 for c in pairs(classes) do
250 if c.shell then
251 local nc = fill_shell_class(c)
252 if not nc then
253 -- FIXME: useless, but may change
254 ignore(c.xarg.fullname, 'failed to generate shell class')
255 classes[c] = nil
261 ----------------------------------------------------------------------
263 function print_shell_classes(classes)
264 for c in pairs(classes) do
265 local n = c.xarg.cname
266 local fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
267 local print_head = function(...)
268 fhead:write(...)
269 fhead:write'\n'
271 print_head('#ifndef LQT_HEAD_'..n)
272 print_head('#define LQT_HEAD_'..n)
273 print_head('/* ugly ugly ugly, but needed to access protected members from outside */')
274 print_head('#define protected public')
275 print_head(output_includes)
276 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
277 print_head''
278 if c.shell then
279 print_head('#include "'..module_name..'_slot.hpp'..'"\n\n')
280 if c.shell_class then
281 print_head(c.shell_class)
282 else
283 dump(c)
287 print_head('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *);')
288 print_head('\n\n#endif // LQT_HEAD_'..n)
289 fhead:close()
291 return classes
294 function sort_by_index(c)
295 local res = {}
296 local vidx = 0
297 for name, virt in pairs(c.virtuals) do
298 virt.virtual_index = vidx
299 res[#res + 1] = virt
300 vidx = vidx + 1
302 table.sort(res, function(v1, v2) return v1.virtual_index < v2.virtual_index end)
303 c.nvirtuals = vidx
304 return res