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