UE4对象系统_UFunction_字节码执行

本来想研究一下vm的字节码并写个反汇编工具,结果发现UE4已经有了:-) 。反汇编的工具相关源码有:
Engine\Source\Editor\UnrealEd\Public\ScriptDisassembler.h
Engine\Source\Editor\UnrealEd\Private\ScriptDisassembler.cpp
编辑中的使用方法:

Paste_Image.png

然后会在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.cppEngine\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<uint8>(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<uint8>(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<uint8>(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<UFunction*> 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:

Paste_Image.png
  • EventHit 函数


    Paste_Image.png
  • CalculateTotalDamage函数

Paste_Image.png

执行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的不一样,它是线性式的,表达式语句肯定是安排在前面执行的。


现在看一下函数的参数传递情况(解决上一节的遗留疑问)。

  1. 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
Paste_Image.png

在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<PropertyType>(&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<class TProperty>
FORCEINLINE_DEBUGGABLE void FFrame::StepCompiledIn(void*const Result)
{
    if (Code)
    {
        Step(Object, Result);
    }
    else
    {
        checkSlow(dynamic_cast<TProperty*>(PropertyChainForCompiledIn) && dynamic_cast<UProperty*>(PropertyChainForCompiledIn));
        TProperty* Property = (TProperty*)PropertyChainForCompiledIn;
        PropertyChainForCompiledIn = Property->Next;
        StepExplicitProperty(Result, Property);
    }
}

在FFrame::StepCompiledIn中Step()会执行EX_LocalVariable语句,通过Result返回结果。

  1. 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
Paste_Image.png
Paste_Image.png
Paste_Image.png
Paste_Image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,561评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,263评论 25 707
  • 前言 2000年,伊利诺伊大学厄巴纳-香槟分校(University of Illinois at Urbana-...
    星光社的戴铭阅读 15,846评论 8 180
  • 转自http://blog.csdn.net/navyhu/article/details/47023317理解链...
    扎Zn了老Fe阅读 1,421评论 0 0
  • 生活中,有很多重要的东西在有意无意之间弄丢了,走过的路,去过的地方,唱过的歌,喝过的酒,爱过的人……每当想起这些的...
    禅因阅读 295评论 0 1