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 		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 */
module_index_event(lua_State * L)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 */
module_newindex_event(lua_State * L)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 */
class_index_event(lua_State * L)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 */
class_newindex_event(lua_State * L)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 
class_call_event(lua_State * L)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 
do_operator(lua_State * L,const char * op)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 
class_add_event(lua_State * L)342 static int class_add_event (lua_State* L)
343 {
344 	return do_operator(L,".add");
345 }
346 
class_sub_event(lua_State * L)347 static int class_sub_event (lua_State* L)
348 {
349 	return do_operator(L,".sub");
350 }
351 
class_mul_event(lua_State * L)352 static int class_mul_event (lua_State* L)
353 {
354 	return do_operator(L,".mul");
355 }
356 
class_div_event(lua_State * L)357 static int class_div_event (lua_State* L)
358 {
359 	return do_operator(L,".div");
360 }
361 
class_lt_event(lua_State * L)362 static int class_lt_event (lua_State* L)
363 {
364 	return do_operator(L,".lt");
365 }
366 
class_le_event(lua_State * L)367 static int class_le_event (lua_State* L)
368 {
369 	return do_operator(L,".le");
370 }
371 
class_eq_event(lua_State * L)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 */
class_gc_event(lua_State * L)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 */
tolua_moduleevents(lua_State * L)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 */
tolua_ismodulemetatable(lua_State * L)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 */
tolua_classevents(lua_State * L)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 
537