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 | */ |
---|
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 | */ |
---|
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 | */ |
---|
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 | */ |
---|
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 | */ |
---|
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 | |
---|
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 | |
---|
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 | |
---|
342 | static int class_add_event (lua_State* L) |
---|
343 | { |
---|
344 | return do_operator(L,".add"); |
---|
345 | } |
---|
346 | |
---|
347 | static int class_sub_event (lua_State* L) |
---|
348 | { |
---|
349 | return do_operator(L,".sub"); |
---|
350 | } |
---|
351 | |
---|
352 | static int class_mul_event (lua_State* L) |
---|
353 | { |
---|
354 | return do_operator(L,".mul"); |
---|
355 | } |
---|
356 | |
---|
357 | static int class_div_event (lua_State* L) |
---|
358 | { |
---|
359 | return do_operator(L,".div"); |
---|
360 | } |
---|
361 | |
---|
362 | static int class_lt_event (lua_State* L) |
---|
363 | { |
---|
364 | return do_operator(L,".lt"); |
---|
365 | } |
---|
366 | |
---|
367 | static int class_le_event (lua_State* L) |
---|
368 | { |
---|
369 | return do_operator(L,".le"); |
---|
370 | } |
---|
371 | |
---|
372 | static int class_eq_event (lua_State* L) |
---|
373 | { |
---|
374 | return do_operator(L,".eq"); |
---|
375 | } |
---|
376 | |
---|
377 | /* |
---|
378 | static int class_gc_event (lua_State* L) |
---|
379 | { |
---|
380 | void* u = *((void**)lua_touserdata(L,1)); |
---|
381 | fprintf(stderr, "collecting: looking at %p\n", u); |
---|
382 | lua_pushstring(L,"tolua_gc"); |
---|
383 | lua_rawget(L,LUA_REGISTRYINDEX); |
---|
384 | lua_pushlightuserdata(L,u); |
---|
385 | lua_rawget(L,-2); |
---|
386 | if (lua_isfunction(L,-1)) |
---|
387 | { |
---|
388 | lua_pushvalue(L,1); |
---|
389 | lua_call(L,1,0); |
---|
390 | lua_pushlightuserdata(L,u); |
---|
391 | lua_pushnil(L); |
---|
392 | lua_rawset(L,-3); |
---|
393 | } |
---|
394 | lua_pop(L,2); |
---|
395 | return 0; |
---|
396 | } |
---|
397 | */ |
---|
398 | TOLUA_API int class_gc_event (lua_State* L) |
---|
399 | { |
---|
400 | void* u = *((void**)lua_touserdata(L,1)); |
---|
401 | int top; |
---|
402 | /*fprintf(stderr, "collecting: looking at %p\n", u);*/ |
---|
403 | /* |
---|
404 | lua_pushstring(L,"tolua_gc"); |
---|
405 | lua_rawget(L,LUA_REGISTRYINDEX); |
---|
406 | */ |
---|
407 | lua_pushvalue(L, lua_upvalueindex(1)); |
---|
408 | lua_pushlightuserdata(L,u); |
---|
409 | lua_rawget(L,-2); /* stack: gc umt */ |
---|
410 | lua_getmetatable(L,1); /* stack: gc umt mt */ |
---|
411 | /*fprintf(stderr, "checking type\n");*/ |
---|
412 | top = lua_gettop(L); |
---|
413 | if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */ |
---|
414 | { |
---|
415 | /*fprintf(stderr, "Found type!\n");*/ |
---|
416 | /* get gc function */ |
---|
417 | lua_pushliteral(L,".collector"); |
---|
418 | lua_rawget(L,-2); /* stack: gc umt mt collector */ |
---|
419 | if (lua_isfunction(L,-1)) { |
---|
420 | /*fprintf(stderr, "Found .collector!\n");*/ |
---|
421 | } |
---|
422 | else { |
---|
423 | lua_pop(L,1); |
---|
424 | /*fprintf(stderr, "Using default cleanup\n");*/ |
---|
425 | lua_pushcfunction(L,tolua_default_collect); |
---|
426 | } |
---|
427 | |
---|
428 | lua_pushvalue(L,1); /* stack: gc umt mt collector u */ |
---|
429 | lua_call(L,1,0); |
---|
430 | |
---|
431 | lua_pushlightuserdata(L,u); /* stack: gc umt mt u */ |
---|
432 | lua_pushnil(L); /* stack: gc umt mt u nil */ |
---|
433 | lua_rawset(L,-5); /* stack: gc umt mt */ |
---|
434 | } |
---|
435 | lua_pop(L,3); |
---|
436 | return 0; |
---|
437 | } |
---|
438 | |
---|
439 | |
---|
440 | /* Register module events |
---|
441 | * It expects the metatable on the top of the stack |
---|
442 | */ |
---|
443 | TOLUA_API void tolua_moduleevents (lua_State* L) |
---|
444 | { |
---|
445 | lua_pushstring(L,"__index"); |
---|
446 | lua_pushcfunction(L,module_index_event); |
---|
447 | lua_rawset(L,-3); |
---|
448 | lua_pushstring(L,"__newindex"); |
---|
449 | lua_pushcfunction(L,module_newindex_event); |
---|
450 | lua_rawset(L,-3); |
---|
451 | } |
---|
452 | |
---|
453 | /* Check if the object on the top has a module metatable |
---|
454 | */ |
---|
455 | TOLUA_API int tolua_ismodulemetatable (lua_State* L) |
---|
456 | { |
---|
457 | int r = 0; |
---|
458 | if (lua_getmetatable(L,-1)) |
---|
459 | { |
---|
460 | lua_pushstring(L,"__index"); |
---|
461 | lua_rawget(L,-2); |
---|
462 | r = (lua_tocfunction(L,-1) == module_index_event); |
---|
463 | lua_pop(L,2); |
---|
464 | } |
---|
465 | return r; |
---|
466 | } |
---|
467 | |
---|
468 | /* Register class events |
---|
469 | * It expects the metatable on the top of the stack |
---|
470 | */ |
---|
471 | TOLUA_API void tolua_classevents (lua_State* L) |
---|
472 | { |
---|
473 | lua_pushstring(L,"__index"); |
---|
474 | lua_pushcfunction(L,class_index_event); |
---|
475 | lua_rawset(L,-3); |
---|
476 | lua_pushstring(L,"__newindex"); |
---|
477 | lua_pushcfunction(L,class_newindex_event); |
---|
478 | lua_rawset(L,-3); |
---|
479 | |
---|
480 | lua_pushstring(L,"__add"); |
---|
481 | lua_pushcfunction(L,class_add_event); |
---|
482 | lua_rawset(L,-3); |
---|
483 | lua_pushstring(L,"__sub"); |
---|
484 | lua_pushcfunction(L,class_sub_event); |
---|
485 | lua_rawset(L,-3); |
---|
486 | lua_pushstring(L,"__mul"); |
---|
487 | lua_pushcfunction(L,class_mul_event); |
---|
488 | lua_rawset(L,-3); |
---|
489 | lua_pushstring(L,"__div"); |
---|
490 | lua_pushcfunction(L,class_div_event); |
---|
491 | lua_rawset(L,-3); |
---|
492 | |
---|
493 | lua_pushstring(L,"__lt"); |
---|
494 | lua_pushcfunction(L,class_lt_event); |
---|
495 | lua_rawset(L,-3); |
---|
496 | lua_pushstring(L,"__le"); |
---|
497 | lua_pushcfunction(L,class_le_event); |
---|
498 | lua_rawset(L,-3); |
---|
499 | lua_pushstring(L,"__eq"); |
---|
500 | lua_pushcfunction(L,class_eq_event); |
---|
501 | lua_rawset(L,-3); |
---|
502 | |
---|
503 | lua_pushstring(L,"__call"); |
---|
504 | lua_pushcfunction(L,class_call_event); |
---|
505 | lua_rawset(L,-3); |
---|
506 | |
---|
507 | lua_pushstring(L,"__gc"); |
---|
508 | lua_pushstring(L, "tolua_gc_event"); |
---|
509 | lua_rawget(L, LUA_REGISTRYINDEX); |
---|
510 | /*lua_pushcfunction(L,class_gc_event);*/ |
---|
511 | lua_rawset(L,-3); |
---|
512 | } |
---|
513 | |
---|