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