Re-enable access to protected functions
[lqt/mk.git] / generator / classes.lua
blob6756a3947eee0eb299fd29facf71ef67689a38bf
1 require 'virtuals'
2 require 'templates'
3 require 'operators'
4 require 'signalslot'
5 require 'properties'
7 module('classes', package.seeall)
9 -- collection of all functions
10 local functions = {}
11 -- collection of bound classes
12 local classes = {}
13 -- list of files to be included
14 local cpp_files = {}
15 -- table of classes by their cname
16 local classlist = {}
18 --- Copies functions from the index.
19 function copy_functions(index)
20 for e in pairs(index) do
21 if e.label:match'^Function' then
22 e.label = 'Function'
23 functions[e] = true
24 end
25 end
26 end
28 function class_contains(where, what)
29 local class = fullnames[where]
30 if not class then return false end
31 for _, child in ipairs(class) do
32 if child.xarg.name == what then return true
33 elseif child.label == 'Enum' then
34 for _, enum in ipairs(child) do
35 if enum.xarg.name == what then return true end
36 end
37 end
38 end
39 return false
40 end
42 --- Fixes default arguments.
43 -- Default arguments are processed outside of the enclosing class, so every reference to an
44 -- enum or variable needs to be properly prefixed with the class name.
45 function fix_arguments(index)
46 for a in pairs(index) do
47 if a.label=='Argument'
48 and a.xarg.default=='1'
49 and (not string.match(a.xarg.defaultvalue, '^[-+]?%d+%.?%d*[L]?$'))
50 and (not string.match(a.xarg.defaultvalue, '^".*"$'))
51 and a.xarg.defaultvalue~='true'
52 and a.xarg.defaultvalue~='false'
53 and (not string.match(a.xarg.defaultvalue, '^0[.xX][%da-fA-F]+$'))
54 then
55 local dv, call = string.match(a.xarg.defaultvalue, '(.-)(%(.-%))')
56 dv = dv or a.xarg.defaultvalue
57 call = call or ''
58 local context = a.xarg.context
59 -- try to determine the class scope which contains the default value (dv)
60 while not fullnames[context..'::'..dv] and context~='' do
61 context = string.match(context, '^(.*)::') or ''
62 end
63 -- try in superclass (FIXME: try all superclasses)
64 if not fullnames[context..'::'..dv] then
65 context = a.xarg.context
66 local class = fullnames[context]
67 if class.xarg.bases then
68 context = class.xarg.bases:match('[^;]+')
69 end
70 end
71 -- workaround for enums and values in the context class
72 local callValue = call:sub(2,-2)
73 if #callValue>0 then
74 callValue = callValue:gsub('[%a][%a%d]+', function(id)
75 if class_contains(context, id) then return context..'::'..id end
76 end)
77 call = '('..callValue..')'
78 end
79 local firstChar = dv:sub(1,1)
80 if fullnames[context..'::'..dv] then
81 -- remove unneeded context prefix
82 if fullnames[context..'::'..dv].xarg.name==fullnames[context..'::'..dv].xarg.member_of_class then
83 context = string.match(context, '^(.*)::') or ''
84 end
85 a.xarg.defaultvalue = context..'::'..dv..call
86 elseif fullnames[dv] or dv == dv:upper() or (firstChar == 'Q' and #call == 0) or firstChar == "'" then
87 a.xarg.defaultvalue = dv..call
88 else
89 ignore(a.xarg.context..'::'..a.xarg.name, 'Unsupported default value', a.xarg.defaultvalue)
90 a.xarg.default = nil
91 a.xarg.defaultvalue = nil
92 end
93 end
94 end
95 end
98 --- Removes unneeded 'void' parameters and return values.
99 function fix_functions()
100 for f in pairs(functions) do
101 local args = {}
102 for i, a in ipairs(f) do
103 -- avoid bogus 'void' arguments
104 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
105 if a.label=='Argument' then
106 table.insert(args, a)
109 f.arguments = args
110 f.return_type = f.xarg.type_name
111 if f.xarg.type_name=='void' then
112 f.return_type = nil
117 --- Determines, if a class is public.
118 function class_is_public(c)
119 repeat
120 if c.xarg.access~='public' then return false end
121 if c.xarg.member_of_class then
122 local p = fullnames[c.xarg.member_of_class]
123 assert(p, 'member_of_class should exist')
124 assert(p.label=='Class', 'member_of_class should be a class')
125 c = fullnames[c.xarg.member_of_class]
126 else
127 return true
129 until true
132 --- Selects public classes from the index, and creates templated instances
133 -- where appropriate.
134 function copy_classes(index)
135 for e in pairs(index) do
136 if e.label=='Class' then
137 e.xarg.cname = string.gsub(e.xarg.fullname, '::', '_LQT_')
138 if class_is_public(e)
139 and not e.xarg.fullname:match'%b<>' then
140 classes[e] = true
141 elseif not e.xarg.fullname:match'%b<>' then
142 ignore(e.xarg.fullname, 'not public')
143 else
144 if templates.should_copy(e) then
145 templates.create(e, classes)
150 templates.finish(index)
151 for c in pairs(classes) do
152 classlist[c.xarg.cname] = c
156 function fix_methods_wrappers()
157 for c in pairs(classes) do
158 c.shell = c.public_destr
159 c.shell = c.shell and (next(c.virtuals)~=nil)
160 for _, constr in ipairs(c.constructors) do
161 if c.shell then
162 local shellname = 'lqt_shell_'..c.xarg.cname
163 constr.calling_line = 'new '..shellname..'(L'
164 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
165 else
166 local shellname = c.xarg.fullname
167 constr.calling_line = 'new '..shellname..'('
169 for i=1,#(constr.arguments) do
170 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
172 constr.calling_line = '*('..constr.calling_line .. '))'
173 constr.xarg.static = '1'
174 constr.return_type = constr.xarg.scope..'&'
176 if c.destructor then
177 c.destructor.return_type = nil
182 --- Determines, whether classes are children of QObject.
183 -- Fills the 'qobject' field on class if it is child of QObject.
184 function get_qobjects()
185 local function is_qobject(c)
186 if c==nil then return false end
187 if c.qobject then return true end
188 if c.xarg.fullname=='QObject' then
189 c.qobject = true
190 return true
192 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
193 local base = fullnames[b]
194 if is_qobject(base) then
195 --debug(c.xarg.fullname, "is a QObject")
196 c.qobject = true
197 return true
200 return false
202 for c in pairs(classes) do
203 local qobj = is_qobject(c)
209 local should_wrap = function(f)
210 local name = f.xarg.name
211 -- unfixed operator and friend, causes trouble with QDataStream
212 -- if f.xarg.friend and #f.arguments ==2 then return false end
213 -- not an operator - accept
214 if not name:match('^operator') then return true end
215 -- accept supported operators
216 if operators.is_operator(name) then return true end
217 return false
222 function distinguish_methods()
223 for c in pairs(classes) do
224 local construct, destruct, normal = {}, nil, {}
225 local n = c.xarg.name:gsub('%b<>', '')
227 local copy = nil
228 for _, f in ipairs(c) do
229 if n==f.xarg.name then
230 table.insert(construct, f)
231 elseif f.xarg.name:match'~' then
232 destruct = f
233 else
234 if should_wrap(f)
235 and (not f.xarg.member_template_parameters) then
236 table.insert(normal, f)
237 else
238 ignore(f.xarg.fullname, 'operator/template/friend', c.xarg.name)
242 c.constructors = construct
243 c.destructor = destruct
244 c.methods = normal
248 --- Determines, if a class has a public destructor. It also scans the superclasses.
249 -- Sets the 'public_destr' fiield on the class.
250 function fill_public_destr()
251 local function destr_is_public(c)
252 if c.destructor then
253 return c.destructor.xarg.access=='public'
254 else
255 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
256 local base = fullnames[b]
257 if base and not destr_is_public(base) then
258 return false
261 return true
264 for c in pairs(classes) do
265 c.public_destr = destr_is_public(c)
270 function generate_default_copy_constructor(c)
271 if not c.xarg then return end
273 local copy = {
274 [1] = {
275 label = "Argument";
276 xarg = {
277 context = c.xarg.name;
278 id = next_id();
279 name = "p";
280 scope = "";
281 type_base = c.xarg.name;
282 type_constant = "1";
283 type_name = c.xarg.name .. " const&";
284 type_reference = "1";
287 label = "Function";
288 return_type = c.xarg.name;
289 xarg = {
290 access = "public";
291 context = c.xarg.name;
292 fullname = c.xarg.name.."::"..c.xarg.name;
293 id = next_id();
294 inline = "1";
295 member_of = c.xarg.name;
296 member_of_class = c.xarg.name;
297 name = c.xarg.name;
298 scope = c.xarg.name;
299 type_base = c.xarg.name;
300 type_name = c.xarg.name;
303 copy.arguments = {copy[1]}
305 table.insert(c, copy)
306 table.insert(c.constructors, copy)
307 functions[copy] = true
309 return copy
312 -- HACK: do not create copy contructors for classes, that
313 -- contain variables of class '*Private' - they will not compile
314 -- in Qt 4.6
315 local function has_private_fields(c)
316 for _,v in ipairs(c) do
317 if v.label == 'Variable' then
318 if v.xarg.type_base:match('Private') then
319 ignore(c.xarg.fullname, 'cannot create copy constructor', v.xarg.fullname .. ' : ' ..v.xarg.type_base)
320 return true
324 return false
327 function fill_copy_constructor()
328 for c in pairs(classes) do
329 local copy = nil
330 for _, f in ipairs(c.constructors) do
331 if #(f.arguments)==1
332 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
333 copy = f
334 break
337 c.copy_constructor = copy
339 local function copy_constr_is_public(c)
340 if c.copy_constructor then
341 return (c.copy_constructor.xarg.access=='public')
342 or (c.copy_constructor.xarg.access=='protected')
343 else
344 if has_private_fields(c) then return false end
345 local ret = nil
346 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
347 local base = fullnames[b]
348 if base and not copy_constr_is_public(base) then
349 return false
352 return true
355 for c in pairs(classes) do
356 c.public_constr = copy_constr_is_public(c)
357 if c.public_constr and not c.copy_constructor then
358 c.copy_constructor = generate_default_copy_constructor(c)
363 function fill_implicit_constructor()
364 typesystem.can_convert = {}
365 for c in pairs(classes) do
366 for _,f in ipairs(c) do
367 if f.label:match("^Function") then
368 -- find non-explicit constructor, which has 1 argument of type different
369 -- from class, i.e. an implicit conversion constructor
370 if f.xarg.name == c.xarg.name
371 and #f == 1
372 and (not f.xarg.access or f.xarg.access == "public")
373 and f[1].xarg.type_base ~= c.xarg.name
374 and not f[1].xarg.type_base:match('Private$')
375 -- and not f.xarg.explicit
376 and not c.abstract
377 then
378 local class_name = c.xarg.cname
379 local from_type = f[1].xarg.type_base
380 typesystem.can_convert[class_name] = typesystem.can_convert[class_name] or { from = {}, class = c }
381 typesystem.can_convert[class_name].from[ from_type ] = f
382 local safe_from = from_type:gsub('[<>*]', '_'):gsub('::', '_LQT_')
389 local function generate_implicit_code(class_name, t)
390 local c = t.class
391 local fullname = c.xarg.fullname
392 local luaname = fullname:gsub('::', '.')
394 local test_header = 'bool lqt_canconvert_' .. class_name .. '(lua_State *L, int n)'
395 local convert_header = 'void* lqt_convert_' .. class_name .. '(lua_State *L, int n)'
397 local test_code = ""
398 local convert_code = ""
399 local tests = {}
401 local order = {}
402 for _, f in pairs(t.from) do
403 local typ = f[1].xarg.type_name
404 if not typesystem[typ] then
405 ignore(typ, "implicit constructor - unknown type", _)
406 else
407 local test
408 if typesystem[typ].raw_test then
409 test = typesystem[typ].raw_test('n')
410 else
411 test = typesystem[typ].test('n')
413 if not tests[test] then
414 tests[test] = true
415 local test_code =
416 ' if ('..test..')\n'
417 ..' return true;\n'
419 local newtype = fullname .. '(arg)'
420 if c.shell then newtype = 'lqt_shell_'..c.xarg.cname..'(L,arg)' end
422 -- HACK: QVariant with 'char const*' argument - force it to QByteArray
423 if fullname == 'QVariant' and typ == 'char const*' then
424 typ = 'QByteArray'
427 local convert_code =
428 ' if ('..test..') {\n'
429 ..' '..typ..' arg = '..typesystem[typ].get('n')..';\n'
430 ..' '..fullname..' *ret = new '..newtype..';\n'
431 ..' lqtL_passudata(L, ret, "'..luaname..'*");\n'
432 ..' return ret;\n }\n'
434 local item = { type = fullname, test = test, defect = typesystem[typ].defect or 0,
435 test_code = test_code, convert_code = convert_code }
436 table.insert(order, item)
441 table.sort(order, function(a,b) return a.defect < b.defect end)
442 for _,v in ipairs(order) do
443 test_code = test_code .. v.test_code
444 convert_code = convert_code .. v.convert_code
447 test_code = test_code .. ' return false;'
448 convert_code = convert_code..' return NULL;'
450 c.implicit = {
451 headers = { test = test_header, convert = convert_header },
452 test = test_header .. '{\n' .. test_code .. '\n}',
453 convert = convert_header .. '{\n' .. convert_code .. '\n}'
458 function fill_implicit_wrappers()
459 for class_name, t in pairs(typesystem.can_convert) do
460 if not t.class.abstract then
461 generate_implicit_code(class_name, t)
467 local put_class_in_filesystem = lqt.classes.insert
469 function fill_typesystem_with_classes()
470 for c in pairs(classes) do
471 classes[c] = put_class_in_filesystem(c.xarg.fullname)
476 function fill_wrapper_code(f)
477 if f.wrapper_code then return f end
478 local stackn, argn = 1, 1
479 local stack_args, defects = '', 0
480 local has_args = true
481 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
483 if f.xarg.abstract then
484 ignore(f.xarg.fullname, 'abstract method', f.xarg.member_of_class)
485 return nil
487 if f.xarg.member_of_class and f.xarg.static~='1' then
488 if not typesystem[f.xarg.member_of_class..'*'] then
489 ignore(f.xarg.fullname, 'not a member of wrapped class', f.xarg.member_of_class)
490 return nil
492 stack_args = stack_args .. typesystem[f.xarg.member_of_class..'*'].onstack
493 defects = defects + 7 -- FIXME: arbitrary
494 if f.xarg.constant=='1' then
495 defects = defects + 8 -- FIXME: arbitrary
497 local sget, sn = typesystem[f.xarg.member_of_class..'*'].get(stackn)
498 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
499 stackn = stackn + sn
500 wrap = wrap .. ' lqtL_selfcheck(L, self, "'..f.xarg.member_of_class..'");\n'
501 --print(sget, sn)
502 if operators.is_operator(f.xarg.name) then
503 line, has_args = operators.call_line(f)
504 if not line then return nil end
505 else
506 line = 'self->'..f.xarg.fullname..'('
508 if VERBOSE_BUILD then
509 wrap = wrap .. ' printf("[%lx; %p] %s :: %s (%d)\\n", ' ..
510 'QThread::currentThreadId(), self, ' ..
511 '"'..(f.xarg.member_of_class or f.xarg.fullname)..'", ' ..
512 '"'..f.xarg.name..'", '..
513 'oldtop);\n'
515 else
516 line = f.xarg.fullname..'('
517 if VERBOSE_BUILD then
518 wrap = wrap .. ' printf("[%lx; static] %s :: %s (%d)\\n", ' ..
519 'QThread::currentThreadId(), ' ..
520 '"'..(f.xarg.member_of_class or f.xarg.fullname)..'", ' ..
521 '"'..f.xarg.name..'", '..
522 'oldtop);\n'
525 for i, a in ipairs(f.arguments) do
526 if not typesystem[a.xarg.type_name] then
527 ignore(f.xarg.fullname, 'unkown argument type', a.xarg.type_name)
528 return nil
530 local aget, an, arg_as = typesystem[a.xarg.type_name].get(stackn)
531 stack_args = stack_args .. typesystem[a.xarg.type_name].onstack
532 if typesystem[a.xarg.type_name].defect then defects = defects + typesystem[a.xarg.type_name].defect end
533 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
534 if a.xarg.default=='1' and an>0 then
535 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
536 for j = stackn+1,stackn+an-1 do
537 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
539 local dv = a.xarg.defaultvalue
540 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
542 wrap = wrap .. aget .. ';\n'
543 line = line .. (argn==1 and 'arg' or ', arg') .. argn
544 stackn = stackn + an
545 argn = argn + 1
547 if has_args then
548 line = line .. ')'
550 -- FIXME: hack follows for constructors
551 if f.calling_line then line = f.calling_line end
552 if f.return_type then line = f.return_type .. ' ret = ' .. line end
553 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
554 if f.return_type then
555 if not typesystem[f.return_type] then
556 ignore(f.xarg.fullname, 'unknown return type', f.return_type)
557 return nil
559 local rput, rn = typesystem[f.return_type].push'ret'
560 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
561 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
562 else
563 wrap = wrap .. ' return 0;\n'
565 f.wrapper_code = wrap
566 f.stack_arguments = stack_args
567 f.defects = defects
568 return f
572 function fill_test_code(f)
573 local stackn = 1
574 local test = ''
575 if f.xarg.member_of_class and f.xarg.static~='1' then
576 if not typesystem[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
577 local stest, sn = typesystem[f.xarg.member_of_class..'*'].test(stackn)
578 test = test .. ' && ' .. stest
579 stackn = stackn + sn
581 for i, a in ipairs(f.arguments) do
582 if not typesystem[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
583 local atest, an = typesystem[a.xarg.type_name].test(stackn)
584 if a.xarg.default=='1' and an>0 then
585 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
586 test = test .. atest .. ')'
587 else
588 test = test .. ' && ' .. atest
590 stackn = stackn + an
592 -- can't make use of default values if I fix number of args
593 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
594 f.test_code = test
595 return f
600 function fill_wrappers()
601 for f in pairs(functions) do
602 local nf = fill_wrapper_code(f)
603 if nf then
604 nf = assert(fill_test_code(nf), nf.xarg.fullname) -- MUST pass
605 else
606 -- failed to generate wrapper
607 functions[f] = nil
612 ---- Output functions
614 function print_wrappers()
615 for c in pairs(classes) do
616 local meta = {}
617 local wrappers = ''
618 for _, f in ipairs(c.methods) do
619 if f.wrapper_code and not f.ignore then
620 if f.xarg.access~='private' then
621 local out = 'static int lqt_bind'..f.xarg.id
622 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
623 --print_meta(out)
624 wrappers = wrappers .. out .. '\n'
625 meta[f] = f.xarg.name
629 if not c.abstract then
630 for _, f in ipairs(c.constructors) do
631 if f.wrapper_code then
632 if f.xarg.access=='public' then
633 local out = 'static int lqt_bind'..f.xarg.id
634 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
635 --print_meta(out)
636 wrappers = wrappers .. out .. '\n'
637 meta[f] = 'new'
642 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
643 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
644 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
645 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
646 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
647 if c.public_destr then
648 out = out .. ' if (p) delete p;\n'
650 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
651 --print_meta(out)
652 wrappers = wrappers .. out .. '\n'
653 c.meta = meta
654 c.wrappers = wrappers
659 local print_metatable = function(c)
660 local methods = {}
661 local wrappers = c.wrappers
662 for m, n in pairs(c.meta) do
663 methods[n] = methods[n] or {}
664 table.insert(methods[n], m)
666 for n, l in pairs(methods) do
667 local duplicates = {}
668 for _, f in ipairs(l) do
669 if not f.ignore then
670 local itisnew = true
671 for sa, g in pairs(duplicates) do
672 if sa==f.stack_arguments then
673 --debug("function equal: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
674 if f.defects<g.defects then
675 else
676 ignore(f.xarg.fullname, "duplicate function", f.stack_arguments)
677 itisnew = false
679 elseif string.match(sa, "^"..f.stack_arguments) then -- there is already a version with more arguments
680 --debug("function superseded: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
681 elseif string.match(f.stack_arguments, '^'..sa) then -- there is already a version with less arguments
682 --debug("function superseding: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
685 if itisnew then
686 duplicates[f.stack_arguments] = f
690 --[[
691 local numinitial = 0
692 local numfinal = 0
693 for sa, f in pairs(l) do
694 numinitial = numinitial + 1
696 for sa, f in pairs(duplicates) do
697 numfinal = numfinal + 1
699 if numinitial-numfinal>0 then debug(c.xarg.fullname, "suppressed:", numinitial-numfinal) end
700 --]]
701 methods[n] = duplicates
703 for n, l in pairs(methods) do
704 local name = operators.rename_operator(n)
705 local disp = 'static int lqt_dispatcher_'..name..c.xarg.id..' (lua_State *L) {\n'
706 local testcode = {}
707 for tc, f in pairs(l) do
708 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
709 testcode[#testcode+1] = tc
711 -- disp = disp .. ' lua_settop(L, 0);\n'
712 disp = disp .. ' const char * args = lqtL_getarglist(L);\n'
713 disp = disp .. ' lua_pushfstring(L, "%s(%s): incorrect or extra arguments, expecting: %s.", "' ..
714 c.xarg.fullname..'::'..n..'", args, '..string.format("%q", table.concat(testcode, ' or ')) .. ');\n'
715 disp = disp .. ' return lua_error(L);\n}\n'
716 --print_meta(disp)
717 wrappers = wrappers .. disp .. '\n'
719 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
720 for n, l in pairs(methods) do
721 local nn = operators.rename_operator(n)
722 metatable = metatable .. ' { "'..nn..'", lqt_dispatcher_'..nn..c.xarg.id..' },\n'
724 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
725 metatable = metatable .. ' { 0, 0 },\n};\n'
726 --print_meta(metatable)
727 wrappers = wrappers .. metatable .. '\n'
728 local bases = ''
729 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
730 if not string.match(b, '^virtual') then
731 b = string.gsub(b, '^[^%s]* ', '')
732 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
735 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
736 --print_meta(bases)
737 wrappers = wrappers .. bases .. '\n'
738 c.wrappers = wrappers
739 return c
743 function print_metatables()
744 for c in pairs(classes) do
745 print_metatable(c)
750 function print_single_class(c)
751 local n = c.xarg.cname
752 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
753 local cppname = module_name..'_meta_'..n..'.cpp'
754 table.insert(cpp_files, n) -- global cpp_files
755 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
756 local print_meta = function(...)
757 fmeta:write(...)
758 fmeta:write'\n'
760 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
762 if c.implicit then
763 print_meta(c.implicit.test)
764 print_meta(c.implicit.convert)
767 print_meta(c.wrappers)
769 local virtual_methods
770 if c.shell then
771 virtual_methods = virtuals.sort_by_index(c)
772 local shellname = 'lqt_shell_'..c.xarg.cname
773 for _, v in ipairs(virtual_methods) do
774 if v.virtual_overload then
775 local method = string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
776 method = string.gsub(method, 'VIRTUAL_INDEX', v.virtual_index)
777 print_meta(method)
782 local getters_setters = 'NULL, NULL'
783 if c.properties then
784 print_meta(c.properties)
785 getters_setters = 'lqt_getters'..c.xarg.id..', lqt_setters'..c.xarg.id
788 local shellname = 'lqt_shell_'..n
790 local overrides = 'NULL'
791 if c.shell then
792 overrides = shellname..'::lqtAddOverride'
795 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
796 print_meta('\tlqtL_createclass(L, "'
797 ..lua_name..'*", lqt_metatable'
798 ..c.xarg.id..', '..getters_setters..', '..overrides..', lqt_base'
799 ..c.xarg.id..');')
801 if c.implicit then
802 print_meta('\tluaL_getmetatable(L, "'..lua_name..'*");')
803 print_meta('\tlua_pushliteral(L, "__test");')
804 print_meta('\tlua_pushlightuserdata(L, (void*)&lqt_canconvert_'..c.xarg.cname..');')
805 print_meta('\tlua_rawset(L, -3);')
806 print_meta('\tlua_pushliteral(L, "__convert");')
807 print_meta('\tlua_pushlightuserdata(L, (void*)&lqt_convert_'..c.xarg.cname..');')
808 print_meta('\tlua_rawset(L, -3);')
809 print_meta('\tlua_pop(L, 1);')
812 print_meta'\treturn 0;'
813 print_meta'}'
814 print_meta''
816 if c.shell then
817 print_meta('int '..shellname..'::lqtAddOverride(lua_State *L) {')
818 print_meta(' '..shellname..' *self = static_cast<'..shellname..'*>('..typesystem[c.xarg.fullname..'*'].get(1)..');')
819 print_meta(' const char *name = luaL_checkstring(L, 2);')
820 if VERBOSE_BUILD then
821 print_meta(' printf("Overriding \'%s\' in %s [%p]\\n", name, "'..shellname..'", self);')
823 for _, v in ipairs(virtual_methods) do
824 print_meta(' if (!strcmp(name, "'..v.xarg.name..'")) {')
825 print_meta(' self->hasOverride.setBit('..v.virtual_index..');')
826 if VERBOSE_BUILD then
827 print_meta(' printf("-> updated %d to %d\\n", '..v.virtual_index..', (bool)self->hasOverride['..v.virtual_index..']);')
829 print_meta(' return 0;')
830 print_meta(' }')
832 print_meta('}\n\n')
835 if c.shell and c.qobject then
836 print_meta([[
837 #include <QDebug>
839 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
841 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
842 //int oldtop = lua_gettop(L);
843 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
844 lua_getfield(L, -1, LQT_OBJMETASTRING);
845 if (lua_isnil(L, -1)) {
846 lua_pop(L, 2);
847 return &]]..c.xarg.fullname..[[::staticMetaObject;
849 lua_getfield(L, -2, LQT_OBJMETADATA);
850 lqtL_touintarray(L);
851 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
852 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
853 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
854 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
855 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
856 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
857 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
858 lua_pop(L, 1);
859 //qDebug() << (lua_gettop(L) - oldtop);
860 return &lqt_shell_]]..n..[[::staticMetaObject;
863 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
864 //qDebug() << "fake calling!";
865 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
866 if (index < 0) return index;
867 return lqtL_qt_metacall(L, this, lqtSlotAcceptor_]]..module_name..[[, call, "]]..c.xarg.fullname..[[*", index, args);
871 fmeta:close()
874 function print_merged_build()
875 local path = module_name.._src
876 local mergename = module_name..'_merged_build'
877 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
878 for _, p in ipairs(cpp_files) do
879 merged:write('#include "',module_name,'_head_',p,'.hpp"\n')
881 merged:write('\n')
882 for _, p in ipairs(cpp_files) do
883 merged:write('#include "',module_name,'_meta_',p,'.cpp"\n')
885 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
887 local print_pro= function(...)
888 pro_file:write(...)
889 pro_file:write'\n'
891 print_pro('TEMPLATE = lib')
892 print_pro('TARGET = '..module_name)
893 print_pro('INCLUDEPATH += .')
894 print_pro('HEADERS += '..module_name..'_slot.hpp')
895 print_pro('SOURCES += ../common/lqt_common.cpp \\')
896 print_pro(' ../common/lqt_qt.cpp \\')
897 print_pro(' '..module_name..'_enum.cpp \\')
898 print_pro(' '..module_name..'_meta.cpp \\')
899 print_pro(' '..module_name..'_slot.cpp \\')
900 print_pro(' '..mergename..'.cpp')
904 function print_class_list()
905 local qobject_present = false
906 local big_picture = {}
907 local type_list_t = {}
908 for c in pairs(classes) do
909 local n = c.xarg.cname
910 if n=='QObject' then qobject_present = true end
911 print_single_class(c)
912 table.insert(big_picture, n)
913 table.insert(type_list_t, 'add_class \''..c.xarg.fullname..'\'\n')
916 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
917 type_list_f:write([[
918 #!/usr/bin/lua
919 local types = (...) or {}
920 assert(lqt.classes.insert, 'module lqt.classes not loaded')
921 local function add_class(class)
922 lqt.classes.insert(class, true)
925 for k, v in ipairs(type_list_t) do
926 type_list_f:write(v)
928 type_list_f:write('return types\n')
929 type_list_f:close()
931 print_merged_build()
932 local fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
933 local print_meta = function(...)
934 fmeta:write(...)
935 fmeta:write'\n'
937 print_meta()
938 print_meta('#include "lqt_common.hpp"')
939 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
940 for _, p in ipairs(big_picture) do
941 print_meta('extern "C" LQT_EXPORT int luaopen_'..p..' (lua_State *);')
943 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
944 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
945 for _, p in ipairs(big_picture) do
946 print_meta('\tluaopen_'..p..'(L);')
948 print_meta('\tlqt_create_enums_'..module_name..'(L);')
949 if qobject_present then
950 print_meta('\tlqtL_qobject_custom(L);')
952 if module_name == "qtcore" then
953 print_meta("\tlqtL_qvariant_custom(L);")
954 elseif module_name == "qtgui" then
955 print_meta("\tlqtL_qvariant_custom_qtgui(L);")
957 print_meta('\tlqtL_register_super(L);')
958 print_meta('\tlqtSlotAcceptor_'..module_name..' = new LqtSlotAcceptor(L);')
959 print_meta('\treturn 0;\n}')
960 if fmeta then fmeta:close() end
963 ------------------------------------------------------------
965 function preprocess(index)
966 copy_classes(index) -- picks classes if not private and not blacklisted
967 copy_functions(index) -- picks functions and fixes label
968 fix_arguments(index) -- fixes default arguments if they are context-relative
969 fix_functions() -- fixes name and fullname and fills arguments
970 operators.fix_operators(index)
973 function process(index, typesystem, filterfiles)
974 for _, f in ipairs(filterfiles) do
975 classes = loadfile(f)(classes)
978 virtuals.fill_virtuals(classes) -- does that, destructor ("~") excluded
979 distinguish_methods() -- does that
980 fill_public_destr() -- does that: checks if destructor is public
981 fill_copy_constructor() -- does that: checks if copy contructor is public or protected
982 fill_implicit_constructor()
983 fix_methods_wrappers()
984 get_qobjects()
986 fill_typesystem_with_classes()
987 fill_wrappers()
988 virtuals.fill_virtual_overloads(classes) -- does that
989 virtuals.fill_shell_classes(classes) -- does that
990 properties.fill_properties(classes)
991 fill_implicit_wrappers()
993 signalslot.process(functions)
996 function output()
997 virtuals.print_shell_classes(classes) -- does that, and outputs headers
999 print_wrappers(classes) -- just compiles metatable list
1000 print_metatables(classes) -- just collects the wrappers + generates dispatchers
1001 print_class_list(classes) -- does that + prints everything related to class
1003 signalslot.output()