UE4 Unlua源码解析4 - LuaCore内重要方法逐行解释

Posted 珞珈大胖强TURBO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UE4 Unlua源码解析4 - LuaCore内重要方法逐行解释相关的知识,希望对你有一定的参考价值。

LuaCore内重要方法逐行解释

1 Global_RegisterClass

根据之前的介绍,代码很容易读,读虚拟栈的大小,当小于1的时候返回,否则调用RegisterClass,参数传(虚拟机,栈底的值转成C 的string)

2 RegisterClass

总结:

1 记录反射信息

根据传入的类型名,通过UE反射得到它的类型信息,然后记录这个类型信息,以FClassDesc(UnLua自己的数据结构)的形式存入UnLua的反射库GReflectionRegistry中,方便后续Lua、C++交互调用

2 创建元表 为传入的类型,以及它所有的父类,依次创建一个元表,然后设置一些元方法,最后记录在Lua的G表中,这样下次在Lua里调这个类型名,获得的就是创建出的G表。调这个类型里的方法,就会索引到当初注册时赋予的元方法

class FClassDesc* RegisterClass(lua_State *L, const char *ClassName, const char *SuperClassName = nullptr);

形参为虚拟机,类名,父类名

1735 - 1739 类名非空判断

1741 声明类名String

1742 声明继承链数组

1743-1753 调用GReflectionRegistry.RegisterClass生成一些辅助信息,包括类的FClassDesc,名称以及Struct到FClassDesc的字典,供Unlua使用

1754 用虚拟机,生成的FClassDesc,以及继承链作为参数,调用LuaCore的RegisterClassInternal,开始生成元表信息。

3 RegisterClassInternal

1699 通过ClassDesc拿到类名

1701 通过luaL_getmetatable方法在注册表里找类名对应的元表

1703 luaL_getmetatable不论如何都会将结果入栈,所以出栈

1704-1708 找到了不需要再注册

IExportedClass 可导出类

1710 创建静态导出类数组 ExportedClasses

1711 找到祖宗类对应的IExportedClass,静态导出反射类

1712-1716 祖宗类的导出类加入ExportedClasses,祖宗类调用RegisterClassCore

1718-1726 从顶层到子类逐渐调用RegisterClassCore,分开的原因是RegisterClassCore的参数父类,祖宗类要传nullptr

4 RegisterClassCore 较复杂120行
static bool RegisterClassCore(lua_State *L, FClassDesc *InClass, const FClassDesc *InSuperClass, UnLua::IExportedClass **ExportedClasses, int32 NumExportedClasses)

#####1
    check(InClass);
    const TCHAR *InClassName = *InClass->GetName();
    TStringConversion<TStringConvert<TCHAR, ANSICHAR>> ClassName(InClassName);
#####1
#####2
    int32 Type = luaL_getmetatable(L, ClassName.Get());
    if (Type == LUA_TTABLE)
    
        UE_LOG(LogUnLua, Verbose, TEXT("%s: Class %s is already registered!"), ANSI_TO_TCHAR(__FUNCTION__), InClassName);
        lua_pop(L, 1);
        return true;
    
#####2
#####3
    InClass->AddRef();

    lua_pop(L, 1);
    luaL_newmetatable(L, ClassName.Get());                  // 1, will be used as meta table later (lua_setmetatable)
#####3
#####4
    const TCHAR *InSuperClassName = nullptr;
    if (InSuperClass)
    
        InSuperClassName = *InSuperClass->GetName();
        lua_pushstring(L, "ParentClass");                   // 2
        Type = luaL_getmetatable(L, TCHAR_TO_ANSI(InSuperClassName));
        if (Type != LUA_TTABLE)
        
            UNLUA_LOGERROR(L, LogUnLua, Warning, TEXT("%s, Invalid super class %s!"), ANSI_TO_TCHAR(__FUNCTION__), InSuperClassName);
        
        lua_rawset(L, -3);
    
#####4
#####5
    lua_pushstring(L, "__index");                           // 2
    lua_pushcfunction(L, Class_Index);                      // 3
    lua_rawset(L, -3);

    lua_pushstring(L, "__newindex");                        // 2
    lua_pushcfunction(L, Class_NewIndex);                   // 3
    lua_rawset(L, -3);
#####5
    UScriptStruct *ScriptStruct = InClass->AsScriptStruct();
    if (ScriptStruct)
    
