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