diff --git a/stepmania/src/LuaBinding.cpp b/stepmania/src/LuaBinding.cpp new file mode 100644 index 0000000000..5f882cd139 --- /dev/null +++ b/stepmania/src/LuaBinding.cpp @@ -0,0 +1,89 @@ +#include "global.h" +#include "LuaBinding.h" + +void CreateGlobalTable( lua_State *L, const CString &szName ) +{ + lua_pushstring( L, szName ); + lua_rawget( L, LUA_GLOBALSINDEX ); + if( !lua_isnil(L, -1) ) + return; + + lua_pop( L, 1 ); + lua_newtable( L ); + lua_pushstring( L, szName ); + lua_pushvalue( L, -2 ); + lua_rawset( L, LUA_GLOBALSINDEX ); +} + +/* + * Get a userdata, and check that it's either szType or a type + * derived from szType, by walking the __index chain. + */ +bool CheckType( lua_State *L, int narg, const char *szType ) +{ + int iTop = lua_gettop(L); + lua_pushvalue( L, narg ); + + narg = lua_gettop(L); + while(1) + { + /* If the object on the stack has no metatable, it has no type; fail. */ + if( !lua_getmetatable(L, narg) ) + { + lua_settop( L, iTop ); + return false; + } + int iMetatable = lua_gettop(L); + + /* Look up the type name. */ + lua_pushstring( L, "__type" ); + lua_rawget( L, iMetatable ); + const char *szActualType = lua_tostring( L, -1 ); + + if( szActualType != NULL && !strcmp(szActualType, szType) ) + { + /* The type matches. */ + lua_settop( L, iTop ); + return true; + } + + /* The type doesn't match. Does the metatable have __index? */ + lua_pushstring( L, "__index" ); + lua_rawget( L, iMetatable ); + if( lua_isnil(L, -1) ) + { + /* There's no __index. The type doesn't match. */ + lua_settop( L, iTop ); + return false; + } + + /* Start over with __index. */ + lua_replace( L, narg ); + lua_pop( L, 1 ); + } +} + +/* + * (c) 2005 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/LuaBinding.h b/stepmania/src/LuaBinding.h index 18dc28cac3..eb65c1d873 100644 --- a/stepmania/src/LuaBinding.h +++ b/stepmania/src/LuaBinding.h @@ -27,26 +27,27 @@ struct RegType int (*mfunc)(T *p, lua_State *L); }; +void CreateGlobalTable( lua_State *L, const CString &szName ); +bool CheckType( lua_State *L, int narg, const char *szType ); + template class Luna { typedef struct { T *pT; } userdataType; + public: - + static void Register(lua_State *L) { - lua_newtable(L); + /* Create the methods table, if it doesn't already exist. */ + CreateGlobalTable( L, m_sClassName ); + int methods = lua_gettop(L); + /* Create a metatable for the userdata objects. */ luaL_newmetatable(L, m_sClassName); int metatable = lua_gettop(L); - // store method table in globals so that - // scripts can add functions written in Lua. - lua_pushstring(L, m_sClassName); - lua_pushvalue(L, methods); - lua_settable(L, LUA_GLOBALSINDEX); - lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methods); lua_settable(L, metatable); // hide metatable from Lua getmetatable() @@ -72,15 +73,41 @@ public: lua_pushcclosure(L, thunk, 1); lua_settable(L, methods); } - + + /* Create a metatable for the methods table. */ + lua_newtable( L ); + int methods_metatable = lua_gettop(L); + + // Hide the metatable. + lua_pushliteral( L, "__metatable" ); + lua_pushstring( L, "(hidden)" ); + lua_settable( L, methods_metatable ); + + if( strcmp(m_sBaseClassName, "none") ) + { + // If this type has a base class, set the __index of this type + // to the base class. If the base class doesn't exist, we probably + // were called before the Register() of the base class; we'll fill + // it in when we get to it. + lua_pushliteral( L, "__index" ); + CreateGlobalTable( L, m_sBaseClassName ); + lua_settable( L, methods_metatable ); + } + + lua_pushliteral( L, "__type" ); + lua_pushstring( L, m_sClassName ); + lua_settable( L, methods_metatable ); + + /* Set and pop the methods metatable. */ + lua_setmetatable( L, methods ); + lua_pop(L, 2); // drop metatable and method table } // get userdata from Lua stack and return pointer to T object static T *check( lua_State *L, int narg, bool bIsSelf = false ) { - userdataType *pUserdata = static_cast( luaL_checkudata(L, narg, m_sClassName) ); - if( pUserdata == NULL ) + if( !CheckType(L, narg, m_sClassName) ) { if( bIsSelf ) luaL_typerror( L, narg, m_sClassName ); @@ -88,6 +115,7 @@ public: LuaHelpers::TypeError( L, narg, m_sClassName ); } + userdataType *pUserdata = static_cast( lua_touserdata(L, narg) ); return pUserdata->pT; // pointer to T object } @@ -135,7 +163,8 @@ public: s_pvMethods = new RegTypeVector; } private: - static const char m_sClassName[]; + static const char *m_sClassName; + static const char *m_sBaseClassName; static int tostring_T (lua_State *L) { @@ -148,9 +177,12 @@ private: } }; - #define LUA_REGISTER_CLASS( T ) \ - template<> const char Luna::m_sClassName[] = #T; \ + LUA_REGISTER_DERIVED_CLASS( T, none ) + +#define LUA_REGISTER_DERIVED_CLASS( T, B ) \ + template<> const char *Luna::m_sClassName = #T; \ + template<> const char *Luna::m_sBaseClassName = #B; \ template<> Luna::RegTypeVector* Luna::s_pvMethods = NULL; \ static Luna##T registera; \ void T::PushSelf( lua_State *L ) { Luna##T::Push( L, this ); } \