#####6
        lua_pushlightuserdata(L, InClass);                  // FClassDesc

        lua_pushstring(L, "Copy");                          // Key
        lua_pushvalue(L, -2);                               // FClassDesc
        lua_pushcclosure(L, ScriptStruct_Copy, 1);          // closure
        lua_rawset(L, -4);

        lua_pushstring(L, "__eq");                          // Key
        lua_pushvalue(L, -2);                               // FClassDesc
        lua_pushcclosure(L, ScriptStruct_Compare, 1);       // closure
        lua_rawset(L, -4);

        if (!(ScriptStruct->StructFlags & (STRUCT_IsPlainOldData | STRUCT_NoDestructor)))
        
            lua_pushstring(L, "__gc");                      // Key
            lua_pushvalue(L, -2);                           // FClassDesc
            lua_pushcclosure(L, ScriptStruct_Delete, 1);    // closure
            lua_rawset(L, -4);
        

        lua_pushstring(L, "__call");                        // Key
        lua_pushvalue(L, -2);                               // FClassDesc
        lua_pushcclosure(L, ScriptStruct_New, 1);           // closure
        lua_rawset(L, -4);

        lua_pop(L, 1);
    
#####6
#####7
    else
    
        UClass *Class = InClass->AsClass();
        if (Class != UObject::StaticClass() && Class != UClass::StaticClass())
        
            lua_pushstring(L, "ClassDesc");                 // Key
            lua_pushlightuserdata(L, InClass);              // FClassDesc
            lua_rawset(L, -3);

            lua_pushstring(L, "StaticClass");               // Key
            lua_pushlightuserdata(L, InClass);              // FClassDesc
            lua_pushcclosure(L, Class_StaticClass, 1);      // closure
            lua_rawset(L, -3);

            lua_pushstring(L, "Cast");                      // Key
            lua_pushcfunction(L, Class_Cast);               // C function
            lua_rawset(L, -3);

            lua_pushstring(L, "__eq");                      // Key
            lua_pushcfunction(L, UObject_Identical);        // C function
            lua_rawset(L, -3);

            lua_pushstring(L, "__gc");                      // Key
            lua_pushcfunction(L, UObject_Delete);           // C function
            lua_rawset(L, -3);
        
    
#####7
#####8 
    lua_pushvalue(L, -1);                                   // set metatable to self
    lua_setmetatable(L, -2);
#####8
    if (ExportedClasses)
    
        for (int32 i = 0; i < NumExportedClasses; ++i)
        
            ExportedClasses[i]->Register(L);
        
    
#####9 
    SetTableForClass(L, ClassName.Get());
#####9
    if (!InClass->IsNative())
    
        GLuaCxt->AddLibraryName(InClassName);
    

    UE_LOG(LogUnLua, Verbose, TEXT("Succeed to register class %s, super class is %s!"), InClassName, InSuperClassName ? InSuperClassName : TEXT("NULL"));

    return true;

#####1 判断空,并且拿到InClass的名称,转成luaL_getmetatable可用的TStringConversion

#####2 尝试取InName名称对应的元表,如果拿到说明注册过了,结束,结束之前记得通过lua_pop(L, 1);,把luaL_getmetatable压入栈的参数弹出

#####3 如果没拿到,说明没注册,开始注册, InClass加引用,估计是防止被垃圾回收,然后把luaL_getmetatable压入栈的参数弹出,然后通过luaL_newmetatable创建元表,元表入栈,位置是-1

#####4 如果父类在,把"ParentClass"入栈,此时#####3中创建的元表位置为-2,将InSuperClassName拿到的元表入栈,此时#####3中创建的元表位置为-3,-2为"ParentClass",-1为InSuperClassName拿到的元表

lua_rawset(L, -3);将#####3中创建的元表中设置"ParentClass"为父类元表

#####5 设置InClass的元表的元方法index 和newindex为C方法Class_Index,Class_NewIndex、

#####6

#####7 当InClass是一个类的时候,将#####3中创建的元表中设置"ClassDesc"为InClass的C Value,

将#####3中创建的元表中设置"StaticClass"为InClass作为upvalue,方法为Class_StaticClass的闭包

将#####3中创建的元表中设置"Cast"为Class_Cast,__eq为UObject_Identical,gc为UObject_Delete

#####8 设置#####3中创建的元表的元表为self

#####9 将#####3中创建的元表设置为全局表中name对应的值

总结:

根据FClassDesc中的名称创建表,设置表元表为self,设置各种元方法

5 SetTableForClass

把栈顶的表放到lua全局表中,这样_G就可以访问到Name对应的表

6 Class_Index

7 GetField

GetField函数主要做了两件事:

