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内重要方法逐行解释