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