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