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