本来想研究一下vm的字节码并写个反汇编工具,结果发现UE4已经有了:-) 。反汇编的工具相关源码有:
Engine\Source\Editor\UnrealEd\Public\ScriptDisassembler.h
Engine\Source\Editor\UnrealEd\Private\ScriptDisassembler.cpp
编辑中的使用方法:
然后会在Log窗口中打印出反汇编结果。
指令集
//
// Evaluatable expression item types.
//
enum EExprToken
{
// Variable references.
EX_LocalVariable = 0x00, // A local variable.
EX_InstanceVariable = 0x01, // An object variable.
EX_DefaultVariable = 0x02, // Default variable for a class context.
// = 0x03,
EX_Return = 0x04, // Return from function.
// = 0x05,
EX_Jump = 0x06, // Goto a local address in code.
EX_JumpIfNot = 0x07, // Goto if not expression.
// = 0x08,
EX_Assert = 0x09, // Assertion.
// = 0x0A,
EX_Nothing = 0x0B, // No operation.
// = 0x0C,
// = 0x0D,
// = 0x0E,
EX_Let = 0x0F, // Assign an arbitrary size value to a variable.
// = 0x10,
// = 0x11,
EX_ClassContext = 0x12, // Class default object context.
EX_MetaCast = 0x13, // Metaclass cast.
EX_LetBool = 0x14, // Let boolean variable.
EX_EndParmValue = 0x15, // end of default value for optional function parameter
EX_EndFunctionParms = 0x16, // End of function call parameters.
EX_Self = 0x17, // Self object.
EX_Skip = 0x18, // Skippable expression.
EX_Context = 0x19, // Call a function through an object context.
EX_Context_FailSilent = 0x1A, // Call a function through an object context (can fail silently if the context is NULL; only generated for functions that don't have output or return values).
EX_VirtualFunction = 0x1B, // A function call with parameters.
EX_FinalFunction = 0x1C, // A prebound function call with parameters.
EX_IntConst = 0x1D, // Int constant.
EX_FloatConst = 0x1E, // Floating point constant.
EX_StringConst = 0x1F, // String constant.
EX_ObjectConst = 0x20, // An object constant.
EX_NameConst = 0x21, // A name constant.
EX_RotationConst = 0x22, // A rotation constant.
EX_VectorConst = 0x23, // A vector constant.
EX_ByteConst = 0x24, // A byte constant.
EX_IntZero = 0x25, // Zero.
EX_IntOne = 0x26, // One.
EX_True = 0x27, // Bool True.
EX_False = 0x28, // Bool False.
EX_TextConst = 0x29, // FText constant
EX_NoObject = 0x2A, // NoObject.
EX_TransformConst = 0x2B, // A transform constant
EX_IntConstByte = 0x2C, // Int constant that requires 1 byte.
EX_NoInterface = 0x2D, // A null interface (similar to EX_NoObject, but for interfaces)
EX_DynamicCast = 0x2E, // Safe dynamic class casting.
EX_StructConst = 0x2F, // An arbitrary UStruct constant
EX_EndStructConst = 0x30, // End of UStruct constant
EX_SetArray = 0x31, // Set the value of arbitrary array
EX_EndArray = 0x32,
// = 0x33,
EX_UnicodeStringConst = 0x34, // Unicode string constant.
EX_Int64Const = 0x35, // 64-bit integer constant.
EX_UInt64Const = 0x36, // 64-bit unsigned integer constant.
// = 0x37,
EX_PrimitiveCast = 0x38, // A casting operator for primitives which reads the type as the subsequent byte
EX_SetSet = 0x39,
EX_EndSet = 0x3A,
EX_SetMap = 0x3B,
EX_EndMap = 0x3C,
// = 0x3D,
// = 0x3E,
// = 0x3F,
// = 0x40,
// = 0x41,
EX_StructMemberContext = 0x42, // Context expression to address a property within a struct
EX_LetMulticastDelegate = 0x43, // Assignment to a multi-cast delegate
EX_LetDelegate = 0x44, // Assignment to a delegate
// = 0x45,
// = 0x46, // CST_ObjectToInterface
// = 0x47, // CST_ObjectToBool
EX_LocalOutVariable = 0x48, // local out (pass by reference) function parameter
// = 0x49, // CST_InterfaceToBool
EX_DeprecatedOp4A = 0x4A,
EX_InstanceDelegate = 0x4B, // const reference to a delegate or normal function object
EX_PushExecutionFlow = 0x4C, // push an address on to the execution flow stack for future execution when a EX_PopExecutionFlow is executed. Execution continues on normally and doesn't change to the pushed address.
EX_PopExecutionFlow = 0x4D, // continue execution at the last address previously pushed onto the execution flow stack.
EX_ComputedJump = 0x4E, // Goto a local address in code, specified by an integer value.
EX_PopExecutionFlowIfNot = 0x4F, // continue execution at the last address previously pushed onto the execution flow stack, if the condition is not true.
EX_Breakpoint = 0x50, // Breakpoint. Only observed in the editor, otherwise it behaves like EX_Nothing.
EX_InterfaceContext = 0x51, // Call a function through a native interface variable
EX_ObjToInterfaceCast = 0x52, // Converting an object reference to native interface variable
EX_EndOfScript = 0x53, // Last byte in script code
EX_CrossInterfaceCast = 0x54, // Converting an interface variable reference to native interface variable
EX_InterfaceToObjCast = 0x55, // Converting an interface variable reference to an object
// = 0x56,
// = 0x57,
// = 0x58,
// = 0x59,
EX_WireTracepoint = 0x5A, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.
EX_SkipOffsetConst = 0x5B, // A CodeSizeSkipOffset constant
EX_AddMulticastDelegate = 0x5C, // Adds a delegate to a multicast delegate's targets
EX_ClearMulticastDelegate = 0x5D, // Clears all delegates in a multicast target
EX_Tracepoint = 0x5E, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.
EX_LetObj = 0x5F, // assign to any object ref pointer
EX_LetWeakObjPtr = 0x60, // assign to a weak object pointer
EX_BindDelegate = 0x61, // bind object and name to delegate
EX_RemoveMulticastDelegate = 0x62, // Remove a delegate from a multicast delegate's targets
EX_CallMulticastDelegate = 0x63, // Call multicast delegate
EX_LetValueOnPersistentFrame = 0x64,
EX_ArrayConst = 0x65,
EX_EndArrayConst = 0x66,
EX_AssetConst = 0x67,
EX_CallMath = 0x68, // static pure function from on local call space
EX_SwitchValue = 0x69,
EX_InstrumentationEvent = 0x6A, // Instrumentation event
EX_ArrayGetByRef = 0x6B,
EX_Max = 0x100,
};
enum ECastToken
{
CST_ObjectToInterface = 0x46,
CST_ObjectToBool = 0x47,
CST_InterfaceToBool = 0x49,
CST_Max = 0xFF,
};
结合Engine\Source\Editor\UnrealEd\Private\ScriptDisassembler.cpp
和Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp
中指令解析代码,下面分析部分指令的执行。
- EX_LocalVariable
void UObject::execLocalVariable(FFrame& Stack, RESULT_DECL)
{
checkSlow(Stack.Object == this);
checkSlow(Stack.Locals != NULL);
UProperty* VarProperty = Stack.ReadProperty(); // 从指令流中读取局部对象的UProperty
if (VarProperty == nullptr)
{
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::AccessViolation, LOCTEXT("MissingLocalVariable", "Attempted to access missing local variable. If this is a packaged/cooked build, are you attempting to use an editor-only property?"));
FBlueprintCoreDelegates::ThrowScriptException(this, Stack, ExceptionInfo);
Stack.MostRecentPropertyAddress = nullptr;
}
else
{
// 获得函数local variable的内存地址
Stack.MostRecentPropertyAddress = VarProperty->ContainerPtrToValuePtr(Stack.Locals);
if (RESULT_PARAM)
{
VarProperty->CopyCompleteValueToScriptVM(RESULT_PARAM, Stack.MostRecentPropertyAddress);
}
}
}
- EX_InstanceVariable
void UObject::execInstanceVariable(FFrame& Stack, RESULT_DECL)
{
// 读取类实例的成员变量UProperty
UProperty* VarProperty = (UProperty*)Stack.ReadObject();
Stack.MostRecentProperty = VarProperty;
if (VarProperty == nullptr || !IsA((UClass*)VarProperty->GetOuter()))
{
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::AccessViolation, FText::Format(LOCTEXT("MissingProperty", "Attempted to access missing property '{0}'. If this is a packaged/cooked build, are you attempting to use an editor-only property?"), FText::FromString(GetNameSafe(VarProperty))));
FBlueprintCoreDelegates::ThrowScriptException(this, Stack, ExceptionInfo);
Stack.MostRecentPropertyAddress = nullptr;
}
else
{
// 获取对象成员变量的地址
Stack.MostRecentPropertyAddress = VarProperty->ContainerPtrToValuePtr(this);
if (RESULT_PARAM)
{
VarProperty->CopyCompleteValueToScriptVM(RESULT_PARAM, Stack.MostRecentPropertyAddress);
}
}
}
- EX_DefaultVariable
void UObject::execDefaultVariable(FFrame& Stack, RESULT_DECL)
{
UProperty* VarProperty = (UProperty*)Stack.ReadObject();
Stack.MostRecentProperty = VarProperty;
Stack.MostRecentPropertyAddress = nullptr;
UObject* DefaultObject = nullptr;
if (HasAnyFlags(RF_ClassDefaultObject))
{
DefaultObject = this;
}
else
{
// @todo - allow access to archetype properties through object references?
}
if (VarProperty == nullptr || (DefaultObject && !DefaultObject->IsA((UClass*)VarProperty->GetOuter())))
{
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::AccessViolation, LOCTEXT("MissingPropertyDefaultObject", "Attempted to access a missing property on a CDO. If this is a packaged/cooked build, are you attempting to use an editor-only property?"));
FBlueprintCoreDelegates::ThrowScriptException(this, Stack, ExceptionInfo);
}
else
{
if(DefaultObject != nullptr)
{
Stack.MostRecentPropertyAddress = VarProperty->ContainerPtrToValuePtr(DefaultObject);
if(RESULT_PARAM)
{
VarProperty->CopyCompleteValueToScriptVM(RESULT_PARAM, Stack.MostRecentPropertyAddress);
}
}
else
{
FBlueprintExceptionInfo ExceptionInfo(EBlueprintExceptionType::AccessViolation, LOCTEXT("AccessNoneDefaultObject", "Accessed None attempting to read a default property"));
FBlueprintCoreDelegates::ThrowScriptException(this, Stack, ExceptionInfo);
}
}
}
- EX_LocalOutVariable
void UObject::execLocalOutVariable(FFrame& Stack, RESULT_DECL)
{
checkSlow(Stack.Object == this);
// get the property we need to find
// 函数返回值的UProperty
UProperty* VarProperty = Stack.ReadProperty();
// look through the out parameter infos and find the one that has the address of this property
FOutParmRec* Out = Stack.OutParms;
checkSlow(Out);
while (Out->Property != VarProperty)
{
Out = Out->NextOutParm;
checkSlow(Out);
}
// 获取返回值地址
Stack.MostRecentPropertyAddress = Out->PropAddr;
// if desired, copy the value in that address to Result
if (RESULT_PARAM && RESULT_PARAM != Stack.MostRecentPropertyAddress)
{
VarProperty->CopyCompleteValueToScriptVM(RESULT_PARAM, Stack.MostRecentPropertyAddress);
}
}
- EX_InterfaceContext
void UObject::execInterfaceContext(FFrame& Stack, RESULT_DECL)
{
// get the value of the interface variable
FScriptInterface InterfaceValue;
Stack.Step(this, &InterfaceValue);
if (RESULT_PARAM != NULL)
{
// copy the UObject pointer to Result
*(UObject**)RESULT_PARAM = InterfaceValue.GetObject();
}
}
- EX_Let
普通类型赋值
void UObject::execLet(FFrame& Stack, RESULT_DECL)
{
Stack.MostRecentProperty = nullptr;
UProperty* LocallyKnownProperty = Stack.ReadPropertyUnchecked();
// Get variable address.
Stack.MostRecentProperty = nullptr;
Stack.MostRecentPropertyAddress = nullptr;
Stack.Step(Stack.Object, nullptr); // Evaluate variable. 获取目的地址,存放在Stack.MostRecentPropertyAddress
uint8* LocalTempResult = nullptr;
if (Stack.MostRecentPropertyAddress == nullptr)
{
FBlueprintExceptionInfo ExceptionInfo(
EBlueprintExceptionType::AccessViolation,
LOCTEXT("LetAccessNone", "Attempted to assign to None"));
FBlueprintCoreDelegates::ThrowScriptException(this, Stack, ExceptionInfo);
if (LocallyKnownProperty)
{
LocalTempResult = (uint8*)FMemory_Alloca(LocallyKnownProperty->GetSize());
LocallyKnownProperty->InitializeValue(LocalTempResult);
Stack.MostRecentPropertyAddress = LocalTempResult;
}
else
{
Stack.MostRecentPropertyAddress = (uint8*)FMemory_Alloca(1024);
FMemory::Memzero(Stack.MostRecentPropertyAddress, sizeof(FString));
}
}
// Evaluate expression into variable. 获取Src地址,并拷贝值到Stack.MostRecentPropertyAddress中
Stack.Step(Stack.Object, Stack.MostRecentPropertyAddress);
if (LocalTempResult && LocallyKnownProperty)
{
LocallyKnownProperty->DestroyValue(LocalTempResult);
}
}
- EX_CallMath
调用数学函数
void UObject::execCallMathFunction(FFrame& Stack, RESULT_DECL)
{
UFunction* Function = (UFunction*)Stack.ReadObject();
checkSlow(Function);
checkSlow(Function->FunctionFlags & FUNC_Native);
UObject* NewContext = Function->GetOuterUClass()->GetDefaultObject(false);
checkSlow(NewContext);
{
FScopeCycleCounterUObject ContextScope(Stack.Object);
FScopeCycleCounterUObject FunctionScope(Function);
// CurrentNativeFunction is used so far only by FLuaContext::InvokeScriptFunction
// TGuardValue NativeFuncGuard(Stack.CurrentNativeFunction, Function);
Native Func = Function->GetNativeFunc();
checkSlow(Func);
(NewContext->*Func)(Stack, RESULT_PARAM);
}
}
- EX_VirtualFunction
void UObject::execVirtualFunction( FFrame& Stack, RESULT_DECL )
{
// Call the virtual function.
CallFunction( Stack, RESULT_PARAM, FindFunctionChecked(Stack.ReadName()) );
}
- EX_FinalFunction
void UObject::execFinalFunction( FFrame& Stack, RESULT_DECL )
{
// Call the final function.
CallFunction( Stack, RESULT_PARAM, (UFunction*)Stack.ReadObject() );
}
- EX_Jump
void UObject::execJump( FFrame& Stack, RESULT_DECL )
{
CHECK_RUNAWAY;
// Jump immediate.
CodeSkipSizeType Offset = Stack.ReadCodeSkipCount();
Stack.Code = &Stack.Node->Script[Offset];
}
- EX_ComputedJump
void UObject::execComputedJump( FFrame& Stack, RESULT_DECL )
{
CHECK_RUNAWAY;
// Get the jump offset expression
int32 ComputedOffset = 0;
Stack.Step( Stack.Object, &ComputedOffset );
check((ComputedOffset < Stack.Node->Script.Num()) && (ComputedOffset >= 0));
// Jump to the new offset
Stack.Code = &Stack.Node->Script[ComputedOffset];
}
- EX_JumpIfNot
void UObject::execJumpIfNot( FFrame& Stack, RESULT_DECL )
{
CHECK_RUNAWAY;
// Get code offset.
CodeSkipSizeType Offset = Stack.ReadCodeSkipCount();
// Get boolean test value.
bool Value=0;
Stack.Step( Stack.Object, &Value );
// Jump if false.
if( !Value )
{
Stack.Code = &Stack.Node->Script[ Offset ];
}
}
案例分析
下图为一个蓝图FirstPersonProjectile:
-
EventHit 函数
CalculateTotalDamage函数
执行console命令: DisasmScript FirstPersonProjectile_C
:
说明: Label_xxx为字节码在函数体内的位置号.
- CalculateTotalDamage函数
Processing function CalculateTotalDamage (256 bytes)
Label_0x0:
EX_Tracepoint(5E): .. debug site ..
Label_0x1:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x2: // GetActorLocation到临时变量中
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
Expression:
EX_FinalFunction(1C): Final Function (stack node Actor::K2_GetActorLocation)
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x1E: // 执行InHitLocation - CallFunc_K2_GetActorLocation_ReturnValue 保存结果到临时变量中
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_Subtract_VectorVector_ReturnValue
Expression:
EX_CallMath(68): Call Math (stack node KismetMathLibrary::Subtract_VectorVector)
EX_LocalVariable(0): Local variable named InHitLocation
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x4C: // 执行VectorLength到临时变量中
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_VSize_ReturnValue
Expression: // 调用VSize函数
EX_CallMath(68): Call Math (stack node KismetMathLibrary::VSize)
EX_LocalVariable(0): Local variable named CallFunc_Subtract_VectorVector_ReturnValue
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x71: // 执行 >= 运算,结果放入临时变量
EX_LetBool(14): LetBool (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_GreaterEqual_FloatFloat_ReturnValue
Expression:
EX_CallMath(68): Call Math (stack node KismetMathLibrary::GreaterEqual_FloatFloat)
EX_LocalVariable(0): Local variable named CallFunc_VSize_ReturnValue
EX_FloatConst(1E): literal float 10.000000
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x93:
EX_Tracepoint(5E): .. debug site ..
Label_0x94:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x95: // Branch语句
EX_JumpIfNot(7): Jump to offset 0xDF if not expr:
EX_LocalVariable(0): Local variable named CallFunc_GreaterEqual_FloatFloat_ReturnValue
Label_0xA3:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0xA4:
EX_Tracepoint(5E): .. debug site ..
Label_0xA5: // TotalVal = 100.0
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named TotalVal
Expression:
EX_FloatConst(1E): literal float 100.000000
Label_0xBC:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0xBD:
EX_Tracepoint(5E): .. debug site ..
Label_0xBE: //将TotalVal赋予返回值local out variable DamageVal
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalOutVariable(48): Local out variable named DamageVal
Expression:
EX_LocalVariable(0): Local variable named TotalVal
Label_0xD9:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0xDA:
EX_Jump(6): Jump to offset 0xFD
Label_0xDF:
EX_Tracepoint(5E): .. debug site ..
Label_0xE0: // 执行TotalVal = 200.0
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named TotalVal
Expression:
EX_FloatConst(1E): literal float 200.000000
Label_0xF7:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0xF8:
EX_Jump(6): Jump to offset 0xBD
Label_0xFD: // 函数返回
EX_Return(4): Return expression
EX_Nothing(B): EX_Nothing
Label_0xFF:
EX_EndOfScript(53): EX_EndOfScript
- EventHit函数
Processing function ReceiveHit (166 bytes)
Label_0x0: // 将函数参数copy到临时变量中
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_MyComp, offset: 8
Expression:
EX_LocalVariable(0): Local variable named MyComp
Label_0x12:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_Other, offset: 16
Expression:
EX_LocalVariable(0): Local variable named Other
Label_0x24:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_OtherComp, offset: 24
Expression:
EX_LocalVariable(0): Local variable named OtherComp
Label_0x36:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_bSelfMoved, offset: 32
Expression:
EX_LocalVariable(0): Local variable named bSelfMoved
Label_0x48:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_HitLocation, offset: 36
Expression:
EX_LocalVariable(0): Local variable named HitLocation
Label_0x5A:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_HitNormal, offset: 48
Expression:
EX_LocalVariable(0): Local variable named HitNormal
Label_0x6C:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_NormalImpulse, offset: 60
Expression:
EX_LocalVariable(0): Local variable named NormalImpulse
Label_0x7E:
EX_LetValueOnPersistentFrame(64): LetValueOnPersistentFrame
Destination variable: K2Node_Event_Hit, offset: 72
Expression:
EX_LocalOutVariable(48): Local out variable named Hit
Label_0x90: // 调用下面的函数
EX_VirtualFunction(1B): Virtual Function named ExecuteUbergraph_FirstPersonProjectile
EX_IntConst(1D): literal int32 10
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0xA3:
EX_Return(4): Return expression
EX_Nothing(B): EX_Nothing
Label_0xA5:
EX_EndOfScript(53): EX_EndOfScript
===========================================================================
// 被方框封起来的部分, 单独搞了个函数
Processing function ExecuteUbergraph_FirstPersonProjectile (340 bytes)
Label_0x0:
EX_ComputedJump(4E): Computed Jump, offset specified by expression:
EX_LocalVariable(0): Local variable named EntryPoint
Label_0xA:
EX_Tracepoint(5E): .. debug site ..
Label_0xB:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0xC:
EX_LetBool(14): LetBool (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_IsSimulatingPhysics_ReturnValue
Expression:
$19: EX_Context // 执行对象的成员函数
ObjectExpression: // 获取pObj
EX_LocalVariable(0): Local variable named K2Node_Event_OtherComp
Skip Bytes: 0x1B
R-Value Property: CallFunc_IsSimulatingPhysics_ReturnValue
ContextExpression: // 函数对象
EX_VirtualFunction(1B): Virtual Function named IsSimulatingPhysics
EX_NameConst(21): literal name None 参数
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x47:
EX_Tracepoint(5E): .. debug site ..
Label_0x48:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x49:
EX_JumpIfNot(7): Jump to offset 0x151 if not expr:
EX_LocalVariable(0): Local variable named CallFunc_IsSimulatingPhysics_ReturnValue
Label_0x57:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x58:
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
Expression:
EX_FinalFunction(1C): Final Function (stack node Actor::K2_GetActorLocation)
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x74:
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_GetVelocity_ReturnValue
Expression:
EX_VirtualFunction(1B): Virtual Function named GetVelocity
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x94:
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_Multiply_VectorFloat_ReturnValue
Expression:
EX_CallMath(68): Call Math (stack node KismetMathLibrary::Multiply_VectorFloat)
EX_LocalVariable(0): Local variable named CallFunc_GetVelocity_ReturnValue
EX_FloatConst(1E): literal float 100.000000
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0xBE:
EX_Tracepoint(5E): .. debug site ..
Label_0xBF:
$19: EX_Context
ObjectExpression:
EX_LocalVariable(0): Local variable named K2Node_Event_OtherComp
Skip Bytes: 0x2D
R-Value Property: (null)
ContextExpression:
EX_VirtualFunction(1B): Virtual Function named AddImpulseAtLocation
EX_LocalVariable(0): Local variable named CallFunc_Multiply_VectorFloat_ReturnValue
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
EX_NameConst(21): literal name None
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x102:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x103:
EX_Tracepoint(5E): .. debug site ..
Label_0x104:
EX_VirtualFunction(1B): Virtual Function named K2_DestroyActor
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x112:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x113:
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
Expression:
EX_FinalFunction(1C): Final Function (stack node Actor::K2_GetActorLocation)
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x12F:
EX_Tracepoint(5E): .. debug site ..
Label_0x130:
// 调用CalculateTotalDamage函数
EX_VirtualFunction(1B): Virtual Function named CalculateTotalDamage
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
EX_LocalVariable(0): Local variable named CallFunc_CalculateTotalDamage_DamageVal
EX_EndFunctionParms(16): EX_EndFunctionParms
Label_0x150:
EX_WireTracepoint(5A): .. wire debug site ..
Label_0x151:
EX_Return(4): Return expression
EX_Nothing(B): EX_Nothing
Label_0x153:
EX_EndOfScript(53): EX_EndOfScript
从上述的字节码可以看出,UE的VM指令流的布局与Intel CPU上的指令布局是不一样的,比如EX_Let指令后面可以跟随其它指令,这些指令来实现variable,和expression功能,所以在字节码执行时会发生递归调解释器函数(Step)。而Intel的不一样,它是线性式的,表达式语句肯定是安排在前面执行的。
现在看一下函数的参数传递情况(解决上一节的遗留疑问)。
- Call MathFunction
Label_0x4C: // 执行VectorLength到临时变量中
EX_Let(F): Let (Variable = Expression)
Variable:
EX_LocalVariable(0): Local variable named CallFunc_VSize_ReturnValue
Expression: // 调用VSize函数
EX_CallMath(68): Call Math (stack node KismetMathLibrary::VSize)
EX_LocalVariable(0): Local variable named CallFunc_Subtract_VectorVector_ReturnValue
EX_EndFunctionParms(16): EX_EndFunctionParms
在KismetMathLibrary.generated.h中找到:
DECLARE_FUNCTION(execVSize) \
{ \
P_GET_STRUCT(FVector,Z_Param_A); \
P_FINISH; \
P_NATIVE_BEGIN; \
*(float*)Z_Param__Result=UKismetMathLibrary::VSize(Z_Param_A); \
P_NATIVE_END; \
} \
相关宏为:
#define P_GET_STRUCT(StructType,ParamName) PARAM_PASSED_BY_VAL(ParamName, UStructProperty, StructType)
#define PARAM_PASSED_BY_VAL(ParamName, PropertyType, ParamType) \
ParamType ParamName; \
Stack.StepCompiledIn(&ParamName);
/**
* Replacement for Step that checks the for byte code, and if none exists, then PropertyChainForCompiledIn is used.
* Also makes an effort to verify that the params are in the correct order and the types are compatible.
**/
template
FORCEINLINE_DEBUGGABLE void FFrame::StepCompiledIn(void*const Result)
{
if (Code)
{
Step(Object, Result);
}
else
{
checkSlow(dynamic_cast(PropertyChainForCompiledIn) && dynamic_cast(PropertyChainForCompiledIn));
TProperty* Property = (TProperty*)PropertyChainForCompiledIn;
PropertyChainForCompiledIn = Property->Next;
StepExplicitProperty(Result, Property);
}
}
在FFrame::StepCompiledIn中Step()会执行EX_LocalVariable语句,通过Result返回结果。
- Call CalculateTotalDamage
Label_0x130:
// 调用CalculateTotalDamage函数
EX_VirtualFunction(1B): Virtual Function named CalculateTotalDamage
EX_LocalVariable(0): Local variable named CallFunc_K2_GetActorLocation_ReturnValue
EX_LocalVariable(0): Local variable named CallFunc_CalculateTotalDamage_DamageVal
EX_EndFunctionParms(16): EX_EndFunctionParms