`UFunction
UFunction對(duì)象描述了一個(gè)Object的成員函數(shù),使之能夠被腳本系統(tǒng)調(diào)用。UFunction可描述如下2種類型函數(shù):
- Native Function C++函數(shù)導(dǎo)出給腳本系統(tǒng)
- Blueprint Function 藍(lán)圖生成的函數(shù)
一般地,函數(shù)有如下幾個(gè)部分組成:
- 函數(shù)名稱
- 參數(shù)
- 返回值
- 局部變量
- 代碼塊

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; // 參數(shù)個(gè)數(shù)
uint16 ParmsSize; // 所有參數(shù)占用的內(nèi)存塊大小
uint16 ReturnValueOffset; // 返回值的在參數(shù)內(nèi)存塊中的偏移量
/** 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; // 如果該函數(shù)是C++ Native型,那么為Native函數(shù)的指針; 否則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;
};
實(shí)例分析,依然在FPS Example項(xiàng)目中, 給AFirstPersonCharacter添加一個(gè)函數(shù)
UFUNCTION(BlueprintCallable)
bool CheckNameValid(const FString &InStr);
在FirstPerson.generated.cpp中會(huì)生成如下代碼

UFunction的調(diào)用
一般地在設(shè)計(jì)腳本系統(tǒng)時(shí),需要考慮函數(shù)的調(diào)用情形有:
- Native函數(shù)調(diào)用Native函數(shù)
- Native函數(shù)調(diào)用Script函數(shù)
- Script函數(shù)調(diào)用Native函數(shù)
- Script函數(shù)調(diào)用Script函數(shù)
在UE4中,抽象出UFunction用于表示Native和BP函數(shù)。腳本系統(tǒng)的字節(jié)碼指令對(duì)應(yīng)著每個(gè)C++小函數(shù)(UObject中的execXXXX函數(shù))。所以這里只需要考慮C++代碼執(zhí)行UFunction對(duì)象的情形。
// 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);
實(shí)例分析, 在FPS Example中, AFirstPersonProjectile::OnHit調(diào)用時(shí)堆棧如下:

FirstPersonProjectile.generated.h里做了些調(diào)用包裝,
#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的注冊(cè)
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函數(shù)
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)
{
// 腳本函數(shù), 卻無(wú)字節(jié)碼(空體)
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)
{
// 分配函數(shù)執(zhí)行需要的內(nèi)存(參數(shù) + 返回值 + 局部變量)
Frame = (uint8*)FMemory_Alloca(Function->PropertiesSize);
// zero the local property memory
FMemory::Memzero(Frame + Function->ParmsSize, Function->PropertiesSize - Function->ParmsSize);
}
// initialize the parameter properties 拷貝參數(shù), 參數(shù)可以是基本類型,或者引用類型;如果參數(shù)是結(jié)構(gòu)體的話, 結(jié)構(gòu)體肯定是沒(méi)有顯式的構(gòu)造函數(shù)的
FMemory::Memcpy(Frame, Parms, Function->ParmsSize);
// Create a new local execution stack.
// 創(chuàng)建執(zhí)行棧幀
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)
{
// 銷毀臨時(shí)變量
// 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
函數(shù)運(yùn)行的時(shí)候都有一個(gè)上下文, 這個(gè)稱為棧幀(frame); 這些棧幀串起來(lái)就組成了調(diào)用棧(Stack)。
源碼路徑Engine\Source\Runtime\CoreUObject\Public\UObject\Stack.h
//
// Information about script execution at one stack level.
//
// UFunction的執(zhí)行上下文
struct FFrame : public FOutputDevice
{
public:
// Variables.
UFunction* Node; // 對(duì)應(yīng)的UFunction對(duì)象
UObject* Object; // this Object
uint8* Code; // 當(dāng)前字節(jié)碼指針(類似cpu eip)
uint8* Locals; // 局部變量?jī)?nèi)存塊
UProperty* MostRecentProperty;
uint8* MostRecentPropertyAddress;
/** The execution flow stack for compiled Kismet code */
FlowStackType FlowStack;
/** Previous frame on the stack */
FFrame* PreviousFrame; // 前一個(gè)幀
/** 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();
};



總結(jié)
經(jīng)過(guò)上述的粗略分析,明白了對(duì)象系統(tǒng)中函數(shù)的描述方法和調(diào)用思路。但是還有些問(wèn)題尚未解決:
- 函數(shù)中字節(jié)碼的組織和布局,指令有哪些,操作數(shù)如何和指令混在一起
- 在腳本中調(diào)用函數(shù)時(shí), 參數(shù)是放在何處的
這些問(wèn)題待到研究VM指令這塊再做分析。