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