aafef187ef
-jsoncpp -lua -tolua++ -WebServer -zlib -iniFile git-svn-id: http://mc-server.googlecode.com/svn/trunk@4 0a769ca7-a7f5-676a-18bf-c427514a06d6
537 lines
14 KiB
C
537 lines
14 KiB
C
/* 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);
|
|
}
|
|
|