/* tolua: event functions
** Support code for Lua bindings.
** Written by Waldemar Celes
** TeCGraf/PUC-Rio
** Apr 2003
** $Id: $
*/
/* This code is free software; you can redistribute it and/or modify it.
** The software provided hereunder is on an "as is" basis, and
** the author has no obligation to provide maintenance, support, updates,
** enhancements, or modifications.
*/
#include <stdio.h>
#include "tolua++.h"
/* Store at ubox
* It stores, creating the corresponding table if needed,
* the pair key/value in the corresponding ubox table
*/
static void storeatubox (lua_State* L, int lo)
{
#ifdef LUA_VERSION_NUM
lua_getfenv(L, lo);
if (lua_rawequal(L, -1, TOLUA_NOPEER)) {
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setfenv(L, lo); /* stack: k,v,table */
};
lua_insert(L, -3);
lua_settable(L, -3); /* on lua 5.1, we trade the "tolua_peers" lookup for a settable call */
lua_pop(L, 1);
#else
/* stack: key value (to be stored) */
lua_pushstring(L,"tolua_peers");
lua_rawget(L,LUA_REGISTRYINDEX); /* stack: k v ubox */
lua_pushvalue(L,lo);
lua_rawget(L,-2); /* stack: k v ubox ubox[u] */
if (!lua_istable(L,-1))
{
lua_pop(L,1); /* stack: k v ubox */
lua_newtable(L); /* stack: k v ubox table */
lua_pushvalue(L,1);
lua_pushvalue(L,-2); /* stack: k v ubox table u table */
lua_rawset(L,-4); /* stack: k v ubox ubox[u]=table */
}
lua_insert(L,-4); /* put table before k */
lua_pop(L,1); /* pop ubox */
lua_rawset(L,-3); /* store at table */
lua_pop(L,1); /* pop ubox[u] */
#endif
}
/* Module index function
*/
static int module_index_event (lua_State* L)
{
lua_pushstring(L,".get");
lua_rawget(L,-3);
if (lua_istable(L,-1))
{
lua_pushvalue(L,2); /* key */
lua_rawget(L,-2);
if (lua_iscfunction(L,-1))
{
lua_call(L,0,1);
return 1;
}
else if (lua_istable(L,-1))
return 1;
}
/* call old index meta event */
if (lua_getmetatable(L,1))
{
lua_pushstring(L,"__index");
lua_rawget(L,-2);
lua_pushvalue(L,1);
lua_pushvalue(L,2);
if (lua_isfunction(L,-1))
{
lua_call(L,2,1);
return 1;
}
else if (lua_istable(L,-1))
{
lua_gettable(L,-3);
return 1;
}
}
lua_pushnil(L);
return 1;
}
/* Module newindex function
*/
static int module_newindex_event (lua_State* L)
{
lua_pushstring(L,".set");
lua_rawget(L,-4);
if (lua_istable(L,-1))
{
lua_pushvalue(L,2); /* key */
lua_rawget(L,-2);
if (lua_iscfunction(L,-1))
{
lua_pushvalue(L,1); /* only to be compatible with non-static vars */
lua_pushvalue(L,3); /* value */
lua_call(L,2,0);
return 0;
}
}
/* call old newindex meta event */
if (lua_getmetatable(L,1) && lua_getmetatable(L,-1))
{
lua_pushstring(L,"__newindex");
lua_rawget(L,-2);
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_pushvalue(L,3);
lua_call(L,3,0);
}
}
lua_settop(L,3);
lua_rawset(L,-3);
return 0;
}
/* Class index function
* If the object is a userdata (ie, an object), it searches the field in
* the alternative table stored in the corresponding "ubox" table.
*/
static int class_index_event (lua_State* L)
{
int t = lua_type(L,1);
if (t == LUA_TUSERDATA)
{
/* Access alternative table */
#ifdef LUA_VERSION_NUM /* new macro on version 5.1 */
lua_getfenv(L,1);
if (!lua_rawequal(L, -1, TOLUA_NOPEER)) {
lua_pushvalue(L, 2); /* key */
lua_gettable(L, -2); /* on lua 5.1, we trade the "tolua_peers" lookup for a gettable call */
if (!lua_isnil(L, -1))
return 1;
};
#else
lua_pushstring(L,"tolua_peers");
lua_rawget(L,LUA_REGISTRYINDEX); /* stack: obj key ubox */
lua_pushvalue(L,1);
lua_rawget(L,-2); /* stack: obj key ubox ubox[u] */
if (lua_istable(L,-1))
{
lua_pushvalue(L,2); /* key */
lua_rawget(L,-2); /* stack: obj key ubox ubox[u] value */
if (!lua_isnil(L,-1))
return 1;
}
#endif
lua_settop(L,2); /* stack: obj key */
/* Try metatables */
lua_pushvalue(L,1); /* stack: obj key obj */
while (lua_getmetatable(L,-1))
{ /* stack: obj key obj mt */
lua_remove(L,-2); /* stack: obj key mt */
if (lua_isnumber(L,2)) /* check if key is a numeric value */
{
/* try operator[] */
lua_pushstring(L,".geti");
lua_rawget(L,-2); /* stack: obj key mt func */
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_call(L,2,1);
return 1;
}
}
else
{
lua_pushvalue(L,2); /* stack: obj key mt key */
lua_rawget(L,-2); /* stack: obj key mt value */
if (!lua_isnil(L,-1))
return 1;
else
lua_pop(L,1);
/* try C/C++ variable */
lua_pushstring(L,".get");
lua_rawget(L,-2); /* stack: obj key mt tget */
if (lua_istable(L,-1))
{
lua_pushvalue(L,2);
lua_rawget(L,-2); /* stack: obj key mt value */
if (lua_iscfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_call(L,2,1);
return 1;
}
else if (lua_istable(L,-1))
{
/* deal with array: create table to be returned and cache it in ubox */
void* u = *((void**)lua_touserdata(L,1));
lua_newtable(L); /* stack: obj key mt value table */
lua_pushstring(L,".self");
lua_pushlightuserdata(L,u);
lua_rawset(L,-3); /* store usertype in ".self" */
lua_insert(L,-2); /* stack: obj key mt table value */
lua_setmetatable(L,-2); /* set stored value as metatable */
lua_pushvalue(L,-1); /* stack: obj key met table table */
lua_pushvalue(L,2); /* stack: obj key mt table table key */
lua_insert(L,-2); /* stack: obj key mt table key table */
storeatubox(L,1); /* stack: obj key mt table */
return 1;
}
}
}
lua_settop(L,3);
}
lua_pushnil(L);
return 1;
}
else if (t== LUA_TTABLE)
{
module_index_event(L);
return 1;
}
lua_pushnil(L);
return 1;
}
/* Newindex function
* It first searches for a C/C++ varaible to be set.
* Then, it either stores it in the alternative ubox table (in the case it is
* an object) or in the own table (that represents the class or module).
*/
static int class_newindex_event (lua_State* L)
{
int t = lua_type(L,1);
if (t == LUA_TUSERDATA)
{
/* Try accessing a C/C++ variable to be set */
lua_getmetatable(L,1);
while (lua_istable(L,-1)) /* stack: t k v mt */
{
if (lua_isnumber(L,2)) /* check if key is a numeric value */
{
/* try operator[] */
lua_pushstring(L,".seti");
lua_rawget(L,-2); /* stack: obj key mt func */
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_pushvalue(L,3);
lua_call(L,3,0);
return 0;
}
}
else
{
lua_pushstring(L,".set");
lua_rawget(L,-2); /* stack: t k v mt tset */
if (lua_istable(L,-1))
{
lua_pushvalue(L,2);
lua_rawget(L,-2); /* stack: t k v mt tset func */
if (lua_iscfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,3);
lua_call(L,2,0);
return 0;
}
lua_pop(L,1); /* stack: t k v mt tset */
}
lua_pop(L,1); /* stack: t k v mt */
if (!lua_getmetatable(L,-1)) /* stack: t k v mt mt */
lua_pushnil(L);
lua_remove(L,-2); /* stack: t k v mt */
}
}
lua_settop(L,3); /* stack: t k v */
/* then, store as a new field */
storeatubox(L,1);
}
else if (t== LUA_TTABLE)
{
module_newindex_event(L);
}
return 0;
}
static int class_call_event(lua_State* L) {
if (lua_istable(L, 1)) {
lua_pushstring(L, ".call");
lua_rawget(L, 1);
if (lua_isfunction(L, -1)) {
lua_insert(L, 1);
lua_call(L, lua_gettop(L)-1, 1);
return 1;
};
};
tolua_error(L,"Attempt to call a non-callable object.",NULL);
return 0;
};
static int do_operator (lua_State* L, const char* op)
{
if (lua_isuserdata(L,1))
{
/* Try metatables */
lua_pushvalue(L,1); /* stack: op1 op2 */
while (lua_getmetatable(L,-1))
{ /* stack: op1 op2 op1 mt */
lua_remove(L,-2); /* stack: op1 op2 mt */
lua_pushstring(L,op); /* stack: op1 op2 mt key */
lua_rawget(L,-2); /* stack: obj key mt func */
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_call(L,2,1);
return 1;
}
lua_settop(L,3);
}
}
tolua_error(L,"Attempt to perform operation on an invalid operand",NULL);
return 0;
}
static int class_add_event (lua_State* L)
{
return do_operator(L,".add");
}
static int class_sub_event (lua_State* L)
{
return do_operator(L,".sub");
}
static int class_mul_event (lua_State* L)
{
return do_operator(L,".mul");
}
static int class_div_event (lua_State* L)
{
return do_operator(L,".div");
}
static int class_lt_event (lua_State* L)
{
return do_operator(L,".lt");
}
static int class_le_event (lua_State* L)
{
return do_operator(L,".le");
}
static int class_eq_event (lua_State* L)
{
/* copying code from do_operator here to return false when no operator is found */
if (lua_isuserdata(L,1))
{
/* Try metatables */
lua_pushvalue(L,1); /* stack: op1 op2 */
while (lua_getmetatable(L,-1))
{ /* stack: op1 op2 op1 mt */
lua_remove(L,-2); /* stack: op1 op2 mt */
lua_pushstring(L,".eq"); /* stack: op1 op2 mt key */
lua_rawget(L,-2); /* stack: obj key mt func */
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_pushvalue(L,2);
lua_call(L,2,1);
return 1;
}
lua_settop(L,3);
}
}
lua_settop(L, 3);
lua_pushboolean(L, 0);
return 1;
}
/*
static int class_gc_event (lua_State* L)
{
void* u = *((void**)lua_touserdata(L,1));
fprintf(stderr, "collecting: looking at %p\n", u);
lua_pushstring(L,"tolua_gc");
lua_rawget(L,LUA_REGISTRYINDEX);
lua_pushlightuserdata(L,u);
lua_rawget(L,-2);
if (lua_isfunction(L,-1))
{
lua_pushvalue(L,1);
lua_call(L,1,0);
lua_pushlightuserdata(L,u);
lua_pushnil(L);
lua_rawset(L,-3);
}
lua_pop(L,2);
return 0;
}
*/
TOLUA_API int class_gc_event (lua_State* L)
{
void* u = *((void**)lua_touserdata(L,1));
int top;
/*fprintf(stderr, "collecting: looking at %p\n", u);*/
/*
lua_pushstring(L,"tolua_gc");
lua_rawget(L,LUA_REGISTRYINDEX);
*/
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushlightuserdata(L,u);
lua_rawget(L,-2); /* stack: gc umt */
lua_getmetatable(L,1); /* stack: gc umt mt */
/*fprintf(stderr, "checking type\n");*/
top = lua_gettop(L);
if (tolua_fast_isa(L,top,top-1, lua_upvalueindex(2))) /* make sure we collect correct type */
{
/*fprintf(stderr, "Found type!\n");*/
/* get gc function */
lua_pushliteral(L,".collector");
lua_rawget(L,-2); /* stack: gc umt mt collector */
if (lua_isfunction(L,-1)) {
/*fprintf(stderr, "Found .collector!\n");*/
}
else {
lua_pop(L,1);
/*fprintf(stderr, "Using default cleanup\n");*/
lua_pushcfunction(L,tolua_default_collect);
}
lua_pushvalue(L,1); /* stack: gc umt mt collector u */
lua_call(L,1,0);
lua_pushlightuserdata(L,u); /* stack: gc umt mt u */
lua_pushnil(L); /* stack: gc umt mt u nil */
lua_rawset(L,-5); /* stack: gc umt mt */
}
lua_pop(L,3);
return 0;
}
/* Register module events
* It expects the metatable on the top of the stack
*/
TOLUA_API void tolua_moduleevents (lua_State* L)
{
lua_pushstring(L,"__index");
lua_pushcfunction(L,module_index_event);
lua_rawset(L,-3);
lua_pushstring(L,"__newindex");
lua_pushcfunction(L,module_newindex_event);
lua_rawset(L,-3);
}
/* Check if the object on the top has a module metatable
*/
TOLUA_API int tolua_ismodulemetatable (lua_State* L)
{
int r = 0;
if (lua_getmetatable(L,-1))
{
lua_pushstring(L,"__index");
lua_rawget(L,-2);
r = (lua_tocfunction(L,-1) == module_index_event);
lua_pop(L,2);
}
return r;
}
/* Register class events
* It expects the metatable on the top of the stack
*/
TOLUA_API void tolua_classevents (lua_State* L)
{
lua_pushstring(L,"__index");
lua_pushcfunction(L,class_index_event);
lua_rawset(L,-3);
lua_pushstring(L,"__newindex");
lua_pushcfunction(L,class_newindex_event);
lua_rawset(L,-3);
lua_pushstring(L,"__add");
lua_pushcfunction(L,class_add_event);
lua_rawset(L,-3);
lua_pushstring(L,"__sub");
lua_pushcfunction(L,class_sub_event);
lua_rawset(L,-3);
lua_pushstring(L,"__mul");
lua_pushcfunction(L,class_mul_event);
lua_rawset(L,-3);
lua_pushstring(L,"__div");
lua_pushcfunction(L,class_div_event);
lua_rawset(L,-3);
lua_pushstring(L,"__lt");
lua_pushcfunction(L,class_lt_event);
lua_rawset(L,-3);
lua_pushstring(L,"__le");
lua_pushcfunction(L,class_le_event);
lua_rawset(L,-3);
lua_pushstring(L,"__eq");
lua_pushcfunction(L,class_eq_event);
lua_rawset(L,-3);
lua_pushstring(L,"__call");
lua_pushcfunction(L,class_call_event);
lua_rawset(L,-3);
lua_pushstring(L,"__gc");
lua_pushstring(L, "tolua_gc_event");
lua_rawget(L, LUA_REGISTRYINDEX);
/*lua_pushcfunction(L,class_gc_event);*/
lua_rawset(L,-3);
}