Tizen 2.0 Release
[external/tolua++.git] / tolua_map.c
1 /* tolua: functions to map features
2 ** Support code for Lua bindings.
3 ** Written by Waldemar Celes
4 ** TeCGraf/PUC-Rio
5 ** Apr 2003
6 ** $Id: $
7 */
8
9 /* This code is free software; you can redistribute it and/or modify it.
10 ** The software provided hereunder is on an "as is" basis, and
11 ** the author has no obligation to provide maintenance, support, updates,
12 ** enhancements, or modifications.
13 */
14
15 #include "tolua++.h"
16 #include "tolua_event.h"
17 #include "lauxlib.h"
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <math.h>
23
24
25 /* Create metatable
26         * Create and register new metatable
27 */
28 static int tolua_newmetatable (lua_State* L, char* name)
29 {
30         int r = luaL_newmetatable(L,name);
31
32         #ifdef LUA_VERSION_NUM /* only lua 5.1 */
33         if (r) {
34                 lua_pushvalue(L, -1);
35                 lua_pushstring(L, name);
36                 lua_settable(L, LUA_REGISTRYINDEX); /* reg[mt] = type_name */
37         };
38         #endif
39
40         if (r)
41                 tolua_classevents(L); /* set meta events */
42         lua_pop(L,1);
43         return r;
44 }
45
46 /* Map super classes
47         * It sets 'name' as being also a 'base', mapping all super classes of 'base' in 'name'
48 */
49 static void mapsuper (lua_State* L, const char* name, const char* base)
50 {
51         /* push registry.super */
52  lua_pushstring(L,"tolua_super");
53  lua_rawget(L,LUA_REGISTRYINDEX);    /* stack: super */
54         luaL_getmetatable(L,name);          /* stack: super mt */
55  lua_rawget(L,-2);                   /* stack: super table */
56         if (lua_isnil(L,-1))
57         {
58          /* create table */
59                 lua_pop(L,1);
60          lua_newtable(L);                    /* stack: super table */
61          luaL_getmetatable(L,name);          /* stack: super table mt */
62                 lua_pushvalue(L,-2);                /* stack: super table mt table */
63                 lua_rawset(L,-4);                   /* stack: super table */
64         }
65
66         /* set base as super class */
67         lua_pushstring(L,base);
68         lua_pushboolean(L,1);
69         lua_rawset(L,-3);                    /* stack: super table */
70
71         /* set all super class of base as super class of name */
72         luaL_getmetatable(L,base);          /* stack: super table base_mt */
73         lua_rawget(L,-3);                   /* stack: super table base_table */
74         if (lua_istable(L,-1))
75         {
76                 /* traverse base table */
77                 lua_pushnil(L);  /* first key */
78                 while (lua_next(L,-2) != 0)
79                 {
80                         /* stack: ... base_table key value */
81                         lua_pushvalue(L,-2);    /* stack: ... base_table key value key */
82                         lua_insert(L,-2);       /* stack: ... base_table key key value */
83                         lua_rawset(L,-5);       /* stack: ... base_table key */
84                 }
85         }
86         lua_pop(L,3);                       /* stack: <empty> */
87 }
88
89 /* creates a 'tolua_ubox' table for base clases, and
90 // expects the metatable and base metatable on the stack */
91 static void set_ubox(lua_State* L) {
92
93         /* mt basemt */
94         if (!lua_isnil(L, -1)) {
95                 lua_pushstring(L, "tolua_ubox");
96                 lua_rawget(L,-2);
97         } else {
98                 lua_pushnil(L);
99         };
100         /* mt basemt base_ubox */
101         if (!lua_isnil(L,-1)) {
102                 lua_pushstring(L, "tolua_ubox");
103                 lua_insert(L, -2);
104                 /* mt basemt key ubox */
105                 lua_rawset(L,-4);
106                 /* (mt with ubox) basemt */
107         } else {
108                 /* mt basemt nil */
109                 lua_pop(L, 1);
110                 lua_pushstring(L,"tolua_ubox"); lua_newtable(L);
111                 /* make weak value metatable for ubox table to allow userdata to be
112                 garbage-collected */
113                 lua_newtable(L); lua_pushliteral(L, "__mode"); lua_pushliteral(L, "v"); lua_rawset(L, -3);               /* stack: string ubox mt */
114                 lua_setmetatable(L, -2);  /* stack:mt basemt string ubox */
115                 lua_rawset(L,-4);
116         };
117
118 };
119
120 /* Map inheritance
121         * It sets 'name' as derived from 'base' by setting 'base' as metatable of 'name'
122 */
123 static void mapinheritance (lua_State* L, const char* name, const char* base)
124 {
125         /* set metatable inheritance */
126         luaL_getmetatable(L,name);
127
128         if (base && *base)
129                 luaL_getmetatable(L,base);
130         else {
131
132                 if (lua_getmetatable(L, -1)) { /* already has a mt, we don't overwrite it */
133                         lua_pop(L, 2);
134                         return;
135                 };
136                 luaL_getmetatable(L,"tolua_commonclass");
137         };
138
139         set_ubox(L);
140
141         lua_setmetatable(L,-2);
142         lua_pop(L,1);
143 }
144
145 /* Object type
146 */
147 static int tolua_bnd_type (lua_State* L)
148 {
149         tolua_typename(L,lua_gettop(L));
150         return 1;
151 }
152
153 /* Take ownership
154 */
155 static int tolua_bnd_takeownership (lua_State* L)
156 {
157         int success = 0;
158         if (lua_isuserdata(L,1))
159         {
160                 if (lua_getmetatable(L,1))        /* if metatable? */
161                 {
162                         lua_pop(L,1);             /* clear metatable off stack */
163                         /* force garbage collection to avoid C to reuse a to-be-collected address */
164                         #ifdef LUA_VERSION_NUM
165                         lua_gc(L, LUA_GCCOLLECT, 0);
166                         #else
167                         lua_setgcthreshold(L,0);
168                         #endif
169
170                         success = tolua_register_gc(L,1);
171                 }
172         }
173         lua_pushboolean(L,success!=0);
174         return 1;
175 }
176
177 /* Release ownership
178 */
179 static int tolua_bnd_releaseownership (lua_State* L)
180 {
181         int done = 0;
182         if (lua_isuserdata(L,1))
183         {
184                 void* u = *((void**)lua_touserdata(L,1));
185                 /* force garbage collection to avoid releasing a to-be-collected address */
186                 #ifdef LUA_VERSION_NUM
187                 lua_gc(L, LUA_GCCOLLECT, 0);
188                 #else
189                 lua_setgcthreshold(L,0);
190                 #endif
191                 lua_pushstring(L,"tolua_gc");
192                 lua_rawget(L,LUA_REGISTRYINDEX);
193                 lua_pushlightuserdata(L,u);
194                 lua_rawget(L,-2);
195                 lua_getmetatable(L,1);
196                 if (lua_rawequal(L,-1,-2))  /* check that we are releasing the correct type */
197                 {
198                         lua_pushlightuserdata(L,u);
199                         lua_pushnil(L);
200                         lua_rawset(L,-5);
201                         done = 1;
202                 }
203         }
204         lua_pushboolean(L,done!=0);
205         return 1;
206 }
207
208 /* Type casting
209 */
210 static int tolua_bnd_cast (lua_State* L)
211 {
212
213 /* // old code
214         void* v = tolua_tousertype(L,1,NULL);
215         const char* s = tolua_tostring(L,2,NULL);
216         if (v && s)
217          tolua_pushusertype(L,v,s);
218         else
219          lua_pushnil(L);
220         return 1;
221 */
222
223         void* v;
224         const char* s;
225         if (lua_islightuserdata(L, 1)) {
226                 v = tolua_touserdata(L, 1, NULL);
227         } else {
228                 v = tolua_tousertype(L, 1, 0);
229         };
230
231         s = tolua_tostring(L,2,NULL);
232         if (v && s)
233          tolua_pushusertype(L,v,s);
234         else
235          lua_pushnil(L);
236         return 1;
237 }
238
239 /* Inheritance
240 */
241 static int tolua_bnd_inherit (lua_State* L) {
242
243         /* stack: lua object, c object */
244         lua_pushstring(L, ".c_instance");
245         lua_pushvalue(L, -2);
246         lua_rawset(L, -4);
247         /* l_obj[".c_instance"] = c_obj */
248
249         return 0;
250 };
251
252 #ifdef LUA_VERSION_NUM /* lua 5.1 */
253 static int tolua_bnd_setpeer(lua_State* L) {
254
255         /* stack: userdata, table */
256         if (!lua_isuserdata(L, -2)) {
257                 lua_pushstring(L, "Invalid argument #1 to setpeer: userdata expected.");
258                 lua_error(L);
259         };
260         
261         if (lua_isnil(L, -1)) {
262
263                 lua_pop(L, 1);
264                 lua_pushvalue(L, TOLUA_NOPEER);
265         };
266         lua_setfenv(L, -2);
267
268         return 0;
269 };
270
271 static int tolua_bnd_getpeer(lua_State* L) {
272
273         /* stack: userdata */
274         lua_getfenv(L, -1);
275         if (lua_rawequal(L, -1, TOLUA_NOPEER)) {
276                 lua_pop(L, 1);
277                 lua_pushnil(L);
278         };
279         return 1;
280 };
281 #endif
282
283 /* static int class_gc_event (lua_State* L); */
284
285 TOLUA_API void tolua_open (lua_State* L)
286 {
287  int top = lua_gettop(L);
288  lua_pushstring(L,"tolua_opened");
289  lua_rawget(L,LUA_REGISTRYINDEX);
290  if (!lua_isboolean(L,-1))
291  {
292   lua_pushstring(L,"tolua_opened"); lua_pushboolean(L,1); lua_rawset(L,LUA_REGISTRYINDEX);
293
294   #ifndef LUA_VERSION_NUM /* only prior to lua 5.1 */
295   /* create peer object table */
296   lua_pushstring(L, "tolua_peers"); lua_newtable(L);
297   /* make weak key metatable for peers indexed by userdata object */
298   lua_newtable(L); lua_pushliteral(L, "__mode"); lua_pushliteral(L, "k"); lua_rawset(L, -3);                /* stack: string peers mt */
299   lua_setmetatable(L, -2);   /* stack: string peers */
300   lua_rawset(L,LUA_REGISTRYINDEX);
301   #endif
302
303   /* create object ptr -> udata mapping table */
304   lua_pushstring(L,"tolua_ubox"); lua_newtable(L);
305   /* make weak value metatable for ubox table to allow userdata to be
306      garbage-collected */
307   lua_newtable(L); lua_pushliteral(L, "__mode"); lua_pushliteral(L, "v"); lua_rawset(L, -3);               /* stack: string ubox mt */
308   lua_setmetatable(L, -2);  /* stack: string ubox */
309   lua_rawset(L,LUA_REGISTRYINDEX);
310
311   lua_pushstring(L,"tolua_super"); lua_newtable(L); lua_rawset(L,LUA_REGISTRYINDEX);
312   lua_pushstring(L,"tolua_gc"); lua_newtable(L);lua_rawset(L,LUA_REGISTRYINDEX);
313
314   /* create gc_event closure */
315   lua_pushstring(L, "tolua_gc_event");
316   lua_pushstring(L, "tolua_gc");
317   lua_rawget(L, LUA_REGISTRYINDEX);
318   lua_pushstring(L, "tolua_super");
319   lua_rawget(L, LUA_REGISTRYINDEX);
320   lua_pushcclosure(L, class_gc_event, 2);
321   lua_rawset(L, LUA_REGISTRYINDEX);
322
323   tolua_newmetatable(L,"tolua_commonclass");
324
325   tolua_module(L,NULL,0);
326   tolua_beginmodule(L,NULL);
327   tolua_module(L,"tolua",0);
328   tolua_beginmodule(L,"tolua");
329   tolua_function(L,"type",tolua_bnd_type);
330   tolua_function(L,"takeownership",tolua_bnd_takeownership);
331   tolua_function(L,"releaseownership",tolua_bnd_releaseownership);
332   tolua_function(L,"cast",tolua_bnd_cast);
333   tolua_function(L,"inherit", tolua_bnd_inherit);
334   #ifdef LUA_VERSION_NUM /* lua 5.1 */
335   tolua_function(L, "setpeer", tolua_bnd_setpeer);
336   tolua_function(L, "getpeer", tolua_bnd_getpeer);
337   #endif
338
339   tolua_endmodule(L);
340   tolua_endmodule(L);
341  }
342  lua_settop(L,top);
343 }
344
345 /* Copy a C object
346 */
347 TOLUA_API void* tolua_copy (lua_State* L, void* value, unsigned int size)
348 {
349         void* clone = (void*)malloc(size);
350         if (clone)
351          memcpy(clone,value,size);
352         else
353                 tolua_error(L,"insuficient memory",NULL);
354         return clone;
355 }
356
357 /* Default collect function
358 */
359 TOLUA_API int tolua_default_collect (lua_State* tolua_S)
360 {
361  void* self = tolua_tousertype(tolua_S,1,0);
362  free(self);
363  return 0;
364 }
365
366 /* Do clone
367 */
368 TOLUA_API int tolua_register_gc (lua_State* L, int lo)
369 {
370  int success = 1;
371  void *value = *(void **)lua_touserdata(L,lo);
372  lua_pushstring(L,"tolua_gc");
373  lua_rawget(L,LUA_REGISTRYINDEX);
374         lua_pushlightuserdata(L,value);
375         lua_rawget(L,-2);
376         if (!lua_isnil(L,-1)) /* make sure that object is not already owned */
377                 success = 0;
378         else
379         {
380                 lua_pushlightuserdata(L,value);
381                 lua_getmetatable(L,lo);
382                 lua_rawset(L,-4);
383         }
384         lua_pop(L,2);
385         return success;
386 }
387
388 /* Register a usertype
389         * It creates the correspoding metatable in the registry, for both 'type' and 'const type'.
390         * It maps 'const type' as being also a 'type'
391 */
392 TOLUA_API void tolua_usertype (lua_State* L, const char* type)
393 {
394  char ctype[128] = "const ";
395  strncat(ctype,type,120);
396
397         /* create both metatables */
398  if (tolua_newmetatable(L,ctype) && tolua_newmetatable(L,type))
399          mapsuper(L,type,ctype);             /* 'type' is also a 'const type' */
400 }
401
402
403 /* Begin module
404         * It pushes the module (or class) table on the stack
405 */
406 TOLUA_API void tolua_beginmodule (lua_State* L, const char* name)
407 {
408         if (name)
409         {
410          lua_pushstring(L,name);
411                 lua_rawget(L,-2);
412         }
413         else
414          lua_pushvalue(L,LUA_GLOBALSINDEX);
415 }
416
417 /* End module
418         * It pops the module (or class) from the stack
419 */
420 TOLUA_API void tolua_endmodule (lua_State* L)
421 {
422         lua_pop(L,1);
423 }
424
425 /* Map module
426         * It creates a new module
427 */
428 #if 1
429 TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar)
430 {
431         if (name)
432         {
433                 /* tolua module */
434                 lua_pushstring(L,name);
435                 lua_rawget(L,-2);
436                 if (!lua_istable(L,-1))  /* check if module already exists */
437                 {
438                         lua_pop(L,1);
439                  lua_newtable(L);
440                  lua_pushstring(L,name);
441                         lua_pushvalue(L,-2);
442                  lua_rawset(L,-4);       /* assing module into module */
443                 }
444         }
445         else
446         {
447                 /* global table */
448                 lua_pushvalue(L,LUA_GLOBALSINDEX);
449         }
450         if (hasvar)
451         {
452                 if (!tolua_ismodulemetatable(L))  /* check if it already has a module metatable */
453                 {
454                         /* create metatable to get/set C/C++ variable */
455                         lua_newtable(L);
456                         tolua_moduleevents(L);
457                         if (lua_getmetatable(L,-2))
458                                 lua_setmetatable(L,-2);  /* set old metatable as metatable of metatable */
459                         lua_setmetatable(L,-2);
460                 }
461         }
462         lua_pop(L,1);               /* pop module */
463 }
464 #else
465 TOLUA_API void tolua_module (lua_State* L, const char* name, int hasvar)
466 {
467         if (name)
468         {
469                 /* tolua module */
470                 lua_pushstring(L,name);
471                 lua_newtable(L);
472         }
473         else
474         {
475                 /* global table */
476                 lua_pushvalue(L,LUA_GLOBALSINDEX);
477         }
478         if (hasvar)
479         {
480                 /* create metatable to get/set C/C++ variable */
481                 lua_newtable(L);
482                 tolua_moduleevents(L);
483                 if (lua_getmetatable(L,-2))
484                         lua_setmetatable(L,-2);  /* set old metatable as metatable of metatable */
485                 lua_setmetatable(L,-2);
486         }
487         if (name)
488                 lua_rawset(L,-3);       /* assing module into module */
489         else
490                 lua_pop(L,1);           /* pop global table */
491 }
492 #endif
493
494 static void push_collector(lua_State* L, const char* type, lua_CFunction col) {
495
496         /* push collector function, but only if it's not NULL, or if there's no
497            collector already */
498         if (!col) return;
499         luaL_getmetatable(L,type);
500         lua_pushstring(L,".collector");
501         /*
502         if (!col) {
503                 lua_pushvalue(L, -1);
504                 lua_rawget(L, -3);
505                 if (!lua_isnil(L, -1)) {
506                         lua_pop(L, 3);
507                         return;
508                 };
509                 lua_pop(L, 1);
510         };
511         //      */
512         lua_pushcfunction(L,col);
513
514         lua_rawset(L,-3);
515         lua_pop(L, 1);
516 };
517
518 /* Map C class
519         * It maps a C class, setting the appropriate inheritance and super classes.
520 */
521 TOLUA_API void tolua_cclass (lua_State* L, const char* lname, const char* name, const char* base, lua_CFunction col)
522 {
523         char cname[128] = "const ";
524         char cbase[128] = "const ";
525         strncat(cname,name,120);
526         strncat(cbase,base,120);
527
528         mapinheritance(L,name,base);
529         mapinheritance(L,cname,name);
530
531         mapsuper(L,cname,cbase);
532         mapsuper(L,name,base);
533
534         lua_pushstring(L,lname);
535         
536         push_collector(L, name, col);
537         /*
538         luaL_getmetatable(L,name);
539         lua_pushstring(L,".collector");
540         lua_pushcfunction(L,col);
541
542         lua_rawset(L,-3);
543         */
544         
545         luaL_getmetatable(L,name);
546         lua_rawset(L,-3);              /* assign class metatable to module */
547
548         /* now we also need to store the collector table for the const
549            instances of the class */
550         push_collector(L, cname, col);
551         /*
552         luaL_getmetatable(L,cname);
553         lua_pushstring(L,".collector");
554         lua_pushcfunction(L,col);
555         lua_rawset(L,-3);
556         lua_pop(L,1);
557         */
558         
559
560 }
561
562 /* Add base
563         * It adds additional base classes to a class (for multiple inheritance)
564         * (not for now)
565 TOLUA_API void tolua_addbase(lua_State* L, char* name, char* base) {
566
567         char cname[128] = "const ";
568         char cbase[128] = "const ";
569         strncat(cname,name,120);
570         strncat(cbase,base,120);
571
572         mapsuper(L,cname,cbase);
573         mapsuper(L,name,base);
574 };
575 */
576
577 /* Map function
578         * It assigns a function into the current module (or class)
579 */
580 TOLUA_API void tolua_function (lua_State* L, const char* name, lua_CFunction func)
581 {
582  lua_pushstring(L,name);
583  lua_pushcfunction(L,func);
584         lua_rawset(L,-3);
585 }
586
587 /* sets the __call event for the class (expects the class' main table on top) */
588 /*      never really worked :(
589 TOLUA_API void tolua_set_call_event(lua_State* L, lua_CFunction func, char* type) {
590
591         lua_getmetatable(L, -1);
592         //luaL_getmetatable(L, type);
593         lua_pushstring(L,"__call");
594         lua_pushcfunction(L,func);
595         lua_rawset(L,-3);
596         lua_pop(L, 1);
597 };
598 */
599
600 /* Map constant number
601         * It assigns a constant number into the current module (or class)
602 */
603 TOLUA_API void tolua_constant (lua_State* L, const char* name, lua_Number value)
604 {
605         lua_pushstring(L,name);
606         tolua_pushnumber(L,value);
607         lua_rawset(L,-3);
608 }
609
610
611 /* Map variable
612         * It assigns a variable into the current module (or class)
613 */
614 TOLUA_API void tolua_variable (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set)
615 {
616         /* get func */
617         lua_pushstring(L,".get");
618         lua_rawget(L,-2);
619         if (!lua_istable(L,-1))
620         {
621                 /* create .get table, leaving it at the top */
622                 lua_pop(L,1);
623                 lua_newtable(L);
624          lua_pushstring(L,".get");
625                 lua_pushvalue(L,-2);
626                 lua_rawset(L,-4);
627         }
628         lua_pushstring(L,name);
629         lua_pushcfunction(L,get);
630  lua_rawset(L,-3);                  /* store variable */
631         lua_pop(L,1);                      /* pop .get table */
632
633         /* set func */
634         if (set)
635         {
636                 lua_pushstring(L,".set");
637                 lua_rawget(L,-2);
638                 if (!lua_istable(L,-1))
639                 {
640                         /* create .set table, leaving it at the top */
641                         lua_pop(L,1);
642                         lua_newtable(L);
643                         lua_pushstring(L,".set");
644                         lua_pushvalue(L,-2);
645                         lua_rawset(L,-4);
646                 }
647                 lua_pushstring(L,name);
648                 lua_pushcfunction(L,set);
649                 lua_rawset(L,-3);                  /* store variable */
650                 lua_pop(L,1);                      /* pop .set table */
651         }
652 }
653
654 /* Access const array
655         * It reports an error when trying to write into a const array
656 */
657 static int const_array (lua_State* L)
658 {
659  luaL_error(L,"value of const array cannot be changed");
660  return 0;
661 }
662
663 /* Map an array
664         * It assigns an array into the current module (or class)
665 */
666 TOLUA_API void tolua_array (lua_State* L, const char* name, lua_CFunction get, lua_CFunction set)
667 {
668         lua_pushstring(L,".get");
669         lua_rawget(L,-2);
670         if (!lua_istable(L,-1))
671         {
672                 /* create .get table, leaving it at the top */
673                 lua_pop(L,1);
674                 lua_newtable(L);
675          lua_pushstring(L,".get");
676                 lua_pushvalue(L,-2);
677                 lua_rawset(L,-4);
678         }
679         lua_pushstring(L,name);
680
681  lua_newtable(L);           /* create array metatable */
682  lua_pushvalue(L,-1);
683         lua_setmetatable(L,-2);    /* set the own table as metatable (for modules) */
684  lua_pushstring(L,"__index");
685  lua_pushcfunction(L,get);
686         lua_rawset(L,-3);
687  lua_pushstring(L,"__newindex");
688  lua_pushcfunction(L,set?set:const_array);
689         lua_rawset(L,-3);
690
691  lua_rawset(L,-3);                  /* store variable */
692         lua_pop(L,1);                      /* pop .get table */
693 }
694
695
696 TOLUA_API void tolua_dobuffer(lua_State* L, char* B, unsigned int size, const char* name) {
697
698  #ifdef LUA_VERSION_NUM /* lua 5.1 */
699  luaL_loadbuffer(L, B, size, name) || lua_pcall(L, 0, 0, 0);
700  #else
701  lua_dobuffer(L, B, size, name);
702  #endif
703 };
704