text: /* 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);
  }
  
  #include "toluaext_event.inl"
  