(1)获取FPropertyDesc或者FFunctionDesc。根据传入名字找对应元表,进而找到类型名和Field名,然后去UnLua反射系统中获取对应的FPropertyDesc或者FFunctionDesc,如果FClassDesc缓存里有则直接获取,如果没有,就通过UE4反射获取并缓存到FClassDesc里

(2)将FPropertyDesc或者FFunctionDesc设置到元表中,同时将FPropertyDesc或者FFunctionDesc压入lua栈中

lua调用index元方法的时候,传入的参数是索引表和索引,根据C和lua交互机制,传入的参数入虚拟栈,索引表在虚拟栈中的位置是1,索引在虚拟栈中的位置是2

###1将索引表的元表入虚拟栈,索引表的元表在虚拟栈中的位置是3

static int32 GetField(lua_State* L)

###1
    int32 Type = lua_getmetatable(L, 1);       // get meta table of table/userdata (first parameter passed in)
    check(Type == 1 && lua_istable(L, -1));
###1
    lua_pushvalue(L, 2);                    // push key
    Type = lua_rawget(L, -2);

    if (Type == LUA_TNIL)
    
        lua_pop(L, 1);

        lua_pushstring(L, "__name");
        Type = lua_rawget(L, -2);
        check(Type == LUA_TSTRING);

        const char* ClassName = lua_tostring(L, -1);
        const char* FieldName = lua_tostring(L, 2);
        lua_pop(L, 1);

        // desc maybe released on c++ side,but lua side may still hold it
        FClassDesc* ClassDesc = GReflectionRegistry.FindClass(ClassName);
        if (!ClassDesc)
        
            lua_pushnil(L);
        
        else
        
            FScopedSafeClass SafeClass(ClassDesc);
            FFieldDesc* Field = ClassDesc->RegisterField(FieldName, ClassDesc);
            if (Field && Field->IsValid())
            
                bool bCached = false;
                bool bInherited = Field->IsInherited();
                if (bInherited)
                
                    FString SuperStructName = Field->GetOuterName();
                    Type = luaL_getmetatable(L, TCHAR_TO_UTF8(*SuperStructName));
                    check(Type == LUA_TTABLE);
                    lua_pushvalue(L, 2);
                    Type = lua_rawget(L, -2);
                    bCached = Type != LUA_TNIL;
                    if (!bCached)
                    
                        lua_pop(L, 1);
                    
                

                if (!bCached)
                
                    PushField(L, Field);                // Property / closure
                    lua_pushvalue(L, 2);                // key
                    lua_pushvalue(L, -2);               // Property / closure
                    lua_rawset(L, -4);
                
                if (bInherited)
                
                    lua_remove(L, -2);
                    lua_pushvalue(L, 2);                // key
                    lua_pushvalue(L, -2);               // Property / closure
                    lua_rawset(L, -4);
                
            
            else
            
                if (ClassDesc->IsClass())
                
                    luaL_getmetatable(L, "UObject");
                    lua_pushvalue(L, 2);                // push key
                    lua_rawget(L, -2);
                    lua_remove(L, -2);
                
                else
                
                    lua_pushnil(L);
                
            
        
    
    lua_remove(L, -2);
    return 1;

8 GetFunctionList

拿到lua中InModuleName模块内的所有方法名,放到FunctionNames里

9 PushObjectCore

lua栈中创建了一个userdata,然后将它的值设为一个指向UObject指针的指针,它的元表设为RegisterClass时创建的、类型对应的元表。

static void PushObjectCore(lua_State *L, UObjectBaseUtility *Object)

    if (!Object)
    
        lua_pushnil(L);
        return;
    

    void **Dest = (void**)lua_newuserdata(L, sizeof(void*));        // create a userdata
    *Dest = Object;                                                 // store the UObject address
   MarkUserdataTwoLvlPtr(L, -1);                                   // set two level pointer flag

    UClass *Class = Object->GetClass();
    if (Class->IsChildOf(UScriptStruct::StaticClass()))
    
        // the UObject is 'UScriptStruct'
      luaL_getmetatable(L, "UObject");
      check(lua_type(L, -1) != LUA_TNIL);

        lua_setmetatable(L, -2);
    
    else
    
        bool bSuccess = false;
        if (Class->IsChildOf(UClass::StaticClass()))
        
            // the UObject is 'UClass'
            bSuccess = TryToSetMetatable(L, "UClass");
        
        else
        
            // the UObject is object instance
            TStringConversion<TStringConvert<TCHAR, ANSICHAR>> ClassName(*FString::Printf(TEXT("%s%s"), Class->GetPrefixCPP(), *Class->GetName()));
            bSuccess = TryToSetMetatable(L, ClassName.Get());
        
        if (!bSuccess)
        
            UNLUA_LOGERROR(L, LogUnLua, Warning, TEXT("%s, Invalid metatable! %s Object 0x%llx"), ANSI_TO_TCHAR(__FUNCTION__), *Class->GetName(), Object);
        
    

