//----------------------------------------------------------------// uintptr MOAISerializer::AffirmMemberID ( MOAILuaState& state, int idx ) { // if we're an object, affirm as such... if ( state.IsType ( idx, LUA_TUSERDATA )) { return this->AffirmMemberID ( state.GetLuaObject < MOAILuaObject >( -1 )); } // bail if we're not a table if ( !state.IsType ( idx, LUA_TTABLE )) return 0; // get the table's address uintptr memberID = ( uintptr )lua_topointer ( state, idx ); // bail if the table's already been added if ( this->mTableMap.contains ( memberID )) return memberID; // add the ref now to avoid cycles this->mTableMap [ memberID ].SetStrongRef ( state, idx ); // follow the table's refs to make sure everything gets added u32 itr = state.PushTableItr ( idx ); while ( state.TableItrNext ( itr )) { this->AffirmMemberID ( state, -1 ); } return memberID; }
//----------------------------------------------------------------// int MOAILuaObject::_gc ( lua_State* L ) { MOAILuaState state ( L ); MOAILuaObject* self = ( MOAILuaObject* )state.GetPtrUserData ( 1 ); self->mCollected = true; if ( MOAILuaRuntime::IsValid ()) { if ( self->mFinalizer ) { self->mFinalizer.PushRef ( state ); if ( state.IsType ( -1, LUA_TFUNCTION )) { state.DebugCall ( 0, 0 ); } else if ( state.IsType ( -1, LUA_TSTRING )) { printf ( "%s\n", state.GetValue < cc8* >( -1, "" )); } else { state.Pop ( 1 ); } self->mFinalizer.Clear (); } if ( MOAILuaRuntime::Get ().mReportGC ) { printf ( "GC %s <%p>\n", self->TypeName (), self ); } } if ( self->GetRefCount () == 0 ) { delete ( self ); } return 0; }
/** @name serializeToString @text Serializes the specified table or object to a string. @overload @in table data The table to serialize. @out string serialized The serialized string. @overload @in MOAILuaObject data The object to serialize. @out string serialized The serialized string. */ int MOAISerializer::_serializeToString ( lua_State* L ) { MOAILuaState state ( L ); if ( !( state.IsType ( 1, LUA_TTABLE ) || state.IsType ( 1, LUA_TUSERDATA ))) return 0; MOAISerializer serializer; serializer.AddLuaReturn ( state, 1 ); STLString result = serializer.SerializeToString (); lua_pushstring ( state, result ); return 1; }
/** @name serializeToFile @text Serializes the specified table or object to a file. @overload @in string filename The file to create. @in table data The table to serialize. @out nil @overload @in string filename The file to create. @in MOAILuaObject data The object to serialize. @out nil */ int MOAISerializer::_serializeToFile ( lua_State* L ) { MOAILuaState state ( L ); if ( !( state.IsType ( 1, LUA_TSTRING ))) return 0; if ( !( state.IsType ( 2, LUA_TTABLE ) || state.IsType ( 2, LUA_TUSERDATA ))) return 0; cc8* filename = state.GetValue < cc8* >( 1, "" ); MOAISerializer serializer; serializer.AddLuaReturn ( state, 2 ); serializer.SerializeToFile ( filename ); return 0; }
//----------------------------------------------------------------// int MOAILuaObject::_tostring ( lua_State* L ) { MOAILuaState state ( L ); MOAILuaObject* data = ( MOAILuaObject* )state.GetPtrUserData ( 1 ); if ( data ) { STLString str; lua_getfield ( state, 1, "getClassName" ); if ( state.IsType ( -1, LUA_TFUNCTION )) { lua_pushvalue ( state, 1 ); state.DebugCall ( 1, 1 ); cc8* classname = state.GetValue < cc8* >( -1, "" ); str.write ( "%p <%s>", data, classname ); state.Push ( str ); return 1; } str.write ( "%p <%s>", data, data->TypeName ()); state.Push ( str ); return 1; } return 0; }
/** @name base64Encode @text If a string is provided, encodes it in base64. Otherwise, encodes the current data stored in this object as a base64 encoded sequence of characters. @opt MOAIDataBuffer self @opt string data The string data to encode. You must either provide either a MOAIDataBuffer (via a :base64Encode type call) or string data (via a .base64Encode type call), but not both. @out string output If passed a string, returns either a string or nil depending on whether it could be encoded. Otherwise the encoding occurs inline on the existing data buffer in this object, and nil is returned. */ int MOAIDataBuffer::_base64Encode ( lua_State* L ) { MOAILuaState state ( L ); if ( state.IsType ( 1, LUA_TSTRING )) { return state.Base64Encode ( 1 ) ? 1 : 0; } MOAIDataBuffer* self = state.GetLuaObject < MOAIDataBuffer >( 1, true ); if ( self ) { if ( state.IsType ( 2, LUA_TSTRING )) { size_t len; cc8* str = lua_tolstring ( state, 2, &len ); self->Load (( void* )str, len ); } self->Base64Encode (); } return 0; }
/** @name setValue @text Sets an environment value and also triggers the listener callback (if any). @in string key @opt variant value Default value is nil. @out nil */ int MOAIEnvironment::_setValue ( lua_State* L ) { MOAILuaState state ( L ); if ( state.IsType ( 1, LUA_TSTRING )) { MOAIEnvironment& environment = MOAIEnvironment::Get (); environment.SetValue ( state ); } return 0; }
//----------------------------------------------------------------// // TODO: doxygen int MOAIMath::_randSFMT ( lua_State* L ) { MOAILuaState state ( L ); double lower = 0.0; double upper = 1.0; if ( state.IsType ( 1, LUA_TNUMBER )) { upper = state.GetValue < double >( 1, 0.0 ); if ( state.IsType ( 2, LUA_TNUMBER )) { lower = upper; upper = state.GetValue < double >( 2, 0.0 ); } } double r = sfmt_genrand_real1 ( MOAIMath::Get ().mSFMT ); // [0, 1] state.Push ( lower + ( r * ( upper - lower ))); return 1; }
//----------------------------------------------------------------// bool MOAILuaObject::IsMoaiUserdata ( MOAILuaState& state, int idx ) { bool result = false; if ( state.IsType ( idx, LUA_TUSERDATA )) { if ( lua_getmetatable ( state, idx )) { result = state.HasField ( -1, MOAI_TAG ); state.Pop ( 1 ); } } return result; }
//----------------------------------------------------------------// void MOAILuaObject::BindToLuaWithTable ( MOAILuaState& state ) { assert ( !this->mUserdata ); assert ( state.IsType ( -1, LUA_TTABLE )); MOAILuaClass* type = this->GetLuaClass (); assert ( type ); // create and initialize a new userdata state.PushPtrUserData ( this ); // create and initialize the private table lua_newtable ( state ); // set the ref to the private table lua_pushvalue ( state, -3 ); lua_setfield ( state, -2, LUA_MEMBER_TABLE_NAME ); // initialize the private table lua_pushcfunction ( state, MOAILuaObject::_gc ); lua_setfield ( state, -2, "__gc" ); lua_pushcfunction ( state, MOAILuaObject::_tostring ); lua_setfield ( state, -2, "__tostring" ); lua_pushcfunction ( state, MOAILuaObject::_index ); lua_setfield ( state, -2, "__index" ); lua_pushcfunction ( state, MOAILuaObject::_newindex ); lua_setfield ( state, -2, "__newindex" ); // make the interface table the instance table's meta type->PushInterfaceTable ( state ); lua_setmetatable ( state, -2 ); // grab a ref to the instance table; attach it to the userdata this->mInstanceTable = state.GetWeakRef ( -1 ); lua_setmetatable ( state, -2 ); // and take a ref back to the userdata if ( this->GetRefCount () == 0 ) { this->mUserdata.SetWeakRef ( state, -1 ); } else { this->mUserdata.SetStrongRef ( state, -1 ); } // overwrite the member table lua_replace ( state, -2 ); assert ( !lua_isnil ( state, -1 )); }
//----------------------------------------------------------------// int MOAILuaClass::_new ( lua_State* L ) { // upvalues: // 1: interface table // 2: original 'new' MOAILuaState state ( L ); lua_pushvalue ( L, lua_upvalueindex ( 2 )); if ( state.IsType ( -1, LUA_TFUNCTION )) { // call the original new state.DebugCall ( 0, 1 ); if ( state.IsType ( -1, LUA_TUSERDATA )) { // get the ref table if ( lua_getmetatable ( state, -1 )) { // get the member table if ( lua_getmetatable ( state, -1 )) { // get the interface table lua_pushvalue ( L, lua_upvalueindex ( 1 )); // set the interface table as the metatable lua_setmetatable ( L, -2 ); // done with the member table lua_pop ( L, 1 ); } // done with the ref table lua_pop ( L, 1 ); } } return 1; } return 0; }
/** @name encode @text Encode a hierarchy of Lua tables into a JSON string. @in table input @out string result */ int MOAIJsonParser::_encode ( lua_State* L ) { MOAILuaState state ( L ); if ( state.IsType ( 1, LUA_TTABLE )) { json_t* json = _luaToJSON ( state, 1 ); if ( json ) { int flags = state.IsType ( 2, 0 ); char* str = json_dumps ( json, flags ); json_decref ( json ); if ( str ) { lua_pushstring ( state, str ); free ( str ); return 1; } } } return 0; }
/** @name inflate @text Decompresses the string or the current data stored in this object using the DEFLATE algorithm. @opt MOAIDataBuffer self @opt string data The string data to inflate. You must either provide either a MOAIDataBuffer (via a :base64Decode type call) or string data (via a .base64Decode type call), but not both. @in number windowBits The window bits used in the DEFLATE algorithm. Pass nil to use the default value. @out string output If passed a string, returns either a string or nil depending on whether it could be decompressed. Otherwise the decompression occurs inline on the existing data buffer in this object, and nil is returned. */ int MOAIDataBuffer::_inflate ( lua_State* L ) { MOAILuaState state ( L ); int windowBits = state.GetValue < int >( 2, USDeflateReader::DEFAULT_WBITS ); if ( state.IsType ( 1, LUA_TSTRING )) { return state.Inflate ( 1, windowBits ) ? 1 : 0; } MOAIDataBuffer* self = state.GetLuaObject < MOAIDataBuffer >( 1, true ); if ( self ) { self->Inflate ( windowBits ); } return 0; }
/** @name showDialog @text Show a native dialog to the user. @in string title The title of the dialog box. Can be nil. @in string message The message to show the user. Can be nil. @in string positive The text for the positive response dialog button. Can be nil. @in string neutral The text for the neutral response dialog button. Can be nil. @in string negative The text for the negative response dialog button. Can be nil. @in bool cancelable Specifies whether or not the dialog is cancelable @opt function callback A function to callback when the dialog is dismissed. Default is nil. @out nil */ int MOAIDialogAndroid::_showDialog ( lua_State* L ) { MOAILuaState state ( L ); cc8* title = lua_tostring ( state, 1 ); cc8* message = lua_tostring ( state, 2 ); cc8* positive = lua_tostring ( state, 3 ); cc8* neutral = lua_tostring ( state, 4 ); cc8* negative = lua_tostring ( state, 5 ); bool cancelable = lua_toboolean ( state, 6 ); if ( state.IsType ( 7, LUA_TFUNCTION )) { // NOTE: This is fragile. We're storing the callback function in a global variable, // effectively. Invoking the showDialog method multiple times in succession can // therefore lead to unpredictable results. In fact, it's unknown how Android itself // handles multiple invocations - are they queued? On iOS, UIAlertView is LIFO and // new invocations supersede previous ones, but once dismissed, the system continues // down the alert stack. MOAIDialogAndroid::Get ().mDialogCallback.SetStrongRef ( state, 7 ); } JNI_GET_ENV ( jvm, env ); JNI_GET_JSTRING ( title, jtitle ); JNI_GET_JSTRING ( message, jmessage ); JNI_GET_JSTRING ( positive, jpositive ); JNI_GET_JSTRING ( neutral, jneutral ); JNI_GET_JSTRING ( negative, jnegative ); jclass moai = env->FindClass ( "com/ziplinegames/moai/Moai" ); if ( moai == NULL ) { ZLLog::Print ( "MOAIDialogAndroid: Unable to find java class %s", "com/ziplinegames/moai/Moai" ); } else { jmethodID showDialog = env->GetStaticMethodID ( moai, "showDialog", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V" ); if ( showDialog == NULL ) { ZLLog::Print ( "MOAIDialogAndroid: Unable to find static java method %s", "showDialog" ); } else { env->CallStaticVoidMethod ( moai, showDialog, jtitle, jmessage, jpositive, jneutral, jnegative, cancelable ); } } return 0; }
/** @name deflate @text Compresses the string or the current data stored in this object using the DEFLATE algorithm. @opt MOAIDataBuffer self @opt string data The string data to deflate. You must either provide either a MOAIDataBuffer (via a :base64Decode type call) or string data (via a .base64Decode type call), but not both. @in number level The level used in the DEFLATE algorithm. Pass nil to use the default value. @in number windowBits The window bits used in the DEFLATE algorithm. Pass nil to use the default value. @out string output If passed a string, returns either a string or nil depending on whether it could be compressed. Otherwise the compression occurs inline on the existing data buffer in this object, and nil is returned. */ int MOAIDataBuffer::_deflate ( lua_State* L ) { MOAILuaState state ( L ); int level = state.GetValue < int >( 2, USDeflateWriter::DEFAULT_LEVEL ); int windowBits = state.GetValue < int >( 3, USDeflateWriter::DEFAULT_WBITS ); if ( state.IsType ( 1, LUA_TSTRING )) { return state.Deflate ( 1, level, windowBits ) ? 1 : 0; } MOAIDataBuffer* self = state.GetLuaObject < MOAIDataBuffer >( 1, true ); if ( self ) { self->Deflate ( level, windowBits ); } return 0; }
/** @name decode @text Decode a JSON string into a hierarchy of Lua tables. @in string input @out table result */ int MOAIJsonParser::_decode ( lua_State* L ) { UNUSED ( L ); MOAILuaState state ( L ); if ( state.IsType ( 1, LUA_TSTRING )) { size_t bufflen; cc8* buffer = lua_tolstring ( L, -1, &bufflen ); json_error_t error; json_t* json = json_loadb ( buffer, bufflen, JSON_DISABLE_EOF_CHECK, &error ); if ( json ) { _jsonToLua ( L, json ); json_decref ( json ); return 1; } } return 0; }
/** @lua decode @text Decode a JSON string into a hierarchy of Lua tables. @in string input @out table result */ int MOAIJsonParser::_decode ( lua_State* L ) { UNUSED ( L ); MOAILuaState state ( L ); if ( state.IsType ( 1, LUA_TSTRING )) { size_t bufflen; cc8* buffer = lua_tolstring ( L, -1, &bufflen ); json_error_t error; json_t* json = json_loadb ( buffer, bufflen, JSON_DISABLE_EOF_CHECK, &error ); if ( json ) { _jsonToLua ( L, json ); json_decref ( json ); return 1; } else { printf ( "%d %d %d %s %s\n", error.line, error.column, error.position, error.source, error.text ); } } return 0; }
/** @name toCppHeader @text Convert data to CPP header file. @overload @in string data The string data to encode @in string name @opt number columns Default value is 12 @out string output @overload @in MOAIDataBuffer data The data buffer to encode @in string name @opt number columns Default value is 12 @out string output */ int MOAIDataBuffer::_toCppHeader ( lua_State* L ) { MOAILuaState state ( L ); cc8* name = state.GetValue < cc8* >( 2, "" ); u32 columns = state.GetValue < u32 >( 3, 12 ); if ( !strlen ( name )) return 0; USMemStream memStream; if ( state.IsType ( 1, LUA_TSTRING )) { size_t size; const void* bytes = lua_tolstring ( state, 1, &size ); USHexDump::DumpAsCPPHeader ( memStream, name, bytes, size, columns ); } MOAIDataBuffer* dataBuffer = state.GetLuaObject < MOAIDataBuffer >( 1, true ); if ( dataBuffer ) { size_t size; void* bytes; dataBuffer->Lock ( &bytes, &size ); USHexDump::DumpAsCPPHeader ( memStream, name, bytes, size, columns ); } if ( memStream.GetLength ()) { memStream.Seek ( 0, SEEK_SET ); STLString result = memStream.ReadString ( memStream.GetLength ()); lua_pushstring ( state, result ); return 1; } return 0; }
//----------------------------------------------------------------// int MOAIApp::_showDialog ( lua_State* L ) { MOAILuaState state ( L ); cc8* title = state.GetValue < cc8* >( 1, "" ); cc8* message = state.GetValue < cc8* >( 2, "" ); cc8* positive = state.GetValue < cc8* >( 3, "" ); cc8* neutral = state.GetValue < cc8* >( 4, "" ); cc8* negative = state.GetValue < cc8* >( 5, "" ); bool cancelable = state.GetValue < bool >( 6, "" ); if ( state.IsType ( 7, LUA_TFUNCTION )) { // NOTE: This is fragile. We're storing the callback function in a global variable, // effectively. Invoking the showDialog method multiple times in succession can // therefore lead to unpredictable results. In fact, it's unknown how Android itself // handles multiple invocations - are they queued? On iOS, UIAlertView is LIFO and // new invocations supersede previous ones, but once dismissed, the system continues // down the alert stack... MOAIApp::Get ().mDialogCallback.SetStrongRef ( state, 7 ); } MOAIApp::Get ().showDialogFunc ( title, message, positive, neutral, negative, cancelable ); return 0; }
/** @name init @text Initialize AdColony. @in string appId Available in AdColony dashboard settings. @in table zones A list of zones to configure. Available in AdColony dashboard settings. @out nil */ int MOAIAdColonyAndroid::_init ( lua_State* L ) { MOAILuaState state ( L ); cc8* identifier = lua_tostring ( state, 1 ); JNI_GET_ENV ( jvm, env ); JNI_GET_JSTRING ( identifier, jidentifier ); jobjectArray jzones = NULL; if ( state.IsType ( 2, LUA_TTABLE )) { int numEntries = 0; for ( int key = 1; ; ++key ) { state.GetField ( 2, key ); cc8* value = _luaParseTable ( state, -1 ); lua_pop ( state, 1 ); if ( !value ) { numEntries = key - 1; break; } } jzones = env->NewObjectArray ( numEntries, env->FindClass( "java/lang/String" ), 0 ); for ( int key = 1; ; ++key ) { state.GetField ( 2, key ); cc8* value = _luaParseTable ( state, -1 ); lua_pop ( state, 1 ); if ( value ) { JNI_GET_JSTRING ( value, jvalue ); env->SetObjectArrayElement ( jzones, key - 1, jvalue ); } else { break; } } } if ( jzones == NULL ) { jzones = env->NewObjectArray ( 0, env->FindClass( "java/lang/String" ), 0 ); } jclass adcolony = env->FindClass ( "com/ziplinegames/moai/MoaiAdColony" ); if ( adcolony == NULL ) { ZLLog::Print ( "MOAIAdColonyAndroid: Unable to find java class %s", "com/ziplinegames/moai/MoaiAdColony" ); } else { jmethodID init = env->GetStaticMethodID ( adcolony, "init", "(Ljava/lang/String;[Ljava/lang/String;)V" ); if ( init == NULL ) { ZLLog::Print ( "MOAIAdColonyAndroid: Unable to find static java method %s", "init" ); } else { env->CallStaticVoidMethod ( adcolony, init, jidentifier, jzones ); } } return 0; }
//----------------------------------------------------------------// int MOAILuaClass::_extendSingleton ( lua_State* L ) { MOAILuaState state ( L ); // upvalues: // 1: singleton userdata // 2: class table // set the userdata MOAILuaObject* luaData = ( MOAILuaObject* )state.GetPtrUserData ( lua_upvalueindex ( 1 )); state.PushPtrUserData ( luaData ); // clone the class table state.CloneTable ( lua_upvalueindex ( 2 )); lua_pushvalue ( state, -1 ); lua_setfield ( state, -2, "__index" ); lua_pushvalue ( state, -1 ); lua_setfield ( state, -2, "__newindex" ); // add getClassName to class table lua_pushvalue ( L, 1 ); lua_pushcclosure ( L, _getUpvalue, 1 ); lua_setfield ( L, -2, "getClassName" ); // copy the extended userdata lua_pushvalue ( L, -2 ); // copy the extended table lua_pushvalue ( L, -2 ); // push the 'extend' method with the singleton userdata and extended class table upvalues lua_pushcclosure ( L, _extendSingleton, 2 ); // set the extended 'extend' method... lua_setfield ( L, -2, "extend" ); // stack: // -1: extended class table // -2: extended userdata // call the extender if ( state.IsType ( 2, LUA_TFUNCTION )) { lua_pushvalue ( L, 2 ); lua_pushvalue ( L, -2 ); lua_pushvalue ( L, lua_upvalueindex ( 2 )); state.DebugCall ( 2, 0 ); } // stack: // -1: extended class table // -2: extended userdata // set the table as a metatable on the userdata lua_setmetatable ( L, -2 ); // and we're done cc8* classname = state.GetValue < cc8* >( 1, "" ); lua_setglobal ( state, classname ); return 0; }
/** @name login @text Prompt the user to login to Facebook. @opt table permissions Optional set of required permissions. See Facebook documentation for a full list. Default is nil. @out nil */ int MOAIFacebookAndroid::_login ( lua_State *L ) { MOAILuaState state ( L ); JNI_GET_ENV ( jvm, env ); jobjectArray jpermissions = NULL; if ( state.IsType ( 1, LUA_TTABLE )) { int numEntries = 0; for ( int key = 1; ; ++key ) { state.GetField ( 1, key ); cc8* value = _luaParseTable ( state, -1 ); lua_pop ( state, 1 ); if ( !value ) { numEntries = key - 1; break; } } jpermissions = env->NewObjectArray ( numEntries, env->FindClass( "java/lang/String" ), 0 ); for ( int key = 1; ; ++key ) { state.GetField ( 1, key ); cc8* value = _luaParseTable ( state, -1 ); lua_pop ( state, 1 ); if ( value ) { JNI_GET_JSTRING ( value, jvalue ); env->SetObjectArrayElement ( jpermissions, key - 1, jvalue ); } else { break; } } } if ( jpermissions == NULL ) { jpermissions = env->NewObjectArray ( 0, env->FindClass( "java/lang/String" ), 0 ); } jclass facebook = env->FindClass ( "com/ziplinegames/moai/MoaiFacebook" ); if ( facebook == NULL ) { ZLLog::Print ( "MOAIFacebookAndroid: Unable to find java class %s", "com/ziplinegames/moai/MoaiFacebook" ); } else { jmethodID login = env->GetStaticMethodID ( facebook, "login", "([Ljava/lang/String;)V" ); if ( login == NULL ) { ZLLog::Print ( "MOAIFacebookAndroid: Unable to find static java method %s", "login" ); } else { env->CallStaticVoidMethod ( facebook, login, jpermissions ); } } return 0; }
//----------------------------------------------------------------// int MOAILuaClass::_extendFactory ( lua_State* L ) { MOAILuaState state ( L ); // upvalues: // 1: class table // 2: interface table // clone the class table state.CloneTable ( lua_upvalueindex ( 1 )); // add getClassName to class table lua_pushvalue ( L, 1 ); lua_pushcclosure ( L, _getUpvalue, 1 ); lua_setfield ( L, -2, "getClassName" ); // clone the interface table state.CloneTable ( lua_upvalueindex ( 2 )); // set the interface table as its own __index lua_pushvalue ( state, -1 ); lua_setfield ( state, -2, "__index" ); // add getClass to interface table lua_pushvalue ( L, -2 ); lua_pushcclosure ( L, _getUpvalue, 1 ); lua_setfield ( L, -2, "getClass" ); // add getClassName to interface table lua_pushvalue ( L, 1 ); lua_pushcclosure ( L, _getUpvalue, 1 ); lua_setfield ( L, -2, "getClassName" ); // stack: // -1: interface table // -2: class table // copy the extended interface table lua_pushvalue ( L, -1 ); // get and push the original factory method lua_pushvalue ( L, lua_upvalueindex ( 1 )); lua_getfield ( L, -1, "new" ); lua_replace ( L, -2 ); // push the 'new' function with the interface table and original factory method as upvalues lua_pushcclosure ( L, _new, 2 ); // set the extended 'new' method into the class table lua_setfield ( L, -3, "new" ); // stack: // -1: interface table // -2: class table // now copy the extended class and interface tables lua_pushvalue ( L, -2 ); lua_pushvalue ( L, -2 ); // push the 'extend' method with the extended class and interface tables as upvalues lua_pushcclosure ( L, _extendFactory, 2 ); // set the extended 'extend' method into the class table lua_setfield ( L, -3, "extend" ); // stack: // -1: interface table // -2: class table // init the getInterfaceTable method lua_pushvalue ( L, -1 ); lua_pushcclosure ( state, _getInterfaceTable, 1 ); lua_setfield ( state, -3, "getInterfaceTable" ); // stack: // -1: interface table // -2: class table // call the extender if ( state.IsType ( 2, LUA_TFUNCTION )) { lua_pushvalue ( L, 2 ); // function lua_pushvalue ( L, -2 ); // interface table lua_pushvalue ( L, -4 ); // class table lua_pushvalue ( L, lua_upvalueindex ( 2 )); // super interface table lua_pushvalue ( L, lua_upvalueindex ( 1 )); // super class table state.DebugCall ( 4, 0 ); } // stack: // -1: interface table // -2: class table lua_pop ( L, 1 ); // stack: // -1: class table // and we're done cc8* classname = state.GetValue < cc8* >( 1, "" ); lua_setglobal ( state, classname ); return 0; }