Tizen 2.1 base
[external/tolua++.git] / tolua_event.c
1 /* tolua: event functions
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 <stdio.h>
16
17 #include "tolua++.h"
18
19 /* Store at ubox
20         * It stores, creating the corresponding table if needed,
21         * the pair key/value in the corresponding ubox table
22 */
23 static void storeatubox (lua_State* L, int lo)
24 {
25         #ifdef LUA_VERSION_NUM
26                 lua_getfenv(L, lo);
27                 if (lua_rawequal(L, -1, TOLUA_NOPEER)) {
28                         lua_pop(L, 1);
29                         lua_newtable(L);
30                         lua_pushvalue(L, -1);
31                         lua_setfenv(L, lo);     /* stack: k,v,table  */
32                 };
33                 lua_insert(L, -3);
34                 lua_settable(L, -3); /* on lua 5.1, we trade the "tolua_peers" lookup for a settable call */
35                 lua_pop(L, 1);
36         #else
37          /* stack: key value (to be stored) */
38                 lua_pushstring(L,"tolua_peers");
39                 lua_rawget(L,LUA_REGISTRYINDEX);        /* stack: k v ubox */
40                 lua_pushvalue(L,lo);
41                 lua_rawget(L,-2);                       /* stack: k v ubox ubox[u] */
42                 if (!lua_istable(L,-1))
43                 {
44                         lua_pop(L,1);                          /* stack: k v ubox */
45                         lua_newtable(L);                       /* stack: k v ubox table */
46                         lua_pushvalue(L,1);
47                         lua_pushvalue(L,-2);                   /* stack: k v ubox table u table */
48                         lua_rawset(L,-4);                      /* stack: k v ubox ubox[u]=table */
49                 }
50                 lua_insert(L,-4);                       /* put table before k */
51                 lua_pop(L,1);                           /* pop ubox */
52                 lua_rawset(L,-3);                       /* store at table */
53                 lua_pop(L,1);                           /* pop ubox[u] */
54         #endif
55 }
56
57 /* Module index function
58 */
59 static int module_index_event (lua_State* L)
60 {
61         lua_pushstring(L,".get");
62         lua_rawget(L,-3);
63         if (lua_istable(L,-1))
64         {
65                 lua_pushvalue(L,2);  /* key */
66                 lua_rawget(L,-2);
67                 if (lua_iscfunction(L,-1))
68                 {
69                         lua_call(L,0,1);
70                         return 1;
71                 }
72                 else if (lua_istable(L,-1))
73                         return 1;
74         }
75         /* call old index meta event */
76         if (lua_getmetatable(L,1))
77         {
78                 lua_pushstring(L,"__index");
79                 lua_rawget(L,-2);
80                 lua_pushvalue(L,1);
81                 lua_pushvalue(L,2);
82                 if (lua_isfunction(L,-1))
83                 {
84                         lua_call(L,2,1);
85                         return 1;
86                 }
87                 else if (lua_istable(L,-1))
88                 {
89                         lua_gettable(L,-3);
90                         return 1;
91                 }
92         }
93         lua_pushnil(L);
94         return 1;
95 }
96
97 /* Module newindex function
98 */
99 static int module_newindex_event (lua_State* L)
100 {
101         lua_pushstring(L,".set");
102         lua_rawget(L,-4);
103         if (lua_istable(L,-1))
104         {
105                 lua_pushvalue(L,2);  /* key */
106                 lua_rawget(L,-2);
107                 if (lua_iscfunction(L,-1))
108                 {
109                         lua_pushvalue(L,1); /* only to be compatible with non-static vars */
110                         lua_pushvalue(L,3); /* value */
111                         lua_call(L,2,0);
112                         return 0;
113                 }
114         }
115         /* call old newindex meta event */
116         if (lua_getmetatable(L,1) && lua_getmetatable(L,-1))
117         {
118                 lua_pushstring(L,"__newindex");
119                 lua_rawget(L,-2);
120                 if (lua_isfunction(L,-1))
121                 {
122                  lua_pushvalue(L,1);
123                  lua_pushvalue(L,2);
124                  lua_pushvalue(L,3);
125                         lua_call(L,3,0);
126                 }
127         }
128         lua_settop(L,3);
129         lua_rawset(L,-3);
130         return 0;
131 }
132
133 /* Class index function
134         * If the object is a userdata (ie, an object), it searches the field in
135         * the alternative table stored in the corresponding "ubox" table.
136 */
137 static int class_index_event (lua_State* L)
138 {
139  int t = lua_type(L,1);
140         if (t == LUA_TUSERDATA)
141         {
142                 /* Access alternative table */
143                 #ifdef LUA_VERSION_NUM /* new macro on version 5.1 */
144                 lua_getfenv(L,1);
145                 if (!lua_rawequal(L, -1, TOLUA_NOPEER)) {
146                         lua_pushvalue(L, 2); /* key */
147                         lua_gettable(L, -2); /* on lua 5.1, we trade the "tolua_peers" lookup for a gettable call */
148                         if (!lua_isnil(L, -1))
149                                 return 1;
150                 };
151                 #else
152                 lua_pushstring(L,"tolua_peers");
153                 lua_rawget(L,LUA_REGISTRYINDEX);        /* stack: obj key ubox */
154                 lua_pushvalue(L,1);
155                 lua_rawget(L,-2);                       /* stack: obj key ubox ubox[u] */
156                 if (lua_istable(L,-1))
157                 {
158                         lua_pushvalue(L,2);  /* key */
159                         lua_rawget(L,-2);                      /* stack: obj key ubox ubox[u] value */
160                         if (!lua_isnil(L,-1))
161                                 return 1;
162                 }
163                 #endif
164                 lua_settop(L,2);                        /* stack: obj key */
165                 /* Try metatables */
166                 lua_pushvalue(L,1);                     /* stack: obj key obj */
167                 while (lua_getmetatable(L,-1))
168                 {                                       /* stack: obj key obj mt */
169                         lua_remove(L,-2);                      /* stack: obj key mt */
170                         if (lua_isnumber(L,2))                 /* check if key is a numeric value */
171                         {
172                                 /* try operator[] */
173                                 lua_pushstring(L,".geti");
174                                 lua_rawget(L,-2);                      /* stack: obj key mt func */
175                                 if (lua_isfunction(L,-1))
176                                 {
177                                         lua_pushvalue(L,1);
178                                         lua_pushvalue(L,2);
179                                         lua_call(L,2,1);
180                                         return 1;
181                                 }
182                         }
183                         else
184                         {
185                          lua_pushvalue(L,2);                    /* stack: obj key mt key */
186                                 lua_rawget(L,-2);                      /* stack: obj key mt value */
187                                 if (!lua_isnil(L,-1))
188                                         return 1;
189                                 else
190                                         lua_pop(L,1);
191                                 /* try C/C++ variable */
192                                 lua_pushstring(L,".get");
193                                 lua_rawget(L,-2);                      /* stack: obj key mt tget */
194                                 if (lua_istable(L,-1))
195                                 {
196                                         lua_pushvalue(L,2);
197                                         lua_rawget(L,-2);                      /* stack: obj key mt value */
198                                         if (lua_iscfunction(L,-1))
199                                         {
200                                                 lua_pushvalue(L,1);
201                                                 lua_pushvalue(L,2);
202                                                 lua_call(L,2,1);
203                                                 return 1;
204                                         }
205                                         else if (lua_istable(L,-1))
206                                         {
207                                                 /* deal with array: create table to be returned and cache it in ubox */
208                                                 void* u = *((void**)lua_touserdata(L,1));
209                                                 lua_newtable(L);                /* stack: obj key mt value table */
210                                                 lua_pushstring(L,".self");
211                                                 lua_pushlightuserdata(L,u);
212                                                 lua_rawset(L,-3);               /* store usertype in ".self" */
213                                                 lua_insert(L,-2);               /* stack: obj key mt table value */
214                                                 lua_setmetatable(L,-2);         /* set stored value as metatable */
215                                                 lua_pushvalue(L,-1);            /* stack: obj key met table table */
216                                                 lua_pushvalue(L,2);             /* stack: obj key mt table table key */
217                                                 lua_insert(L,-2);               /*  stack: obj key mt table key table */
218                                                 storeatubox(L,1);               /* stack: obj key mt table */
219                                                 return 1;
220                                         }
221                                 }
222                         }
223                         lua_settop(L,3);
224                 }
225                 lua_pushnil(L);
226                 return 1;
227         }
228         else if (t== LUA_TTABLE)
229         {
230                 module_index_event(L);
231                 return 1;
232         }
233         lua_pushnil(L);
234         return 1;
235 }
236
237 /* Newindex function
238         * It first searches for a C/C++ varaible to be set.
239         * Then, it either stores it in the alternative ubox table (in the case it is
240         * an object) or in the own table (that represents the class or module).
241 */
242 static int class_newindex_event (lua_State* L)
243 {
244  int t = lua_type(L,1);
245         if (t == LUA_TUSERDATA)
246         {
247          /* Try accessing a C/C++ variable to be set */
248                 lua_getmetatable(L,1);
249                 while (lua_istable(L,-1))                /* stack: t k v mt */
250                 {
251                         if (lua_isnumber(L,2))                 /* check if key is a numeric value */
252                         {
253                                 /* try operator[] */
254                                 lua_pushstring(L,".seti");
255                                 lua_rawget(L,-2);                      /* stack: obj key mt func */
256                                 if (lua_isfunction(L,-1))
257                                 {
258                                         lua_pushvalue(L,1);
259                                         lua_pushvalue(L,2);
260                                         lua_pushvalue(L,3);
261                                         lua_call(L,3,0);
262                                         return 0;
263                                 }
264                         }
265                         else
266                         {
267                                 lua_pushstring(L,".set");
268                                 lua_rawget(L,-2);                      /* stack: t k v mt tset */
269                                 if (lua_istable(L,-1))
270                                 {
271                                         lua_pushvalue(L,2);
272                                         lua_rawget(L,-2);                     /* stack: t k v mt tset func */
273                                         if (lua_iscfunction(L,-1))
274                                         {
275                                                 lua_pushvalue(L,1);
276                                                 lua_pushvalue(L,3);
277                                                 lua_call(L,2,0);
278                                                 return 0;
279                                         }
280                                         lua_pop(L,1);                          /* stack: t k v mt tset */
281                                 }
282                                 lua_pop(L,1);                           /* stack: t k v mt */
283                                 if (!lua_getmetatable(L,-1))            /* stack: t k v mt mt */
284                                         lua_pushnil(L);
285                                 lua_remove(L,-2);                       /* stack: t k v mt */
286                         }
287                 }
288          lua_settop(L,3);                          /* stack: t k v */
289
290                 /* then, store as a new field */
291                 storeatubox(L,1);
292         }
293         else if (t== LUA_TTABLE)
294         {
295                 module_newindex_event(L);
296         }
297         return 0;
298 }
299
300 static int class_call_event(lua_State* L) {
301
302         if (lua_istable(L, 1)) {
303                 lua_pushstring(L, ".call");
304                 lua_rawget(L, 1);
305                 if (lua_isfunction(L, -1)) {
306
307                         lua_insert(L, 1);
308                         lua_call(L, lua_gettop(L)-1, 1);
309
310                         return 1;
311                 };
312         };
313         tolua_error(L,"Attempt to call a non-callable object.",NULL);
314         return 0;
315 };
316
317 static int do_operator (lua_State* L, const char* op)
318 {
319         if (lua_isuserdata(L,1))
320         {
321                 /* Try metatables */
322                 lua_pushvalue(L,1);                     /* stack: op1 op2 */
323                 while (lua_getmetatable(L,-1))
324                 {                                       /* stack: op1 op2 op1 mt */
325                         lua_remove(L,-2);                      /* stack: op1 op2 mt */
326                         lua_pushstring(L,op);                  /* stack: op1 op2 mt key */
327                         lua_rawget(L,-2);                      /* stack: obj key mt func */
328                         if (lua_isfunction(L,-1))
329                         {
330                                 lua_pushvalue(L,1);
331                                 lua_pushvalue(L,2);
332                                 lua_call(L,2,1);
333                                 return 1;
334                         }
335                         lua_settop(L,3);
336                 }
337         }
338         tolua_error(L,"Attempt to perform operation on an invalid operand",NULL);
339         return 0;
340 }
341
342 static int class_add_event (lua_State* L)
343 {
344         return do_operator(L,".add");
345 }
346
347 static int class_sub_event (lua_State* L)
348 {
349         return do_operator(L,".sub");
350 }
351
352 static int class_mul_event (lua_State* L)
353 {
354         return do_operator(L,".mul");
355 }
356
357 static int class_div_event (lua_State* L)
358 {
359         return do_operator(L,".div");
360 }
361
362 static int class_lt_event (lua_State* L)
363 {
364         return do_operator(L,".lt");
365 }
366
367 static int class_le_event (lua_State* L)
368 {
369         return do_operator(L,".le");
370 }
371
372 static int class_eq_event (lua_State* L)
373 {
374         /* copying code from do_operator here to return false when no operator is found */
375         if (lua_isuserdata(L,1))
376         {
377                 /* Try metatables */
378                 lua_pushvalue(L,1);                     /* stack: op1 op2 */
379                 while (lua_getmetatable(L,-1))
380                 {                                       /* stack: op1 op2 op1 mt */
381                         lua_remove(L,-2);                      /* stack: op1 op2 mt */
382                         lua_pushstring(L,".eq");                  /* stack: op1 op2 mt key */
383                         lua_rawget(L,-2);                      /* stack: obj key mt func */
384                         if (lua_isfunction(L,-1))
385                         {
386                                 lua_pushvalue(L,1);
387                                 lua_pushvalue(L,2);
388                                 lua_call(L,2,1);
389                                 return 1;
390                         }
391                         lua_settop(L,3);
392                 }
393         }
394
395         lua_settop(L, 3);
396         lua_pushboolean(L, 0);
397         return 1;
398 }
399
400 /*
401 static int class_gc_event (lua_State* L)
402 {
403         void* u = *((void**)lua_touserdata(L,1));
404         fprintf(stderr, "collecting: looking at %p\n", u);
405         lua_pushstring(L,"tolua_gc");
406         lua_rawget(L,LUA_REGISTRYINDEX);
407         lua_pushlightuserdata(L,u);
408         lua_rawget(L,-2);
409         if (lua_isfunction(L,-1))
410         {
411                 lua_pushvalue(L,1);
412                 lua_call(L,1,0);
413                 lua_pushlightuserdata(L,u);
414                 lua_pushnil(L);
415                 lua_rawset(L,-3);
416         }
417         lua_pop(L,2);
418         return 0;
419 }
420 */
421 TOLUA_API int class_gc_event (lua_State* L)
422 {
423         void* u = *((void**)lua_touserdata(L,1));
424         int top;
425         /*fprintf(stderr, "collecting: looking at %p\n", u);*/
426         /*
427         lua_pushstring(L,"tolua_gc");
428         lua_rawget(L,LUA_REGISTRYINDEX);
429         */
430         lua_pushvalue(L, lua_upvalueindex(1));
431         lua_pushlightuserdata(L,u);
432         lua_rawget(L,-2);            /* stack: gc umt    */
433         lua_getmetatable(L,1);       /* stack: gc umt mt */
434         /*fprintf(stderr, "checking type\n");*/
435         top = lua_gettop(L);
436         if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */
437         {
438                 /*fprintf(stderr, "Found type!\n");*/
439                 /* get gc function */
440                 lua_pushliteral(L,".collector");
441                 lua_rawget(L,-2);           /* stack: gc umt mt collector */
442                 if (lua_isfunction(L,-1)) {
443                         /*fprintf(stderr, "Found .collector!\n");*/
444                 }
445                 else {
446                         lua_pop(L,1);
447                         /*fprintf(stderr, "Using default cleanup\n");*/
448                         lua_pushcfunction(L,tolua_default_collect);
449                 }
450
451                 lua_pushvalue(L,1);         /* stack: gc umt mt collector u */
452                 lua_call(L,1,0);
453
454                 lua_pushlightuserdata(L,u); /* stack: gc umt mt u */
455                 lua_pushnil(L);             /* stack: gc umt mt u nil */
456                 lua_rawset(L,-5);           /* stack: gc umt mt */
457         }
458         lua_pop(L,3);
459         return 0;
460 }
461
462
463 /* Register module events
464         * It expects the metatable on the top of the stack
465 */
466 TOLUA_API void tolua_moduleevents (lua_State* L)
467 {
468         lua_pushstring(L,"__index");
469         lua_pushcfunction(L,module_index_event);
470         lua_rawset(L,-3);
471         lua_pushstring(L,"__newindex");
472         lua_pushcfunction(L,module_newindex_event);
473         lua_rawset(L,-3);
474 }
475
476 /* Check if the object on the top has a module metatable
477 */
478 TOLUA_API int tolua_ismodulemetatable (lua_State* L)
479 {
480         int r = 0;
481         if (lua_getmetatable(L,-1))
482         {
483                 lua_pushstring(L,"__index");
484                 lua_rawget(L,-2);
485                 r = (lua_tocfunction(L,-1) == module_index_event);
486                 lua_pop(L,2);
487         }
488         return r;
489 }
490
491 /* Register class events
492         * It expects the metatable on the top of the stack
493 */
494 TOLUA_API void tolua_classevents (lua_State* L)
495 {
496         lua_pushstring(L,"__index");
497         lua_pushcfunction(L,class_index_event);
498         lua_rawset(L,-3);
499         lua_pushstring(L,"__newindex");
500         lua_pushcfunction(L,class_newindex_event);
501         lua_rawset(L,-3);
502
503         lua_pushstring(L,"__add");
504         lua_pushcfunction(L,class_add_event);
505         lua_rawset(L,-3);
506         lua_pushstring(L,"__sub");
507         lua_pushcfunction(L,class_sub_event);
508         lua_rawset(L,-3);
509         lua_pushstring(L,"__mul");
510         lua_pushcfunction(L,class_mul_event);
511         lua_rawset(L,-3);
512         lua_pushstring(L,"__div");
513         lua_pushcfunction(L,class_div_event);
514         lua_rawset(L,-3);
515
516         lua_pushstring(L,"__lt");
517         lua_pushcfunction(L,class_lt_event);
518         lua_rawset(L,-3);
519         lua_pushstring(L,"__le");
520         lua_pushcfunction(L,class_le_event);
521         lua_rawset(L,-3);
522         lua_pushstring(L,"__eq");
523         lua_pushcfunction(L,class_eq_event);
524         lua_rawset(L,-3);
525
526         lua_pushstring(L,"__call");
527         lua_pushcfunction(L,class_call_event);
528         lua_rawset(L,-3);
529
530         lua_pushstring(L,"__gc");
531         lua_pushstring(L, "tolua_gc_event");
532         lua_rawget(L, LUA_REGISTRYINDEX);
533         /*lua_pushcfunction(L,class_gc_event);*/
534         lua_rawset(L,-3);
535 }
536