10 NewLuaObject

总结: 创建Lua表对应UObject

int32 NewLuaObject(lua_State *L, UObjectBaseUtility *Object, UClass *Class, const char *ModuleName)

    check(Object);
###1
	int OldTop = lua_gettop(L);
###1
###2
    lua_getfield(L, LUA_REGISTRYINDEX, "ObjectMap");
    lua_pushlightuserdata(L, Object);
    lua_newtable(L);                                            // create a Lua table ('INSTANCE')
###2
###3
    PushObjectCore(L, Object);                                  // push UObject ('RAW_UOBJECT')
###3
###4
    lua_pushstring(L, "Object");
    lua_pushvalue(L, -2);
    lua_rawset(L, -4);                                          // INSTANCET.Object = RAW_UOBJECT
###4
###5
	// in some case may occur module or object metatable can 
	// not be found problem
	int32 TypeModule = GetLoadedModule(L, ModuleName);          // push the required module/table ('REQUIRED_MODULE') to the top of the stack
	int32 TypeMetatable = lua_getmetatable(L, -2);              // get the metatable ('METATABLE_UOBJECT') of 'RAW_UOBJECT' 
###5
	if ((TypeModule != LUA_TTABLE)
		||(0 == TypeMetatable))
	
		lua_pop(L, lua_gettop(L) - OldTop);		
		return LUA_REFNIL;
	
###6
#if ENABLE_CALL_OVERRIDDEN_FUNCTION
    lua_pushstring(L, "Overridden");
    lua_pushvalue(L, -2);
    lua_rawset(L, -4);
#endif
###6
###7 
    lua_setmetatable(L, -2);                                    // REQUIRED_MODULE.metatable = METATABLE_UOBJECT
    lua_setmetatable(L, -3);                                    // INSTANCE.metatable = REQUIRED_MODULE
###7 
###8 
    lua_pop(L, 1);
###8
###9
    lua_pushvalue(L, -1);
    int32 ObjectRef = luaL_ref(L, LUA_REGISTRYINDEX);           // keep a reference for 'INSTANCE'
###9
###10
    FUnLuaDelegates::OnObjectBinded.Broadcast(Object);          // 'INSTANCE' is on the top of stack now
###10
###11
    lua_rawset(L, -3);
    lua_pop(L, 1);
    return ObjectRef;
###11

###1 拿到栈顶的index,也是栈的长度,存下来,后面做清除的时候使用

###2 获取ObjectMap表放入栈顶,把UObject的指针Push到栈顶,栈顶创建一个Lua表,为了好理解,表名称假设为LuaInstance

###3 调用到方法PushObjectCore,Push进去了一个Userdata,然后将它的值设为一个指向UObject指针的指针,它的元表设为RegisterClass时创建的、类型对应的元表,这里我们把Userdata叫做RAW_UOBJECT

###4 三个代码实现了设置INSTANCET表的Object为RAW_UOBJECT

###5 模块入栈,RAW_UOBJECT的元表入栈,即UObjet对应的元表

###6 三个代码实现了设置 Lua模块.Overridden = UObjet对应的元表

###7 Lua模块.metatable = Object元表

INSTANCE.metatable =Lua模块

###8 弹出Userdata

###9 INSTANCET表入栈,在Registry表中,创建一个对象,对象是当前栈顶的元素,即LuaInstance,然后返回创建对象在Registry表中的索引值,同时pop栈顶对象。因为Registry表是全局表,因此LuaInstance之后就不会被GC

###10 广播绑定事件

###11 ObjectMap.Object指针 = LuaInstance

搞完的类图

以上是关于UE4 Unlua源码解析4 - LuaCore内重要方法逐行解释的主要内容,如果未能解决你的问题,请参考以下文章

UE4 Unlua源码解析5 - FLuaContext内重要方法逐行解释

UE4 Unlua源码解析5 - FLuaContext内重要方法逐行解释

UE4 Unlua源码解析3 -FReflectionRegistry内重要方法逐行解释

UE4 Unlua源码解析3 -FReflectionRegistry内重要方法逐行解释

UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释

UE4 Unlua源码解析6 - UUnLuaManager内重要方法逐行解释