various fixes
[lqt.git] / lqt_common.cpp
blob345538f77886b013d7a76a1edb7121d1e7e9e853
1 /*
2 * Copyright (c) 2007-2008 Mauro Iazzi
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
11 * conditions:
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
27 #include "lqt_common.hpp"
28 #include <cstring>
30 #define LQT_FIXEDINDEX(i) (i<0?(1+lua_gettop(L)+i):i)
31 #define LQT_MAX_ARGS 50
33 //#include <QDebug>
34 #ifndef SEE_STACK
35 # define SEE_STACK(L, j) for (int j=1;j<=lua_gettop(L);j++) { qDebug() << j << '=' << luaL_typename(L, j) << '@' << lua_topointer (L, j); }
36 #endif
38 // TODO
40 // - add a metatable for void* which always results in nil and possibly
41 // accounts for reassigning the userdata to the right metatable.
42 // - proper descending down the dependency tree for indexing, and proper
43 // handling of missing binds
47 //#include <iostream>
48 //using namespace std;
50 int check_gc(lua_State*L){
51 lua_newtable(L);
52 lua_getglobal(L, "print");
53 lua_setfield(L, -2, "__gc");
54 lua_setmetatable(L, -2);
55 return 0;
58 void *get_buffer(lua_State *L, size_t sz) {
59 #if 1
60 void *ret = lua_newuserdata(L, sz);
61 //check_gc(L);
62 lua_pushlightuserdata(L, ret);
63 lua_insert(L, -2);
64 lua_settable(L, LUA_REGISTRYINDEX);
65 #else
66 void *ret = malloc(sz);
67 cout << ret << endl;
68 #endif
69 return ret;
72 int& lqtL_tointref (lua_State *L, int i) {
73 i = LQT_FIXEDINDEX(i);
74 int *ret = NULL;
75 ret = (int*)get_buffer(L, sizeof(int));
76 *ret = lua_type(L, i)==LUA_TNUMBER?lua_tointeger(L, i):0;
77 //cout << "interef " << ret << endl;
78 return *ret;
80 void lqtL_pusharguments (lua_State *L, char **argv) {
81 int i = 0;
82 lua_newtable(L);
83 for (i=0;*argv && i<LQT_MAX_ARGS;argv++,i++) {
84 lua_pushstring(L, *argv);
85 lua_rawseti(L, -2, i+1);
87 return;
89 char** lqtL_toarguments (lua_State *L, int index) {
90 index = LQT_FIXEDINDEX(index);
91 char **ret;
92 const char *str;
93 int retlen = 0;
94 size_t strlen = 0;
95 int i = 0;
96 retlen = lua_objlen(L, index);
97 ret = (char**)get_buffer(L, sizeof(char*)*(retlen+1));
98 //cout << retlen << endl;
99 for (i=0;i<retlen;i++) {
100 lua_rawgeti(L, index, i+1);
101 if (lua_isstring(L, -1)) {
102 str = lua_tolstring(L, -1, &strlen);
103 ret[i] = (char*)get_buffer(L, sizeof(char)*(strlen+1));
104 strncpy(ret[i], str, strlen+1);
105 //cout << "arg " << (void*)ret[i] << ' ' << ret[i] << endl;
106 } else {
107 strlen = 0;
108 str = "\0";
109 ret[i] = (char*)get_buffer(L, sizeof(char)*(strlen+1));
110 strncpy(ret[i], str, strlen+1);
111 //cout << "Zarg " << (void*)ret[i] << ' ' << ret[i] << endl;
113 lua_pop(L, 1);
115 ret[retlen] = NULL;
116 //cout << "arg[] " << ret << endl;
117 return ret;
119 bool lqtL_testarguments (lua_State *L, int index) {
120 return (bool)lua_istable(L, index);
123 void lqtL_setvoidmetatable (lua_State *L, int i, const char *t = 0) {
124 i = LQT_FIXEDINDEX(i);
125 if (luaL_newmetatable(L, "void*")) {
126 lua_newtable(L);
127 lua_setfield(L, -2, "__index");
128 lua_pushstring(L, "void*");
129 lua_setfield(L, -2, "__qtype");
131 lua_setmetatable(L, i);
132 if (t!=0) {
133 lua_newtable(L);
134 lua_pushstring(L, t);
135 lua_setfield(L, -2, "__unknown_type");
136 lua_setfenv(L, i);
140 void lqtL_getpointers (lua_State *L) {
141 lua_getfield(L, LUA_REGISTRYINDEX, LQT_POINTERS);
142 if (!lua_istable(L, -1)) {
143 //cout << "Registry Pointers created" << endl;
144 lua_pop(L, 1);
145 lua_newtable(L);
146 lua_newtable(L);
147 lua_pushstring(L, "kv");
148 lua_setfield(L, -2, "__mode");
149 lua_setmetatable(L, -2);
150 lua_pushvalue(L, -1);
151 lua_setfield(L, LUA_REGISTRYINDEX, LQT_POINTERS);
153 return;
156 int lqtL_reusepointer (lua_State *L, const void *p, const char *t) {
157 lqtL_getpointers(L);
158 lua_pushlightuserdata(L, const_cast<void*>(p));
159 lua_gettable(L, -2);
160 if (lqtL_testudata(L, -1, t) && (p==*(const void**)lua_touserdata(L, -1))) {
161 lua_remove(L, -2);
162 //qDebug();
163 //qDebug() << "reused" << p << "in" << lua_touserdata(L, -1) << t;
164 //lua_getmetatable(L, -1);luaL_getmetatable(L, t);
165 //qDebug() << "meta" << lua_topointer(L, -2) << "type" << lua_topointer(L, -1);
166 //lua_pop(L, 2);
167 return 1;
169 lua_pop(L, 2);
170 return 0;
173 void lqtL_pushpointer (lua_State *L, const void *obj, const char *t) {
174 const void **objp = (const void**)lua_newuserdata(L, sizeof(const void *));
175 *objp = 0;
176 lua_getfield(L, LUA_REGISTRYINDEX, t);
177 if (lua_istable(L, -1)) {
178 lua_setmetatable(L, -2);
179 lua_newtable(L);
180 lua_setfenv(L, -2);
181 } else {
182 //cout << "NO metatable given" << endl;
183 // TODO: add a fallback?
184 lua_pop(L, 1);
185 lqtL_setvoidmetatable(L, -1, t);
187 *objp = obj;
188 //cout << "pushed " << objp << ' ' << obj << endl;
191 void lqtL_setpointer (lua_State *L, int i, const void *obj) {
192 int index = i<0?(1+lua_gettop(L)+i):i;
193 //cout << lua_gettop(L) << ' ' << i << ' ' << index << endl;
194 lqtL_getpointers(L);
195 lua_pushlightuserdata(L, const_cast<void *>(obj));
196 lua_pushvalue(L, index);
197 lua_settable(L, -3);
198 lua_pop(L, 1);
199 return;
201 void lqtL_unsetpointer (lua_State *L, const void *obj) {
202 lqtL_getpointers(L);
203 lua_pushlightuserdata(L, const_cast<void *>(obj));
204 lua_pushnil(L);
205 lua_settable(L, -3);
206 lua_pop(L, 1);
207 return;
210 // TODO: don't need, it's debug
211 void * lqtL_topointer (lua_State *L, int i) {
212 void * ret = NULL;
213 ret = lua_touserdata(L, i);
214 ret = (ret==0)?0:*static_cast<void**>(ret);
215 return ret;
218 void lqtL_unmanageudata (lua_State *L, int i) {
219 if (!lua_isuserdata(L, i)) return;
220 lua_getfenv(L, i);
221 if (lua_istable(L, -1)) {
222 lua_pushnil(L);
223 lua_setfield(L, -2, "__gc");
225 lua_pop(L, 1);
226 return;
228 void lqtL_manageudata (lua_State *L, int index) {
229 //luaL_checkstack(L, 20, "");
230 index = LQT_FIXEDINDEX(index);
231 if (!lua_isuserdata(L, index)) return;
232 lua_getfield(L, index, "delete");
233 if (!lua_isfunction(L, -1)) {
234 lua_pop(L, 1);
235 return;
237 lua_getfenv(L, index);
238 if (!lua_istable(L, -1)) {
239 lua_pop(L, 1);
240 //if (1) {
241 lua_newtable(L);
242 lua_pushvalue(L, -1);
243 lua_setfenv(L, index);
245 lua_insert(L, -2);
246 lua_setfield(L, -2, "__gc");
247 lua_pop(L, 1);
248 return;
250 void lqtL_pushudata (lua_State *L, const void *obj, const char *t) {
251 // TODO: make the udata unique
252 //cout << endl << "pushing udata " << obj << " with type " << t << endl;
254 lua_checkstack(L, 5);
255 if (1) {
256 if (lqtL_reusepointer(L, obj, t)) {
257 //cout << obj << " reused " << t << endl;
258 return;
260 //cout << obj << " NOT reused " << t << endl;
261 } else {
262 lqtL_getpointers(L);
263 lua_pushlightuserdata(L, const_cast<void *>(obj));
264 lua_gettable(L, -2);
265 if (0) {
266 //cout << lua_gettop(L) << endl;
267 lua_getglobal(L, "print");
268 if (lua_getmetatable(L, -2)) {
269 //cout << "metatable "; // << lua_tostring(L, -1) << endl << endl;
270 lua_call(L, 1, 0);
271 } else {
272 //cout << "no metatable" << endl;
273 lua_pop(L, 1);
275 //cout << lua_gettop(L) << endl;
276 lua_getglobal(L, "print");
277 lua_getfield(L, LUA_REGISTRYINDEX, t);
278 //cout << "registry "; // << lua_tostring(L, -1) << endl; lua_pop(L, 1);
279 lua_call(L, 1, 0);
280 } else if (0) {
281 //cout << lua_gettop(L) << endl;
282 if (lua_getmetatable(L, -1)) {
283 lua_getfield(L, LUA_REGISTRYINDEX, t);
284 //cout << (bool)lua_equal(L, -1, -2) << endl;
285 lua_getglobal(L, "print");
286 lua_insert(L, -3);
287 lua_call(L, 2, 0);
288 } else {
289 //cout << "no metatable" << endl;
290 lua_pop(L, 1);
293 if (lqtL_testudata(L, -1, t)) {
294 //cout << obj << " reused" << endl;
295 lua_remove(L, -2);
296 return;
297 } else {
298 //cout << luaL_typename(L, -1) << " testudata failed " << lua_touserdata(L, -1) << ' ' << lqtL_topointer(L, -1) << endl;
300 lua_pop(L, 2);
303 if (1) {
304 lqtL_pushpointer(L, obj, t);
305 } else {
306 const void **objp = (const void**)lua_newuserdata(L, sizeof(const void *));
307 *objp = 0;
308 lua_getfield(L, LUA_REGISTRYINDEX, t);
309 if (lua_istable(L, -1)) {
310 lua_setmetatable(L, -2);
311 } else {
312 //cout << "NO metatable given" << endl;
313 lua_pop(L, 1);
314 // TODO: add a fallback?
316 *objp = obj;
317 //cout << "pushed " << objp << ' ' << obj << endl;
319 //cout << lua_gettop(L) << endl;
320 if (1) {
321 lqtL_setpointer(L, -1, obj);
322 } else {
323 lua_getfield(L, LUA_REGISTRYINDEX, LQT_POINTERS);
324 lua_pushlightuserdata(L, const_cast<void *>(obj));
325 lua_pushvalue(L, -3);
326 lua_settable(L, -3);
327 lua_pop(L, 1);
329 //cout << lua_gettop(L) << endl;
331 void lqtL_passudata (lua_State *L, const void *obj, const char *t) {
332 //cout << "passing: " << obj << " " << t << endl;
333 lqtL_pushudata(L, obj, t);
334 lqtL_manageudata(L, -1);
338 int lqtL_derivesfrom (lua_State *L, int i, const char *t) {
339 int ret = 0;
340 int oldtop = lua_gettop(L);
341 i = LQT_FIXEDINDEX(i);
343 if (!lua_istable(L, i)) {
344 return 0;
347 luaL_checkstack(L, 1, "cannot grow stack for checking object type");
349 //qDebug() << "given a table" << lua_topointer(L, i);
350 lua_getfield(L, LUA_REGISTRYINDEX, t);
351 //qDebug() << "table" << t << lua_topointer(L, -1);
352 ret = lua_equal(L, i, -1);
353 //qDebug() << "same as" << t << "?" << (bool)ret;
356 lua_settop(L, oldtop);
358 return ret;
359 // */
361 if (!ret) {
362 lua_pop(L, 1);
363 luaL_checkstack(L, 13, "cannot grow stack for checking object type"); // FIXME: is it enough?
364 lua_getfield(L, i, "__base");
366 if (!lua_istable(L, -1)) {
367 lua_pop(L, 1);
368 ret = 0;
369 } else {
370 lua_getfield(L, -1, t);
371 ret = lua_isnil(L, -1)?0:1;
372 //if (ret) qDebug() << (lua_toboolean(L, -1)?"directly":"INDIRECTLY") << "derives from" << t;
373 // DANGER: order of control expression is important
374 while ((ret == 0) && (lua_next(L, -2) != 0)) {
375 if (!lua_istable(L, -1)) {
376 lua_pop(L, 1);
377 lua_pushvalue(L, -1);
378 lua_gettable(L, LUA_REGISTRYINDEX);
379 if (lua_istable(L, -1)) {
380 lua_pushvalue(L, -2);
381 lua_pushvalue(L, -2);
382 lua_settable(L, -5);
385 if (lua_istable(L, -1)) {
386 ret = lqtL_derivesfrom(L, -1, t);
388 lua_pop(L, 1);
393 lua_settop(L, oldtop);
395 return ret;
398 bool lqtL_testudata (lua_State *L, int i, const char *t) {
399 //qDebug() << "================ testudata" << t;
400 //luaL_checkstack(L, 99, "");
401 i = LQT_FIXEDINDEX(i);
402 bool ret = false;
403 if (lua_getmetatable(L, i)) {
404 //SEE_STACK(L, pippo);
405 //SEE_STACK(L, pippo);
406 if (0) {
407 lua_getfield(L, LUA_REGISTRYINDEX, "QEvent*");
408 if (0 && lua_istable(L, -1)) {
409 lua_getfield(L, -1, "__qtype");
410 //SEE_STACK(L, pippo);
411 lua_pop(L, 1);
413 lua_pop(L, 2);
415 ret = (bool) lqtL_derivesfrom(L, -1, t);
417 //qDebug() << "derives?" << ret;
418 if (!ret && lqtL_derivesfrom(L, -1, "void*")) {
419 //cout << "checking for a generic void* pointer" << endl;
421 luaL_checkstack(L, 3, "cannot check void* real type");
422 lua_getfenv(L, i);
423 lua_getfield(L, -1, "__unknown_type");
424 //TODO: assign dynamically?
425 lua_pushstring(L, t);
426 ret = (bool)lua_equal(L, -1, -2);
427 lua_pop(L, 3);
428 // */
429 // FIXME: deleting makes QMetaObjects not work
430 //ret = true;
432 lua_pop(L, 1);
433 } else {
435 //cout << t << (ret?"":" NOT") <<" found" << endl;
436 return ret;
438 bool ret = false;
439 int oldtop = lua_gettop(L);
440 lua_getfield(L, LUA_REGISTRYINDEX, t);
441 if (lua_getmetatable(L, i)) {
442 ret = (bool)lua_equal(L, -1, -2);
443 if (!ret) {
444 lua_remove(L, -2);
445 ret = lqTL_derivesfrom(L, -1, t);
448 lua_settop(L, oldtop);
449 return ret;
454 int lqtL_pushtype (lua_State *L, int i) {
455 int type = lua_type(L, i);
456 if (type != LUA_TUSERDATA) {
457 lua_pushstring(L, lua_typename(L, type));
458 } else {
459 lua_getfield(L, i, "__qtype");
460 if (!lua_isstring(L, -1)) {
461 lua_pop(L, 1);
462 lua_pushstring(L, "<unknown>");
465 return 1;
468 void * lqtL_checkudata (lua_State *L, int i, const char *t) {
469 if (lqtL_testudata(L, i, t)) {
470 return lua_touserdata(L, i);
471 } else {
472 lua_pushstring(L, "Fatal error: userdata type mismatch. requested ");
473 lua_pushstring(L, t);
474 lua_pushstring(L, " found ");
475 lqtL_pushtype(L, i);
476 lua_concat(L, 4);
477 lua_error(L);
479 return 0;
481 //*/
482 void * lqtL_toudata (lua_State *L, int i, const char *t) {
483 void * ret = NULL;
484 if (lqtL_testudata(L, i, t)) {
485 ret = *static_cast<void**>(lua_touserdata(L, i));
487 return ret;
490 void lqtL_pushenum (lua_State *L, int v, const char *e) {
491 lua_getfield(L, LUA_REGISTRYINDEX, LQT_ENUMS);
492 if (lua_istable(L, -1)) {
493 //qDebug() << "LQT_ENUMS is a table";
494 lua_getfield(L, -1, e);
495 lua_remove(L, -2);
496 } else {
497 //qDebug() << "LQT_ENUMS is NOT a table";
499 lua_pushinteger(L, v);
500 if (lua_istable(L, -2)) {
501 //qDebug() << "getting translation";
502 lua_gettable(L, -2);
503 } else {
504 //qDebug() << "no translation table for" << e;
506 lua_remove(L, -2);
508 bool lqtL_isenum (lua_State *L, int i, const char *e) {
509 bool ret = false;
510 if (lua_type(L, i)==LUA_TNUMBER) {
511 ret = (lua_tointeger(L, i)==lua_tonumber(L, i));
512 } else if (lua_type(L, i)==LUA_TSTRING) {
513 lua_getfield(L, LUA_REGISTRYINDEX, LQT_ENUMS);
514 if (lua_istable(L, -1)) {
515 lua_getfield(L, -1, e);
516 lua_remove(L, -2);
518 lua_pushvalue(L, i);
519 if (lua_istable(L, -2)) {
520 lua_gettable(L, -2);
522 if (lua_type(L, -1)==LUA_TNUMBER) {
523 ret = (lua_tointeger(L, -1)==lua_tonumber(L, -1));
525 lua_pop(L, 2);
527 return ret;
529 int lqtL_toenum (lua_State *L, int i, const char *e) {
530 int ret = -1;
531 if (lua_type(L, i)==LUA_TNUMBER) {
532 ret = lua_tointeger(L, i);
533 } else if (lua_type(L, i)==LUA_TSTRING) {
534 lua_getfield(L, LUA_REGISTRYINDEX, LQT_ENUMS);
535 if (lua_istable(L, -1)) {
536 lua_getfield(L, -1, e);
537 lua_remove(L, -2);
539 lua_pushvalue(L, i);
540 if (lua_istable(L, -2)) {
541 lua_gettable(L, -2);
543 if (lua_type(L, -1)==LUA_TNUMBER) {
544 ret = lua_tointeger(L, -1);
546 lua_pop(L, 2);
548 return ret;
551 int lqtL_baseindex (lua_State *L, int index, int key) {
552 int ret = 0;
553 int oldtop = lua_gettop(L);
554 index = LQT_FIXEDINDEX(index);
555 key = LQT_FIXEDINDEX(key);
557 if (!lua_istable(L, index)) {
558 return 0;
561 luaL_checkstack(L, 1, "cannot grow stack for retrieving member");
563 lua_pushvalue(L, key);
564 lua_gettable(L, index);
566 if (!lua_isnil(L, -1)) {
567 ret = 1;
568 } else {
569 luaL_checkstack(L, 7, "cannot grow stack for retrieving member"); // FIXME: is it enough?
570 lua_getfield(L, index, "__base");
572 if (!lua_istable(L, -1)) {
573 lua_pop(L, 2);
574 ret = 0;
575 } else {
576 lua_insert(L, -2);
577 // DANGER: order of control expression is important
578 while ((ret == 0) && (lua_next(L, -2) != 0)) {
579 if (!lua_istable(L, -1) && lua_isboolean(L, -1) && lua_toboolean(L, -1)) {
580 lua_pop(L, 1);
581 lua_pushvalue(L, -1);
582 lua_gettable(L, LUA_REGISTRYINDEX);
583 if (lua_istable(L, -1)) {
584 lua_pushvalue(L, -2);
585 lua_pushvalue(L, -2);
586 lua_settable(L, -5);
589 if (lua_istable(L, -1)) {
590 ret = lqtL_baseindex(L, -1, key);
592 if (ret == 0) {
593 lua_pop(L, 1);
599 if (ret != 0) {
600 lua_insert(L, oldtop+1);
602 lua_settop(L, oldtop+ret);
604 return ret;
607 int lqtL_index (lua_State *L) {
608 lua_settop(L, 2);
609 luaL_checkstack(L, 1, "cannot grow stack for retrieving member");
610 if (!lua_getmetatable(L, 1)) {
611 return 0;
614 return lqtL_baseindex(L, 3, 2);
616 luaL_checkstack(L, 2, "cannot grow stack for retrieving member");
617 lua_getmetatable(L, 1);
618 if (lua_istable(L, -1)) {
619 lua_pushvalue(L, 2);
620 lua_gettable(L, -2);
621 } else {
622 lua_pushnil(L);
624 lua_remove(L, -2);
625 return 1;
629 int lqtL_newindex (lua_State *L) {
630 if (!lua_getmetatable(L, 1)) {
631 return 0;
633 if (lua_istable(L, -1)) {
634 lua_pushvalue(L, 2);
635 lua_pushvalue(L, 3);
636 lua_settable(L, -3);
637 lua_pop(L, 1);
639 return 0;
642 int lqtL_gc (lua_State *L) {
643 if (!lua_isuserdata(L, 1)) return 0;
645 lua_getfenv(L, 1);
646 if (lua_istable(L, -1)) {
647 lua_getfield(L, -1, "__gc");
648 if (lua_isfunction(L, -1)) {
649 //cout << "found fenv gc " << lua_topointer(L, -1) << endl;
650 lua_pushvalue(L, 1);
651 lua_call(L, 1, 0);
652 } else {
653 //cout << "NOT found fenv gc" << endl;
654 lua_pop(L, 1);
657 lua_pop(L, 1);
659 // FIXME: this is useless, isn't it?
660 void **p = static_cast<void**>(lua_touserdata(L, 1));
661 *p = 0;
663 return 0;