UE4对象系统_UFunction

`UFunction

UFunction对象描述了一个Object的成员函数,使之能够被脚本系统调用。UFunction可描述如下2种类型函数:

  • Native Function C++函数导出给脚本系统
  • Blueprint Function 蓝图生成的函数

一般地,函数有如下几个部分组成:

  • 函数名称
  • 参数
  • 返回值
  • 局部变量
  • 代码块
function_definition.jpg

UFunction源码如下:

//
// Reflection data for a replicated or Kismet callable function.
//
class COREUOBJECT_API UFunction : public UStruct
{
    DECLARE_CASTED_CLASS_INTRINSIC(UFunction, UStruct, 0, TEXT("/Script/CoreUObject"), CASTCLASS_UFunction)
    DECLARE_WITHIN(UClass)
public:
    // Persistent variables.
    uint32 FunctionFlags;
    uint16 RepOffset;

    // Variables in memory only.
    uint8 NumParms;                // 参数个数
    uint16 ParmsSize;               // 所有参数占用的内存块大小
    uint16 ReturnValueOffset;   // 返回值的在参数内存块中的偏移量
    /** Id of this RPC function call (must be FUNC_Net & (FUNC_NetService|FUNC_NetResponse)) */
    uint16 RPCId;
    /** Id of the corresponding response call (must be FUNC_Net & FUNC_NetService) */
    uint16 RPCResponseId;

    /** pointer to first local struct property in this UFunction that contains defaults */
    UProperty* FirstPropertyToInit;

#if UE_BLUEPRINT_EVENTGRAPH_FASTCALLS
    // The event graph this function calls in to (persistent)
    UFunction* EventGraphFunction;

    // The state offset inside of the event graph (persistent)
    int32 EventGraphCallOffset;
#endif

private:
    Native Func;        // 如果该函数是C++ Native型,那么为Native函数的指针; 否则null

public:
    /**
     * Returns the native func pointer.
     *
     * @return The native function pointer.
     */
    FORCEINLINE Native GetNativeFunc() const
    {
        return Func;
    }

    /**
     * Sets the native func pointer.
     *
     * @param InFunc - The new function pointer.
     */
    FORCEINLINE void SetNativeFunc(Native InFunc)
    {
        Func = InFunc;
    }

    /**
     * Invokes the UFunction on a UObject.
     *
     * @param Obj    - The object to invoke the function on.
     * @param Stack  - The parameter stack for the function call.
     * @param Result - The result of the function.
     */
    void Invoke(UObject* Obj, FFrame& Stack, RESULT_DECL);

    // Constructors.
    explicit UFunction(const FObjectInitializer& ObjectInitializer, UFunction* InSuperFunction, uint32 InFunctionFlags = 0, uint16 InRepOffset = 0, SIZE_T ParamsSize = 0 );
    explicit UFunction(UFunction* InSuperFunction, uint32 InFunctionFlags = 0, uint16 InRepOffset = 0, SIZE_T ParamsSize = 0);

    void InitializeDerivedMembers();

    // UObject interface.
    virtual void Serialize( FArchive& Ar ) override;

    // UField interface.
    virtual void Bind() override;

    // UStruct interface.
    virtual UStruct* GetInheritanceSuper() const override { return NULL;}
    virtual void Link(FArchive& Ar, bool bRelinkExistingProperties) override;

    // UFunction interface.
    UFunction* GetSuperFunction() const;

    UProperty* GetReturnProperty() const;

    /**
     * Used to safely check whether the passed in flag is set.
     *
     * @param   FlagToCheck     Class flag to check for
     *
     * @return  true if the passed in flag is set, false otherwise
     *          (including no flag passed in, unless the FlagsToCheck is CLASS_AllFlags)
     */
    FORCEINLINE bool HasAnyFunctionFlags( uint32 FlagsToCheck ) const
    {
        return (FunctionFlags&FlagsToCheck) != 0 || FlagsToCheck == FUNC_AllFlags;
    }

    /**
     * Used to safely check whether all of the passed in flags are set.
     *
     * @param FlagsToCheck  Function flags to check for
     * @return true if all of the passed in flags are set (including no flags passed in), false otherwise
     */
    FORCEINLINE bool HasAllFunctionFlags( uint32 FlagsToCheck ) const
    {
        return ((FunctionFlags & FlagsToCheck) == FlagsToCheck);
    }

    /**
     * Returns the flags that are ignored by default when comparing function signatures.
     */
    FORCEINLINE static uint64 GetDefaultIgnoredSignatureCompatibilityFlags()
    {
        //@TODO: UCREMOVAL: CPF_ConstParm added as a hack to get blueprints compiling with a const DamageType parameter.
        const uint64 IgnoreFlags = CPF_PersistentInstance | CPF_ExportObject | CPF_InstancedReference 
            | CPF_ContainsInstancedReference | CPF_ComputedFlags | CPF_ConstParm | CPF_UObjectWrapper
            | CPF_NativeAccessSpecifiers | CPF_AdvancedDisplay;
        return IgnoreFlags;
    }

    /**
     * Determines if two functions have an identical signature (note: currently doesn't allow
     * matches with class parameters that differ only in how derived they are; there is no
     * directionality to the call)
     *
     * @param   OtherFunction   Function to compare this function against.
     *
     * @return  true if function signatures are compatible.
     */
    bool IsSignatureCompatibleWith(const UFunction* OtherFunction) const;

    /**
     * Determines if two functions have an identical signature (note: currently doesn't allow
     * matches with class parameters that differ only in how derived they are; there is no
     * directionality to the call)
     *
     * @param   OtherFunction   Function to compare this function against.
     * @param   IgnoreFlags     Custom flags to ignore when comparing parameters between the functions.
     *
     * @return  true if function signatures are compatible.
     */
    bool IsSignatureCompatibleWith(const UFunction* OtherFunction, uint64 IgnoreFlags) const;
};

实例分析,依然在FPS Example项目中, 给AFirstPersonCharacter添加一个函数

    UFUNCTION(BlueprintCallable)
    bool CheckNameValid(const FString &InStr);

在FirstPerson.generated.cpp中会生成如下代码

Function_Example.jpg

UFunction的调用

一般地在设计脚本系统时,需要考虑函数的调用情形有:

  1. Native函数调用Native函数
  2. Native函数调用Script函数
  3. Script函数调用Native函数
  4. Script函数调用Script函数
    在UE4中,抽象出UFunction用于表示Native和BP函数。脚本系统的字节码指令对应着每个C++小函数(UObject中的execXXXX函数)。所以这里只需要考虑C++代码执行UFunction对象的情形。
    // UnrealScript intrinsics.

    // Undefined native handler
    DECLARE_FUNCTION(execUndefined);

    
    // @todo document below declared functions
    // Variables
    DECLARE_FUNCTION(execLocalVariable);
    DECLARE_FUNCTION(execInstanceVariable);
    DECLARE_FUNCTION(execDefaultVariable);
    DECLARE_FUNCTION(execLocalOutVariable);
    DECLARE_FUNCTION(execInterfaceVariable);
    DECLARE_FUNCTION(execInterfaceContext);
    DECLARE_FUNCTION(execArrayElement);
    DECLARE_FUNCTION(execBoolVariable);
    DECLARE_FUNCTION(execClassDefaultVariable);
    DECLARE_FUNCTION(execEndFunctionParms);


    // Do Nothing 
    DECLARE_FUNCTION(execNothing);
    DECLARE_FUNCTION(execNothingOp4a);

    /** Breakpoint; only observed in the editor; executing it at any other time is a NOP */
    DECLARE_FUNCTION(execBreakpoint);

    /** Tracepoint; only observed in the editor; executing it at any other time is a NOP */
    DECLARE_FUNCTION(execTracepoint);
    DECLARE_FUNCTION(execWireTracepoint);

    /** Instrumentation event for profiling; only observed in the builds with blueprint instrumentation */
    DECLARE_FUNCTION(execInstrumentation);

    DECLARE_FUNCTION(execEndOfScript);

    /** failsafe for functions that return a value - returns the zero value for a property and logs that control reached the end of a non-void function */
    DECLARE_FUNCTION(execReturnNothing);
    DECLARE_FUNCTION(execEmptyParmValue);

    // Commands
    DECLARE_FUNCTION(execJump);
    DECLARE_FUNCTION(execJumpIfNot);
    DECLARE_FUNCTION(execAssert);

    /** 
     * Push a code offset onto the execution flow stack for future execution.  
     * Current execution continues to the next instruction after the push one.
     */
    DECLARE_FUNCTION(execPushExecutionFlow);

    /**
     * Pops a code offset from the execution flow stack and starts execution there.
     * If there are no stack entries left, it is treated as an execution error.
     */
    DECLARE_FUNCTION(execPopExecutionFlow);
    DECLARE_FUNCTION(execComputedJump);

    /** 
     * Pops a code offset from the execution flow stack and starts execution there, if a condition is not true.
     * If there are no stack entries left, it is treated as an execution error.
     */
    DECLARE_FUNCTION(execPopExecutionFlowIfNot);


    // Assignment
    DECLARE_FUNCTION(execLet);
    DECLARE_FUNCTION(execLetObj);
    DECLARE_FUNCTION(execLetWeakObjPtr);
    DECLARE_FUNCTION(execLetBool);
    DECLARE_FUNCTION(execLetDelegate);
    DECLARE_FUNCTION(execLetMulticastDelegate);

    // Delegates 
    DECLARE_FUNCTION(execAddMulticastDelegate);
    DECLARE_FUNCTION(execClearMulticastDelegate);
    DECLARE_FUNCTION(execEatReturnValue);
    DECLARE_FUNCTION(execRemoveMulticastDelegate);

    // Context expressions
    DECLARE_FUNCTION(execSelf);
    DECLARE_FUNCTION(execContext);
    DECLARE_FUNCTION(execContext_FailSilent);
    DECLARE_FUNCTION(execStructMemberContext);

    // Function calls
    DECLARE_FUNCTION(execVirtualFunction);
    DECLARE_FUNCTION(execFinalFunction);

    // Struct comparison
    DECLARE_FUNCTION(execStructCmpEq);
    DECLARE_FUNCTION(execStructCmpNe);
    DECLARE_FUNCTION(execStructMember);

    // @todo delegate: Delegate comparison is not supported for multi-cast delegates
    DECLARE_FUNCTION(execEqualEqual_DelegateDelegate);
    DECLARE_FUNCTION(execNotEqual_DelegateDelegate);
    DECLARE_FUNCTION(execEqualEqual_DelegateFunction);
    DECLARE_FUNCTION(execNotEqual_DelegateFunction);

    // Constants
    DECLARE_FUNCTION(execIntConst);
    DECLARE_FUNCTION(execInt64Const);
    DECLARE_FUNCTION(execUInt64Const);
    DECLARE_FUNCTION(execSkipOffsetConst);
    DECLARE_FUNCTION(execFloatConst);
    DECLARE_FUNCTION(execStringConst);
    DECLARE_FUNCTION(execUnicodeStringConst);
    DECLARE_FUNCTION(execTextConst);
    DECLARE_FUNCTION(execObjectConst);
    DECLARE_FUNCTION(execAssetConst);

    // @todo delegate: Multi-cast versions needed for script execution!     (Need Add, Remove, Clear/Empty)
    DECLARE_FUNCTION(execInstanceDelegate);
    DECLARE_FUNCTION(execNameConst);
    DECLARE_FUNCTION(execByteConst);
    DECLARE_FUNCTION(execIntZero);
    DECLARE_FUNCTION(execIntOne);
    DECLARE_FUNCTION(execTrue);
    DECLARE_FUNCTION(execFalse);
    DECLARE_FUNCTION(execNoObject);
    DECLARE_FUNCTION(execNullInterface);
    DECLARE_FUNCTION(execIntConstByte);
    DECLARE_FUNCTION(execRotationConst);
    DECLARE_FUNCTION(execVectorConst);
    DECLARE_FUNCTION(execTransformConst);
    DECLARE_FUNCTION(execStructConst);
    DECLARE_FUNCTION(execSetArray);
    DECLARE_FUNCTION(execSetSet);
    DECLARE_FUNCTION(execSetMap);
    DECLARE_FUNCTION(execArrayConst);

    // Object construction
    DECLARE_FUNCTION(execNew);
    DECLARE_FUNCTION(execClassContext);
    DECLARE_FUNCTION(execNativeParm);

    // Conversions 
    DECLARE_FUNCTION(execDynamicCast);
    DECLARE_FUNCTION(execMetaCast);
    DECLARE_FUNCTION(execPrimitiveCast);
    DECLARE_FUNCTION(execInterfaceCast);

    // Cast functions
    DECLARE_FUNCTION(execObjectToBool);
    DECLARE_FUNCTION(execInterfaceToBool);
    DECLARE_FUNCTION(execObjectToInterface);
    DECLARE_FUNCTION(execInterfaceToInterface);
    DECLARE_FUNCTION(execInterfaceToObject);

    // Dynamic array functions
    // Array support
    DECLARE_FUNCTION(execGetDynArrayElement);
    DECLARE_FUNCTION(execSetDynArrayElement);
    DECLARE_FUNCTION(execGetDynArrayLength);
    DECLARE_FUNCTION(execSetDynArrayLength);
    DECLARE_FUNCTION(execDynArrayInsert);
    DECLARE_FUNCTION(execDynArrayRemove);
    DECLARE_FUNCTION(execDynArrayFind);
    DECLARE_FUNCTION(execDynArrayFindStruct);
    DECLARE_FUNCTION(execDynArrayAdd);
    DECLARE_FUNCTION(execDynArrayAddItem);
    DECLARE_FUNCTION(execDynArrayInsertItem);
    DECLARE_FUNCTION(execDynArrayRemoveItem);
    DECLARE_FUNCTION(execDynArraySort);

    DECLARE_FUNCTION(execBindDelegate);
    DECLARE_FUNCTION(execCallMulticastDelegate);

    DECLARE_FUNCTION(execLetValueOnPersistentFrame);

    DECLARE_FUNCTION(execCallMathFunction);

    DECLARE_FUNCTION(execSwitchValue);

    DECLARE_FUNCTION(execArrayGetByRef);

实例分析, 在FPS Example中, AFirstPersonProjectile::OnHit调用时堆栈如下:

Paste_Image.png

FirstPersonProjectile.generated.h里做了些调用包装,

#define FirstPerson_Source_FirstPerson_FirstPersonProjectile_h_9_RPC_WRAPPERS \
 \
    DECLARE_FUNCTION(execOnHit) \
    { \
        P_GET_OBJECT(UPrimitiveComponent,Z_Param_HitComp); \
        P_GET_OBJECT(AActor,Z_Param_OtherActor); \
        P_GET_OBJECT(UPrimitiveComponent,Z_Param_OtherComp); \
        P_GET_STRUCT(FVector,Z_Param_NormalImpulse); \
        P_GET_STRUCT_REF(FHitResult,Z_Param_Out_Hit); \
        P_FINISH; \
        P_NATIVE_BEGIN; \
        this->OnHit(Z_Param_HitComp,Z_Param_OtherActor,Z_Param_OtherComp,Z_Param_NormalImpulse,Z_Param_Out_Hit); \
        P_NATIVE_END; \
    }


#define FirstPerson_Source_FirstPerson_FirstPersonProjectile_h_9_RPC_WRAPPERS_NO_PURE_DECLS \
 \
    DECLARE_FUNCTION(execOnHit) \
    { \
        P_GET_OBJECT(UPrimitiveComponent,Z_Param_HitComp); \
        P_GET_OBJECT(AActor,Z_Param_OtherActor); \
        P_GET_OBJECT(UPrimitiveComponent,Z_Param_OtherComp); \
        P_GET_STRUCT(FVector,Z_Param_NormalImpulse); \
        P_GET_STRUCT_REF(FHitResult,Z_Param_Out_Hit); \
        P_FINISH; \
        P_NATIVE_BEGIN; \
        this->OnHit(Z_Param_HitComp,Z_Param_OtherActor,Z_Param_OtherComp,Z_Param_NormalImpulse,Z_Param_Out_Hit); \
        P_NATIVE_END; \
    }

FirstPerson.generated.cpp中加入了execXXX的注册

    void AFirstPersonProjectile::StaticRegisterNativesAFirstPersonProjectile()
    {
        FNativeFunctionRegistrar::RegisterFunction(AFirstPersonProjectile::StaticClass(), "OnHit",(Native)&AFirstPersonProjectile::execOnHit);
    }

下面分析一下UObject::ProcessEvent(UFunction* Function, void* Parms), 略有删减:

void UObject::ProcessEvent( UFunction* Function, void* Parms )
{
    // Reject.
    if (IsPendingKill())
    {
        return;
    }

    if ((Function->FunctionFlags & FUNC_Native) != 0)
    { // Native函数
        int32 FunctionCallspace = GetFunctionCallspace(Function, Parms, NULL);
        if (FunctionCallspace & FunctionCallspace::Remote)
        {
            CallRemoteFunction(Function, Parms, NULL, NULL);
        }

        if ((FunctionCallspace & FunctionCallspace::Local) == 0)
        {
            return;
        }
    }
    else if (Function->Script.Num() == 0)
    {
         // 脚本函数, 却无字节码(空体)
        return;
    }
    checkSlow((Function->ParmsSize == 0) || (Parms != NULL));

    // Scope required for scoped script stats.
    {
        uint8* Frame = NULL;
#if USE_UBER_GRAPH_PERSISTENT_FRAME
        Frame = GetClass()->GetPersistentUberGraphFrame(this, Function);
#endif
        const bool bUsePersistentFrame = (NULL != Frame);
        if (!bUsePersistentFrame)
        {
                        // 分配函数执行需要的内存(参数 + 返回值 + 局部变量)
            Frame = (uint8*)FMemory_Alloca(Function->PropertiesSize);
            // zero the local property memory
            FMemory::Memzero(Frame + Function->ParmsSize, Function->PropertiesSize - Function->ParmsSize);
        }

        // initialize the parameter properties 拷贝参数, 参数可以是基本类型,或者引用类型;如果参数是结构体的话, 结构体肯定是没有显式的构造函数的
        FMemory::Memcpy(Frame, Parms, Function->ParmsSize);

        // Create a new local execution stack.
                // 创建执行栈帧
        FFrame NewStack(this, Function, Frame, NULL, Function->Children);
        checkSlow(NewStack.Locals || Function->ParmsSize == 0);

        // if the function has out parameters, fill the stack frame's out parameter info with the info for those params 
        if ( Function->HasAnyFunctionFlags(FUNC_HasOutParms) )
        {
            FOutParmRec** LastOut = &NewStack.OutParms;
            for ( UProperty* Property = (UProperty*)Function->Children; Property && (Property->PropertyFlags&(CPF_Parm)) == CPF_Parm; Property = (UProperty*)Property->Next )
            {
                // this is used for optional parameters - the destination address for out parameter values is the address of the calling function
                // so we'll need to know which address to use if we need to evaluate the default parm value expression located in the new function's
                // bytecode
                if ( Property->HasAnyPropertyFlags(CPF_OutParm) )
                {
                    CA_SUPPRESS(6263)
                    FOutParmRec* Out = (FOutParmRec*)FMemory_Alloca(sizeof(FOutParmRec));
                    // set the address and property in the out param info
                    // note that since C++ doesn't support "optional out" we can ignore that here
                    Out->PropAddr = Property->ContainerPtrToValuePtr<uint8>(Parms);
                    Out->Property = Property;

                    // add the new out param info to the stack frame's linked list
                    if (*LastOut)
                    {
                        (*LastOut)->NextOutParm = Out;
                        LastOut = &(*LastOut)->NextOutParm;
                    }
                    else
                    {
                        *LastOut = Out;
                    }
                }
            }
        }

        if (!bUsePersistentFrame)
        {
            for (UProperty* LocalProp = Function->FirstPropertyToInit; LocalProp != NULL; LocalProp = (UProperty*)LocalProp->Next)
            {
                LocalProp->InitializeValue_InContainer(NewStack.Locals);
            }
        }

        // Call native function or UObject::ProcessInternal.
        const bool bHasReturnParam = Function->ReturnValueOffset != MAX_uint16;
        uint8* ReturnValueAddress = bHasReturnParam ? ((uint8*)Parms + Function->ReturnValueOffset) : nullptr;
        Function->Invoke(this, NewStack, ReturnValueAddress);

        if (!bUsePersistentFrame)
        {
                        // 销毁临时变量
            // Destroy local variables except function parameters.!! see also UObject::CallFunctionByNameWithArguments
            // also copy back constructed value parms here so the correct copy is destroyed when the event function returns
            for (UProperty* P = Function->DestructorLink; P; P = P->DestructorLinkNext)
            {
                if (!P->IsInContainer(Function->ParmsSize))
                {
                    P->DestroyValue_InContainer(NewStack.Locals);
                }
                else if (!(P->PropertyFlags & CPF_OutParm))
                {
                    FMemory::Memcpy(P->ContainerPtrToValuePtr<uint8>(Parms), P->ContainerPtrToValuePtr<uint8>(NewStack.Locals), P->ArrayDim * P->ElementSize);
                }
            }
        }
    }
}

FFrame

函数运行的时候都有一个上下文, 这个称为栈帧(frame); 这些栈帧串起来就组成了调用栈(Stack)。
源码路径Engine\Source\Runtime\CoreUObject\Public\UObject\Stack.h

//
// Information about script execution at one stack level.
//
//  UFunction的执行上下文
struct FFrame : public FOutputDevice
{   
public:
    // Variables.
    UFunction* Node;      // 对应的UFunction对象
    UObject* Object;        // this Object
    uint8* Code;               // 当前字节码指针(类似cpu eip)
    uint8* Locals;             // 局部变量内存块

    UProperty* MostRecentProperty;
    uint8* MostRecentPropertyAddress;

    /** The execution flow stack for compiled Kismet code */
    FlowStackType FlowStack;

    /** Previous frame on the stack */
    FFrame* PreviousFrame;              // 前一个帧

    /** contains information on any out parameters */
    FOutParmRec* OutParms;

    /** If a class is compiled in then this is set to the property chain for compiled-in functions. In that case, we follow the links to setup the args instead of executing by code. */
    UField* PropertyChainForCompiledIn;

    /** Currently executed native function */
    UFunction* CurrentNativeFunction;

    bool bArrayContextFailed;
public:

    // Constructors.
    FFrame( UObject* InObject, UFunction* InNode, void* InLocals, FFrame* InPreviousFrame = NULL, UField* InPropertyChainForCompiledIn = NULL );

    virtual ~FFrame()
    {
#if DO_BLUEPRINT_GUARD
        FBlueprintExceptionTracker& BlueprintExceptionTracker = FBlueprintExceptionTracker::Get();
        if (BlueprintExceptionTracker.ScriptStack.Num())
        {
            BlueprintExceptionTracker.ScriptStack.Pop(false);
        }
#endif
    }

    // Functions.
    COREUOBJECT_API void Step( UObject* Context, RESULT_DECL );

    /** Replacement for Step that uses an explicitly specified property to unpack arguments **/
    COREUOBJECT_API void StepExplicitProperty(void*const Result, UProperty* Property);

    /** 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 StepCompiledIn(void*const Result);

    /** 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, typename TNativeType>
    FORCEINLINE_DEBUGGABLE TNativeType& StepCompiledInRef(void*const TemporaryBuffer);

    COREUOBJECT_API virtual void Serialize( const TCHAR* V, ELogVerbosity::Type Verbosity, const class FName& Category ) override;
    
    COREUOBJECT_API static void KismetExecutionMessage(const TCHAR* Message, ELogVerbosity::Type Verbosity, FName WarningId = FName());

    /** Returns the current script op code */
    const uint8 PeekCode() const { return *Code; }

    /** Skips over the number of op codes specified by NumOps */
    void SkipCode(const int32 NumOps) { Code += NumOps; }

    template<typename TNumericType>
    TNumericType ReadInt();
    float ReadFloat();
    FName ReadName();
    UObject* ReadObject();
    int32 ReadWord();
    UProperty* ReadProperty();

    /** May return null */
    UProperty* ReadPropertyUnchecked();

    /**
     * Reads a value from the bytestream, which represents the number of bytes to advance
     * the code pointer for certain expressions.
     *
     * @param   ExpressionField     receives a pointer to the field representing the expression; used by various execs
     *                              to drive VM logic
     */
    CodeSkipSizeType ReadCodeSkipCount();

    /**
     * Reads a value from the bytestream which represents the number of bytes that should be zero'd out if a NULL context
     * is encountered
     *
     * @param   ExpressionField     receives a pointer to the field representing the expression; used by various execs
     *                              to drive VM logic
     */
    VariableSizeType ReadVariableSize(UProperty** ExpressionField);

    /**
     * This will return the StackTrace of the current callstack from the last native entry point
     **/
    COREUOBJECT_API FString GetStackTrace() const;

    /**
    * This will return the StackTrace of the all script frames currently active
    **/
    COREUOBJECT_API static FString GetScriptCallstack();
};
Paste_Image.png
Paste_Image.png
Paste_Image.png

总结

经过上述的粗略分析,明白了对象系统中函数的描述方法和调用思路。但是还有些问题尚未解决:

  1. 函数中字节码的组织和布局,指令有哪些,操作数如何和指令混在一起
  2. 在脚本中调用函数时, 参数是放在何处的

这些问题待到研究VM指令这块再做分析。

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

推荐阅读更多精彩内容

  • 本来想研究一下vm的字节码并写个反汇编工具,结果发现UE4已经有了:-) 。反汇编的工具相关源码有:Engine\...
    蛋求疼阅读 3,866评论 0 2
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,279评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,581评论 1 19
  • 过年时聚会,大家都带孩子去了,朋友说你家妞妞改变真大。我说那是,有一个爱学习的妈妈啊。我还记得孩子以前什么...
    静妮儿了阅读 314评论 0 0