2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "BlueprintGraphPrivatePCH.h"
# include "CallFunctionHandler.h"
# define LOCTEXT_NAMESPACE "CallFunctionHandler"
//////////////////////////////////////////////////////////////////////////
// FImportTextErrorContext
// Support class to pipe logs from UProperty->ImportText (for struct literals) to the message log as warnings
class FImportTextErrorContext : public FOutputDevice
{
protected :
FCompilerResultsLog & MessageLog ;
UObject * TargetObject ;
public :
int32 NumErrors ;
FImportTextErrorContext ( FCompilerResultsLog & InMessageLog , UObject * InTargetObject )
: FOutputDevice ( )
, MessageLog ( InMessageLog )
, TargetObject ( InTargetObject )
, NumErrors ( 0 )
{
}
2014-06-13 06:14:46 -04:00
virtual void Serialize ( const TCHAR * V , ELogVerbosity : : Type Verbosity , const class FName & Category ) override
2014-03-14 14:13:41 -04:00
{
if ( TargetObject = = NULL )
{
MessageLog . Error ( V ) ;
}
else
{
const FString ErrorString = FString : : Printf ( TEXT ( " Invalid default on node @@: %s " ) , V ) ;
MessageLog . Error ( * ErrorString , TargetObject ) ;
}
NumErrors + + ;
}
} ;
//////////////////////////////////////////////////////////////////////////
// FKCHandler_CallFunction
# if _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4750)
# endif
/**
* Searches for the function referenced by a graph node in the CallingContext class ' s list of functions ,
* validates that the wiring matches up correctly , and creates an execution statement .
*/
void FKCHandler_CallFunction : : CreateFunctionCallStatement ( FKismetFunctionContext & Context , UEdGraphNode * Node , UEdGraphPin * SelfPin )
{
int32 NumErrorsAtStart = CompilerContext . MessageLog . NumErrors ;
// Find the function, starting at the parent class
UFunction * Function = FindFunction ( Context , Node ) ;
if ( Function ! = NULL )
{
CheckIfFunctionIsCallable ( Function , Context , Node ) ;
// Make sure the pin mapping is sound (all pins wire up to a matching function parameter, and all function parameters match a pin)
// Remaining unmatched pins
TArray < UEdGraphPin * > RemainingPins ;
RemainingPins . Append ( Node - > Pins ) ;
// Remove expected exec and self pins
//@TODO: Check to make sure there is exactly one exec in and one exec out, as well as one self pin
for ( int32 i = 0 ; i < RemainingPins . Num ( ) ; )
{
if ( CompilerContext . GetSchema ( ) - > IsMetaPin ( * RemainingPins [ i ] ) )
{
RemainingPins . RemoveAtSwap ( i ) ;
}
else
{
+ + i ;
}
}
// Check for magic pins
const bool bIsLatent = Function - > HasMetaData ( FBlueprintMetadata : : MD_Latent ) ;
if ( bIsLatent & & ( CompilerContext . UbergraphContext ! = & Context ) )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " ContainsLatentCall_Error " , " @@ contains a latent call, which cannot exist outside of the event graph " ) . ToString ( ) , Node ) ;
}
// Check access specifier
const uint32 AccessSpecifier = Function - > FunctionFlags & FUNC_AccessSpecifiers ;
if ( FUNC_Private = = AccessSpecifier )
{
if ( Function - > GetOuter ( ) ! = Context . NewClass )
{
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " PrivateFunctionCall_Error " , " Function @@ is private and cannot be called outside its class " ) . ToString ( ) , Node ) ;
}
}
else if ( FUNC_Protected = = AccessSpecifier )
{
if ( ! Context . NewClass - > IsChildOf ( Cast < UStruct > ( Function - > GetOuter ( ) ) ) )
{
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " ProtectedFunctionCall_Error " , " Function @@ is protected and can be called only from its class or subclasses " ) . ToString ( ) , Node ) ;
}
}
UEdGraphPin * LatentInfoPin = NULL ;
TMap < FName , FString > * MetaData = UMetaData : : GetMapForObject ( Function ) ;
if ( MetaData ! = NULL )
{
for ( TMap < FName , FString > : : TConstIterator It ( * MetaData ) ; It ; + + It )
{
const FName & Key = It . Key ( ) ;
if ( Key = = TEXT ( " LatentInfo " ) )
{
UEdGraphPin * Pin = Node - > FindPin ( It . Value ( ) ) ;
if ( ( Pin ! = NULL ) & & ( Pin - > Direction = = EGPD_Input ) & & ( Pin - > LinkedTo . Num ( ) = = 0 ) )
{
LatentInfoPin = Pin ;
UEdGraphPin * PinToTry = FEdGraphUtilities : : GetNetFromPin ( Pin ) ;
FBPTerminal * * Term = Context . NetMap . Find ( PinToTry ) ;
if ( Term ! = NULL )
{
check ( ( * Term ) - > bIsLiteral ) ;
const int32 UUID = Context . GetContextUniqueID ( ) ;
const FString ExecutionFunctionName = CompilerContext . GetSchema ( ) - > FN_ExecuteUbergraphBase . ToString ( ) + TEXT ( " _ " ) + Context . Blueprint - > GetName ( ) ;
( * Term ) - > Name = FString : : Printf ( TEXT ( " (Linkage=%s,UUID=%s,ExecutionFunction=%s,CallbackTarget=None) " ) , * FString : : FromInt ( INDEX_NONE ) , * FString : : FromInt ( UUID ) , * ExecutionFunctionName ) ;
// Record the UUID in the debugging information
UEdGraphNode * TrueSourceNode = Cast < UEdGraphNode > ( Context . MessageLog . FindSourceObject ( Node ) ) ;
Context . NewClass - > GetDebugData ( ) . RegisterUUIDAssociation ( TrueSourceNode , UUID ) ;
}
}
else
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * LOCTEXT ( " FindPinFromLinkage_Error " , " Function %s (called from @@) was specified with LatentInfo metadata but does not have a pin named %s " ) . ToString ( ) ,
* ( Function - > GetName ( ) ) , * ( It . Value ( ) ) ) , Node ) ;
}
}
}
}
// Parameter info to be stored, and assigned to all function call statements generated below
FBPTerminal * LHSTerm = NULL ;
TArray < FBPTerminal * > RHSTerms ;
2014-04-23 17:45:37 -04:00
UEdGraphPin * ThenExecPin = NULL ;
2014-03-14 14:13:41 -04:00
UEdGraphNode * LatentTargetNode = NULL ;
int32 LatentTargetParamIndex = INDEX_NONE ;
// Grab the special case structs that use their own literal path
2015-01-20 09:33:54 -05:00
UScriptStruct * VectorStruct = GetBaseStructure ( TEXT ( " Vector " ) ) ;
UScriptStruct * RotatorStruct = GetBaseStructure ( TEXT ( " Rotator " ) ) ;
UScriptStruct * TransformStruct = GetBaseStructure ( TEXT ( " Transform " ) ) ;
2014-03-14 14:13:41 -04:00
// Check each property
bool bMatchedAllParams = true ;
for ( TFieldIterator < UProperty > It ( Function ) ; It & & ( It - > PropertyFlags & CPF_Parm ) ; + + It )
{
UProperty * Property = * It ;
bool bFoundParam = false ;
for ( int32 i = 0 ; ! bFoundParam & & ( i < RemainingPins . Num ( ) ) ; + + i )
{
UEdGraphPin * PinMatch = RemainingPins [ i ] ;
if ( FCString : : Stricmp ( * Property - > GetName ( ) , * PinMatch - > PinName ) = = 0 )
{
// Found a corresponding pin, does it match in type and direction?
if ( FKismetCompilerUtilities : : IsTypeCompatibleWithProperty ( PinMatch , Property , CompilerContext . MessageLog , CompilerContext . GetSchema ( ) , Context . NewClass ) )
{
UEdGraphPin * PinToTry = FEdGraphUtilities : : GetNetFromPin ( PinMatch ) ;
FBPTerminal * * Term = Context . NetMap . Find ( PinToTry ) ;
if ( Term ! = NULL )
{
// For literal structs, we have to verify the default here to make sure that it has valid formatting
if ( ( * Term ) - > bIsLiteral & & ( PinMatch ! = LatentInfoPin ) )
{
UStructProperty * StructProperty = Cast < UStructProperty > ( Property ) ;
if ( StructProperty )
{
UScriptStruct * Struct = StructProperty - > Struct ;
if ( Struct ! = VectorStruct
& & Struct ! = RotatorStruct
& & Struct ! = TransformStruct )
{
// Ensure all literal struct terms can be imported if its empty
if ( ( * Term ) - > Name . IsEmpty ( ) )
{
( * Term ) - > Name = TEXT ( " () " ) ;
}
int32 StructSize = Struct - > GetStructureSize ( ) ;
uint8 * StructData = ( uint8 * ) FMemory_Alloca ( StructSize ) ;
StructProperty - > InitializeValue ( StructData ) ;
// Import the literal text to a dummy struct to verify it's well-formed
FImportTextErrorContext ErrorPipe ( CompilerContext . MessageLog , Node ) ;
StructProperty - > ImportText ( * ( ( * Term ) - > Name ) , StructData , 0 , NULL , & ErrorPipe ) ;
if ( ErrorPipe . NumErrors > 0 )
{
bMatchedAllParams = false ;
}
}
}
}
if ( Property - > HasAnyPropertyFlags ( CPF_ReturnParm ) )
{
LHSTerm = * Term ;
}
else
{
2014-06-02 17:55:53 -04:00
FBPTerminal * RHSTerm = * Term ;
// if this term is an object that needs to be cast to an interface
if ( FBPTerminal * * InterfaceTerm = InterfaceTermMap . Find ( PinMatch ) )
{
UClass * InterfaceClass = CastChecked < UClass > ( PinMatch - > PinType . PinSubCategoryObject . Get ( ) ) ;
2014-09-26 11:32:41 -04:00
FBPTerminal * ClassTerm = Context . CreateLocalTerminal ( ETerminalSpecification : : TS_Literal ) ;
2014-06-02 17:55:53 -04:00
ClassTerm - > Name = InterfaceClass - > GetName ( ) ;
ClassTerm - > bIsLiteral = true ;
ClassTerm - > Source = Node ;
ClassTerm - > ObjectLiteral = InterfaceClass ;
// insert a cast op before a call to the function (and replace
// the param with the result from the cast)
FBlueprintCompiledStatement & CastStatement = Context . AppendStatementForNode ( Node ) ;
2014-12-17 16:12:53 -05:00
CastStatement . Type = InterfaceClass - > HasAnyClassFlags ( CLASS_Interface ) ? KCST_CastObjToInterface : KCST_CastInterfaceToObj ;
2014-06-02 17:55:53 -04:00
CastStatement . LHS = * InterfaceTerm ;
CastStatement . RHS . Add ( ClassTerm ) ;
CastStatement . RHS . Add ( * Term ) ;
RHSTerm = * InterfaceTerm ;
}
int32 ParameterIndex = RHSTerms . Add ( RHSTerm ) ;
2014-03-14 14:13:41 -04:00
if ( PinMatch = = LatentInfoPin )
{
// Record the (latent) output impulse from this node
2014-04-23 17:45:37 -04:00
ThenExecPin = CompilerContext . GetSchema ( ) - > FindExecutionPin ( * Node , EGPD_Output ) ;
2014-03-14 14:13:41 -04:00
if ( ( ThenExecPin ! = NULL ) & & ( ThenExecPin - > LinkedTo . Num ( ) > 0 ) )
{
LatentTargetNode = ThenExecPin - > LinkedTo [ 0 ] - > GetOwningNode ( ) ;
}
if ( LatentTargetNode ! = NULL )
{
LatentTargetParamIndex = ParameterIndex ;
}
}
}
// Make sure it isn't trying to modify a const term
if ( Property - > HasAnyPropertyFlags ( CPF_OutParm ) & & ! ( ( * Term ) - > IsTermWritable ( ) ) )
{
if ( Property - > HasAnyPropertyFlags ( CPF_ReferenceParm ) )
{
if ( ! Property - > HasAnyPropertyFlags ( CPF_ConstParm ) )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " PassReadOnlyReferenceParam_Error " , " Cannot pass a read-only variable to a reference parameter @@ " ) . ToString ( ) , PinMatch ) ;
}
}
else
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " PassReadOnlyOutputParam_Error " , " Cannot pass a read-only variable to a output parameter @@ " ) . ToString ( ) , PinMatch ) ;
}
}
}
else
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " ResolveTermPassed_Error " , " Failed to resolve term passed into @@ " ) . ToString ( ) , PinMatch ) ;
bMatchedAllParams = false ;
}
}
else
{
bMatchedAllParams = false ;
}
bFoundParam = true ;
RemainingPins . RemoveAtSwap ( i ) ;
}
}
if ( ! bFoundParam )
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * LOCTEXT ( " FindPinParameter_Error " , " Could not find a pin for the parameter %s of %s on @@ " ) . ToString ( ) , * Property - > GetName ( ) , * Function - > GetName ( ) ) , Node ) ;
bMatchedAllParams = false ;
}
}
// At this point, we should have consumed all pins. If not, there are extras that need to be removed.
for ( int32 i = 0 ; i < RemainingPins . Num ( ) ; + + i )
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * LOCTEXT ( " PinMismatchParameter_Error " , " Pin @@ named %s doesn't match any parameters of function %s " ) . ToString ( ) , * RemainingPins [ i ] - > PinName , * Function - > GetName ( ) ) , RemainingPins [ i ] ) ;
}
if ( NumErrorsAtStart = = CompilerContext . MessageLog . NumErrors )
{
// Build up a list of contexts that this function will be called on
TArray < FBPTerminal * > ContextTerms ;
if ( SelfPin ! = NULL )
{
[UE-2345] BP - enforce const-correctness in native const class method overrides
this change introduces enforcement of 'const-correctness' into implemented function graphs.
summary:
if you have a function declared in C++ like this:
UFUNCTION(BlueprintImplementableEvent)
int32 MyFunctionThatReturnsSomeValue() const;
if you implement that (BPIE) function in a Blueprint that's parented to that native class, it will now be flagged as 'const'. this makes any properties of 'self' read-only within the context of that graph, which means the compiler will emit an error if you try to set a property or otherwise call a non-const, non-static function with 'self' as the target.
if there happens to already be an implemented const function in a Blueprint that was in place prior to this change, the compiler will emit a warning instead of an error, in order to allow existing Blueprints that may currently be "violating" const within the context of a const BPIE function to still compile, while still alerting to issues that should probably be addressed.
notes:
1) this also applies to BlueprintNativeEvent (BPNE) implementations, and also when implementing BPIE/BPNE interface methods that are also declared as const
2) a const BPIE/BPNE function with no return value and no output parameters will be implemented as a "normal" impure function, and not as an event as in the non-const case
3) a const BPIE/BPNE function with a return value and/or output parameters will currently be implemented as a pure function, regardless of whether or not BlueprintCallable is specified
4) this CL also retains some consolidation of static function validation code that i had previously done, mostly to allow static functions to more easily be whitelisted for const function graphs
#codereview Nick.Whiting, Michael.Noland
[CL 2368059 by Phillip Kavan in Main branch]
2014-11-21 17:47:17 -05:00
const bool bIsConstSelfContext = Context . IsConstFunction ( ) ;
const bool bIsNonConstFunction = ! Function - > HasAnyFunctionFlags ( FUNC_Const | FUNC_Static ) ;
const bool bEnforceConstCorrectness = Context . EnforceConstCorrectness ( ) ;
auto CheckAndAddSelfTermLambda = [ this , & Node , & ContextTerms , bIsConstSelfContext , bIsNonConstFunction , bEnforceConstCorrectness ] ( FBPTerminal * Target )
{
bool bIsSelfTerm = true ;
if ( Target ! = nullptr )
{
const UEdGraphPin * SourcePin = Cast < UEdGraphPin > ( Target - > Source ) ;
bIsSelfTerm = ( SourcePin = = nullptr | | CompilerContext . GetSchema ( ) - > IsSelfPin ( * SourcePin ) ) ;
}
// Ensure const correctness within the context of the function call:
// a) Attempting to call a non-const, non-static function within a const function graph (i.e. 'const self' as context)
// b) Attempting to call a non-const, non-static function with a 'const' term linked to the target pin as the function context
if ( bIsSelfTerm & & bIsConstSelfContext & & bIsNonConstFunction )
{
// If we're not enforcing const correctness in this context, emit a warning here rather than an error, and allow compilation of this statement to proceed
if ( Target ! = nullptr )
{
if ( bEnforceConstCorrectness )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " NonConstFunctionCallOnReadOnlyTarget_Error " , " Function @@ can modify state and cannot be called on @@ because it is a read-only Target in this context " ) . ToString ( ) , Node , Target - > Source ) ;
}
else
{
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " NonConstFunctionCallOnReadOnlyTarget_Warning " , " Function @@ can modify state and should not be called on @@ because it is considered to be a read-only Target in this context " ) . ToString ( ) , Node , Target - > Source ) ;
}
}
else
{
if ( bEnforceConstCorrectness )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " NonConstFunctionCallOnReadOnlySelfScope_Error " , " Function @@ can modify state and cannot be called on 'self' because it is a read-only Target in this context " ) . ToString ( ) , Node ) ;
}
else
{
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " NonConstFunctionCallOnReadOnlySelfScope_Warning " , " Function @@ can modify state and should not be called on 'self' because it is considered to be a read-only Target in this context " ) . ToString ( ) , Node ) ;
}
}
}
ContextTerms . Add ( Target ) ;
} ;
2014-03-14 14:13:41 -04:00
if ( SelfPin - > LinkedTo . Num ( ) > 0 )
{
for ( int32 i = 0 ; i < SelfPin - > LinkedTo . Num ( ) ; i + + )
{
FBPTerminal * * pContextTerm = Context . NetMap . Find ( SelfPin - > LinkedTo [ i ] ) ;
[UE-2345] BP - enforce const-correctness in native const class method overrides
this change introduces enforcement of 'const-correctness' into implemented function graphs.
summary:
if you have a function declared in C++ like this:
UFUNCTION(BlueprintImplementableEvent)
int32 MyFunctionThatReturnsSomeValue() const;
if you implement that (BPIE) function in a Blueprint that's parented to that native class, it will now be flagged as 'const'. this makes any properties of 'self' read-only within the context of that graph, which means the compiler will emit an error if you try to set a property or otherwise call a non-const, non-static function with 'self' as the target.
if there happens to already be an implemented const function in a Blueprint that was in place prior to this change, the compiler will emit a warning instead of an error, in order to allow existing Blueprints that may currently be "violating" const within the context of a const BPIE function to still compile, while still alerting to issues that should probably be addressed.
notes:
1) this also applies to BlueprintNativeEvent (BPNE) implementations, and also when implementing BPIE/BPNE interface methods that are also declared as const
2) a const BPIE/BPNE function with no return value and no output parameters will be implemented as a "normal" impure function, and not as an event as in the non-const case
3) a const BPIE/BPNE function with a return value and/or output parameters will currently be implemented as a pure function, regardless of whether or not BlueprintCallable is specified
4) this CL also retains some consolidation of static function validation code that i had previously done, mostly to allow static functions to more easily be whitelisted for const function graphs
#codereview Nick.Whiting, Michael.Noland
[CL 2368059 by Phillip Kavan in Main branch]
2014-11-21 17:47:17 -05:00
if ( ensure ( pContextTerm ! = nullptr ) )
{
CheckAndAddSelfTermLambda ( * pContextTerm ) ;
}
2014-03-14 14:13:41 -04:00
}
}
else
{
FBPTerminal * * pContextTerm = Context . NetMap . Find ( SelfPin ) ;
[UE-2345] BP - enforce const-correctness in native const class method overrides
this change introduces enforcement of 'const-correctness' into implemented function graphs.
summary:
if you have a function declared in C++ like this:
UFUNCTION(BlueprintImplementableEvent)
int32 MyFunctionThatReturnsSomeValue() const;
if you implement that (BPIE) function in a Blueprint that's parented to that native class, it will now be flagged as 'const'. this makes any properties of 'self' read-only within the context of that graph, which means the compiler will emit an error if you try to set a property or otherwise call a non-const, non-static function with 'self' as the target.
if there happens to already be an implemented const function in a Blueprint that was in place prior to this change, the compiler will emit a warning instead of an error, in order to allow existing Blueprints that may currently be "violating" const within the context of a const BPIE function to still compile, while still alerting to issues that should probably be addressed.
notes:
1) this also applies to BlueprintNativeEvent (BPNE) implementations, and also when implementing BPIE/BPNE interface methods that are also declared as const
2) a const BPIE/BPNE function with no return value and no output parameters will be implemented as a "normal" impure function, and not as an event as in the non-const case
3) a const BPIE/BPNE function with a return value and/or output parameters will currently be implemented as a pure function, regardless of whether or not BlueprintCallable is specified
4) this CL also retains some consolidation of static function validation code that i had previously done, mostly to allow static functions to more easily be whitelisted for const function graphs
#codereview Nick.Whiting, Michael.Noland
[CL 2368059 by Phillip Kavan in Main branch]
2014-11-21 17:47:17 -05:00
CheckAndAddSelfTermLambda ( ( pContextTerm ! = nullptr ) ? * pContextTerm : nullptr ) ;
2014-03-14 14:13:41 -04:00
}
}
// Check for a call into the ubergraph, which will require a patchup later on for the exact state entry point
UEdGraphNode * * pSrcEventNode = NULL ;
if ( ! bIsLatent )
{
pSrcEventNode = CompilerContext . CallsIntoUbergraph . Find ( Node ) ;
}
// Iterate over all the contexts this functions needs to be called on, and emit a call function statement for each
for ( auto TargetListIt = ContextTerms . CreateIterator ( ) ; TargetListIt ; + + TargetListIt )
{
FBPTerminal * Target = * TargetListIt ;
FBlueprintCompiledStatement & Statement = Context . AppendStatementForNode ( Node ) ;
Statement . FunctionToCall = Function ;
Statement . FunctionContext = Target ;
Statement . Type = KCST_CallFunction ;
Statement . bIsInterfaceContext = IsCalledFunctionFromInterface ( Node ) ;
Statement . bIsParentContext = IsCalledFunctionFinal ( Node ) ;
Statement . LHS = LHSTerm ;
Statement . RHS = RHSTerms ;
if ( ! bIsLatent )
{
// Fixup ubergraph calls
if ( pSrcEventNode ! = NULL )
{
2014-04-23 17:45:37 -04:00
UEdGraphPin * ExecOut = CompilerContext . GetSchema ( ) - > FindExecutionPin ( * * pSrcEventNode , EGPD_Output ) ;
2014-03-14 14:13:41 -04:00
check ( CompilerContext . UbergraphContext ) ;
2014-04-23 17:45:37 -04:00
CompilerContext . UbergraphContext - > GotoFixupRequestMap . Add ( & Statement , ExecOut ) ;
2014-03-14 14:13:41 -04:00
Statement . UbergraphCallIndex = 0 ;
}
}
else
{
// Fixup latent functions
if ( ( LatentTargetNode ! = NULL ) & & ( Target = = ContextTerms . Last ( ) ) )
{
check ( LatentTargetParamIndex ! = INDEX_NONE ) ;
Statement . UbergraphCallIndex = LatentTargetParamIndex ;
2014-04-23 17:45:37 -04:00
Context . GotoFixupRequestMap . Add ( & Statement , ThenExecPin ) ;
2014-03-14 14:13:41 -04:00
}
}
// Check to see if we need to fix up any terms for array property coersion
if ( UK2Node_CallArrayFunction * ArrayNode = Cast < UK2Node_CallArrayFunction > ( Node ) )
{
TArray < FArrayPropertyPinCombo > ArrayPinInfo ;
ArrayNode - > GetArrayPins ( ArrayPinInfo ) ;
for ( auto Iter = ArrayPinInfo . CreateConstIterator ( ) ; Iter ; + + Iter )
{
UEdGraphPin * ArrayPropPin = Iter - > ArrayPropPin ;
UEdGraphPin * ArrayTargetPin = Iter - > ArrayPin ;
if ( ArrayPropPin & & ArrayTargetPin )
{
// Find the array property literal term, used for specifying the type of TargetArray
for ( int32 i = 0 ; i < Context . Literals . Num ( ) ; i + + )
{
FBPTerminal & Term = Context . Literals [ i ] ;
if ( Term . Source = = ArrayPropPin )
{
// Now, map the array property literal term to the TargetArray term. The AssociatedVarProperty will later be filled in as the array prop's object literal in the backend
UEdGraphPin * Net = FEdGraphUtilities : : GetNetFromPin ( ArrayTargetPin ) ;
FBPTerminal * * TargetTerm = Context . NetMap . Find ( Net ) ;
if ( TargetTerm )
{
Statement . ArrayCoersionTermMap . Add ( & Term , * TargetTerm ) ;
}
}
}
}
}
}
AdditionalCompiledStatementHandling ( Context , Node , Statement ) ;
}
// Create the exit from this node if there is one
if ( bIsLatent )
{
// End this thread of execution; the latent function will resume it at some point in the future
FBlueprintCompiledStatement & PopStatement = Context . AppendStatementForNode ( Node ) ;
PopStatement . Type = KCST_EndOfThread ;
}
else
{
// Generate the output impulse from this node
if ( ! IsCalledFunctionPure ( Node ) )
{
GenerateSimpleThenGoto ( Context , * Node ) ;
}
}
}
}
else
{
FString WarningMessage = FString : : Printf ( * LOCTEXT ( " FindFunction_Error " , " Could not find the function '%s' called from @@ " ) . ToString ( ) , * GetFunctionNameFromNode ( Node ) ) ;
CompilerContext . MessageLog . Warning ( * WarningMessage , Node ) ;
}
}
UClass * FKCHandler_CallFunction : : GetCallingContext ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
// Find the calling scope
UClass * SearchScope = Context . NewClass ;
UK2Node_CallFunction * CallFuncNode = Cast < UK2Node_CallFunction > ( Node ) ;
if ( ( CallFuncNode ! = NULL ) & & CallFuncNode - > bIsFinalFunction )
{
if ( UK2Node_CallParentFunction * ParentCall = Cast < UK2Node_CallParentFunction > ( Node ) )
{
// Special Case: super call functions should search up their class hierarchy, and find the first legitimate implementation of the function
const FName FuncName = CallFuncNode - > FunctionReference . GetMemberName ( ) ;
UClass * SearchContext = Context . NewClass - > GetSuperClass ( ) ;
UFunction * ParentFunc = NULL ;
if ( SearchContext ! = NULL )
{
ParentFunc = SearchContext - > FindFunctionByName ( FuncName ) ;
}
return ParentFunc ? ParentFunc - > GetOuterUClass ( ) : NULL ;
}
else
{
// Final functions need the call context to be the specified class, so don't bother checking for the self pin. The schema should enforce this.
2015-03-12 14:17:48 -04:00
return CallFuncNode - > FunctionReference . GetMemberParentClass ( CallFuncNode - > GetBlueprintClassFromNode ( ) ) ;
2014-03-14 14:13:41 -04:00
}
}
else
{
UEdGraphPin * SelfPin = CompilerContext . GetSchema ( ) - > FindSelfPin ( * Node , EGPD_Input ) ;
if ( SelfPin ! = NULL )
{
SearchScope = Cast < UClass > ( Context . GetScopeFromPinType ( SelfPin - > PinType , Context . NewClass ) ) ;
}
}
return SearchScope ;
}
UClass * FKCHandler_CallFunction : : GetTrueCallingClass ( FKismetFunctionContext & Context , UEdGraphPin * SelfPin )
{
if ( SelfPin ! = NULL )
{
2014-04-23 17:58:27 -04:00
UEdGraphSchema_K2 const * K2Schema = CompilerContext . GetSchema ( ) ;
2014-03-14 14:13:41 -04:00
// TODO: here FBlueprintCompiledStatement::GetScopeFromPinType should be called, but since FEdGraphPinType::PinSubCategory is not always initialized properly that function works wrong
// return Cast<UClass>(Context.GetScopeFromPinType(SelfPin->PinType, Context.NewClass));
FEdGraphPinType & Type = SelfPin - > PinType ;
2014-04-23 17:58:27 -04:00
if ( ( Type . PinCategory = = K2Schema - > PC_Object ) | | ( Type . PinCategory = = K2Schema - > PC_Class ) | | ( Type . PinCategory = = K2Schema - > PC_Interface ) )
2014-03-14 14:13:41 -04:00
{
2014-04-23 17:58:27 -04:00
if ( ! Type . PinSubCategory . IsEmpty ( ) & & ( Type . PinSubCategory ! = K2Schema - > PSC_Self ) )
2014-03-14 14:13:41 -04:00
{
return Cast < UClass > ( Type . PinSubCategoryObject . Get ( ) ) ;
}
}
}
return Context . NewClass ;
}
void FKCHandler_CallFunction : : RegisterNets ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
2014-06-02 17:55:53 -04:00
UEdGraphSchema_K2 const * K2Schema = CompilerContext . GetSchema ( ) ;
2014-03-14 14:13:41 -04:00
2014-06-02 17:55:53 -04:00
UFunction * Function = FindFunction ( Context , Node ) ;
2014-03-14 14:13:41 -04:00
if ( Function ! = NULL )
{
TArray < FString > DefaultToSelfParamNames ;
TArray < FString > RequiresSetValue ;
if ( Function - > HasMetaData ( FBlueprintMetadata : : MD_DefaultToSelf ) )
{
FString const DefaltToSelfPinName = Function - > GetMetaData ( FBlueprintMetadata : : MD_DefaultToSelf ) ;
2014-09-10 16:24:10 -04:00
DefaultToSelfParamNames . Add ( DefaltToSelfPinName ) ;
2014-03-14 14:13:41 -04:00
}
if ( Function - > HasMetaData ( FBlueprintMetadata : : MD_WorldContext ) )
{
[UE-2345] BP - enforce const-correctness in native const class method overrides
this change introduces enforcement of 'const-correctness' into implemented function graphs.
summary:
if you have a function declared in C++ like this:
UFUNCTION(BlueprintImplementableEvent)
int32 MyFunctionThatReturnsSomeValue() const;
if you implement that (BPIE) function in a Blueprint that's parented to that native class, it will now be flagged as 'const'. this makes any properties of 'self' read-only within the context of that graph, which means the compiler will emit an error if you try to set a property or otherwise call a non-const, non-static function with 'self' as the target.
if there happens to already be an implemented const function in a Blueprint that was in place prior to this change, the compiler will emit a warning instead of an error, in order to allow existing Blueprints that may currently be "violating" const within the context of a const BPIE function to still compile, while still alerting to issues that should probably be addressed.
notes:
1) this also applies to BlueprintNativeEvent (BPNE) implementations, and also when implementing BPIE/BPNE interface methods that are also declared as const
2) a const BPIE/BPNE function with no return value and no output parameters will be implemented as a "normal" impure function, and not as an event as in the non-const case
3) a const BPIE/BPNE function with a return value and/or output parameters will currently be implemented as a pure function, regardless of whether or not BlueprintCallable is specified
4) this CL also retains some consolidation of static function validation code that i had previously done, mostly to allow static functions to more easily be whitelisted for const function graphs
#codereview Nick.Whiting, Michael.Noland
[CL 2368059 by Phillip Kavan in Main branch]
2014-11-21 17:47:17 -05:00
const bool bHasIntrinsicWorldContext = ! K2Schema - > IsStaticFunctionGraph ( Context . SourceGraph ) & & Context . Blueprint - > ParentClass - > GetDefaultObject ( ) - > ImplementsGetWorld ( ) ;
2014-03-14 14:13:41 -04:00
FString const WorldContextPinName = Function - > GetMetaData ( FBlueprintMetadata : : MD_WorldContext ) ;
if ( bHasIntrinsicWorldContext )
{
DefaultToSelfParamNames . Add ( WorldContextPinName ) ;
}
2014-09-10 16:39:25 -04:00
else if ( ! Function - > HasMetaData ( FBlueprintMetadata : : MD_CallableWithoutWorldContext ) )
2014-03-14 14:13:41 -04:00
{
RequiresSetValue . Add ( WorldContextPinName ) ;
}
}
for ( auto It = Node - > Pins . CreateConstIterator ( ) ; It ; + + It )
{
UEdGraphPin * Pin = ( * It ) ;
const bool bIsConnected = ( Pin - > LinkedTo . Num ( ) ! = 0 ) ;
// if this pin could use a default (it doesn't have a connection or default of its own)
if ( ! bIsConnected & & ( Pin - > DefaultObject = = NULL ) )
{
2015-03-06 12:35:22 -05:00
if ( DefaultToSelfParamNames . Contains ( Pin - > PinName ) & & FKismetCompilerUtilities : : ValidateSelfCompatibility ( Pin , Context ) )
2014-03-14 14:13:41 -04:00
{
ensure ( Pin - > PinType . PinSubCategoryObject ! = NULL ) ;
2014-04-23 17:58:27 -04:00
ensure ( ( Pin - > PinType . PinCategory = = K2Schema - > PC_Object ) | | ( Pin - > PinType . PinCategory = = K2Schema - > PC_Interface ) ) ;
2014-03-14 14:13:41 -04:00
FBPTerminal * Term = Context . RegisterLiteral ( Pin ) ;
Term - > Type . PinSubCategory = CompilerContext . GetSchema ( ) - > PN_Self ;
Context . NetMap . Add ( Pin , Term ) ;
}
else if ( RequiresSetValue . Contains ( Pin - > PinName ) )
{
CompilerContext . MessageLog . Error ( * NSLOCTEXT ( " KismetCompiler " , " PinMustHaveConnection_Error " , " Pin @@ must have a connection " ) . ToString ( ) , Pin ) ;
}
}
}
}
2014-06-02 17:55:53 -04:00
for ( UEdGraphPin * Pin : Node - > Pins )
{
if ( ( Pin - > Direction ! = EGPD_Input ) | | ( Pin - > LinkedTo . Num ( ) = = 0 ) )
{
continue ;
}
// if we have an object plugged into an interface pin, let's create a
// term that'll be used as an intermediate, holding the result of a cast
// from object to interface
2014-12-17 16:12:53 -05:00
if ( ( ( Pin - > PinType . PinCategory = = K2Schema - > PC_Interface ) & & ( Pin - > LinkedTo [ 0 ] - > PinType . PinCategory = = K2Schema - > PC_Object ) ) | |
( ( Pin - > PinType . PinCategory = = K2Schema - > PC_Object ) & & ( Pin - > LinkedTo [ 0 ] - > PinType . PinCategory = = K2Schema - > PC_Interface ) ) )
2014-06-02 17:55:53 -04:00
{
2014-09-26 11:32:41 -04:00
FBPTerminal * InterfaceTerm = Context . CreateLocalTerminal ( ) ;
2014-06-06 18:15:56 -04:00
InterfaceTerm - > CopyFromPin ( Pin , Context . NetNameMap - > MakeValidName ( Pin ) + TEXT ( " _CastInput " ) ) ;
2014-06-02 17:55:53 -04:00
InterfaceTerm - > Source = Node ;
InterfaceTermMap . Add ( Pin , InterfaceTerm ) ;
}
}
2014-03-14 14:13:41 -04:00
FNodeHandlingFunctor : : RegisterNets ( Context , Node ) ;
}
void FKCHandler_CallFunction : : RegisterNet ( FKismetFunctionContext & Context , UEdGraphPin * Net )
{
// This net is an output from a function call
2014-09-26 11:32:41 -04:00
FBPTerminal * Term = Context . CreateLocalTerminalFromPinAutoChooseScope ( Net , Context . NetNameMap - > MakeValidName ( Net ) ) ;
2014-03-14 14:13:41 -04:00
Context . NetMap . Add ( Net , Term ) ;
}
UFunction * FKCHandler_CallFunction : : FindFunction ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
UClass * CallingContext = GetCallingContext ( Context , Node ) ;
FString FunctionName = GetFunctionNameFromNode ( Node ) ;
return CallingContext - > FindFunctionByName ( * FunctionName ) ;
}
void FKCHandler_CallFunction : : Transform ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
// Add an object reference pin for this call
//UEdGraphPin* OperatingOn = Node->CreatePin(EGPD_Input, Schema->PC_Object, TEXT(""), TEXT("OperatingContext"));
if ( IsCalledFunctionPure ( Node ) )
{
// Flag for removal if pure and there are no consumers of the outputs
//@TODO: This isn't recursive (and shouldn't be here), it'll just catch the last node in a line of pure junk
bool bAnyOutputsUsed = false ;
for ( int32 PinIndex = 0 ; PinIndex < Node - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * Pin = Node - > Pins [ PinIndex ] ;
if ( ( Pin - > Direction = = EGPD_Output ) & & ( Pin - > LinkedTo . Num ( ) > 0 ) )
{
bAnyOutputsUsed = true ;
break ;
}
}
if ( ! bAnyOutputsUsed )
{
//@TODO: Remove this node, not just warn about it
}
}
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Find the function, starting at the parent class
UFunction * Function = FindFunction ( Context , Node ) ;
if ( ( Function ! = NULL ) & & ( Function - > HasMetaData ( FBlueprintMetadata : : MD_Latent ) ) )
{
UK2Node_CallFunction * CallFuncNode = CastChecked < UK2Node_CallFunction > ( Node ) ;
UEdGraphPin * OldOutPin = K2Schema - > FindExecutionPin ( * CallFuncNode , EGPD_Output ) ;
if ( ( OldOutPin ! = NULL ) & & ( OldOutPin - > LinkedTo . Num ( ) > 0 ) )
{
// Create a dummy execution sequence that will be the target of the return call from the latent action
2015-02-17 16:28:07 -05:00
UK2Node_ExecutionSequence * DummyNode = CompilerContext . SpawnIntermediateNode < UK2Node_ExecutionSequence > ( CallFuncNode ) ;
2014-03-14 14:13:41 -04:00
DummyNode - > AllocateDefaultPins ( ) ;
// Wire in the dummy node
UEdGraphPin * NewInPin = K2Schema - > FindExecutionPin ( * DummyNode , EGPD_Input ) ;
UEdGraphPin * NewOutPin = K2Schema - > FindExecutionPin ( * DummyNode , EGPD_Output ) ;
if ( ( NewInPin ! = NULL ) & & ( NewOutPin ! = NULL ) )
{
2014-04-23 17:45:37 -04:00
CompilerContext . MessageLog . NotifyIntermediateObjectCreation ( NewOutPin , OldOutPin ) ;
2014-03-14 14:13:41 -04:00
while ( OldOutPin - > LinkedTo . Num ( ) > 0 )
{
UEdGraphPin * LinkedPin = OldOutPin - > LinkedTo [ 0 ] ;
LinkedPin - > BreakLinkTo ( OldOutPin ) ;
LinkedPin - > MakeLinkTo ( NewOutPin ) ;
}
OldOutPin - > MakeLinkTo ( NewInPin ) ;
}
}
}
}
void FKCHandler_CallFunction : : Compile ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
check ( NULL ! = Node ) ;
//@TODO: Can probably move this earlier during graph verification instead of compilation, but after island pruning
if ( ! IsCalledFunctionPure ( Node ) )
{
// For imperative nodes, make sure the exec function was actually triggered and not just included due to an output data dependency
UEdGraphPin * ExecTriggeringPin = CompilerContext . GetSchema ( ) - > FindExecutionPin ( * Node , EGPD_Input ) ;
if ( ExecTriggeringPin = = NULL )
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * NSLOCTEXT ( " KismetCompiler " , " NoValidExecutionPinForCallFunc_Error " , " @@ must have a valid execution pin " ) . ToString ( ) ) , Node ) ;
return ;
}
else if ( ExecTriggeringPin - > LinkedTo . Num ( ) = = 0 )
{
CompilerContext . MessageLog . Warning ( * FString : : Printf ( * NSLOCTEXT ( " KismetCompiler " , " NodeNeverExecuted_Warning " , " @@ will never be executed " ) . ToString ( ) ) , Node ) ;
return ;
}
}
// Validate the self pin again if it is disconnected, because pruning isolated nodes could have caused an invalid target
UEdGraphPin * SelfPin = CompilerContext . GetSchema ( ) - > FindSelfPin ( * Node , EGPD_Input ) ;
if ( SelfPin & & ( SelfPin - > LinkedTo . Num ( ) = = 0 ) )
{
FEdGraphPinType SelfType ;
SelfType . PinCategory = CompilerContext . GetSchema ( ) - > PC_Object ;
SelfType . PinSubCategory = CompilerContext . GetSchema ( ) - > PSC_Self ;
if ( ! CompilerContext . GetSchema ( ) - > ArePinTypesCompatible ( SelfType , SelfPin - > PinType , Context . NewClass ) & & ( SelfPin - > DefaultObject = = NULL ) )
{
CompilerContext . MessageLog . Error ( * NSLOCTEXT ( " KismetCompiler " , " PinMustHaveConnectionPruned_Error " , " Pin @@ must have a connection. Self pins cannot be connected to nodes that are culled. " ) . ToString ( ) , SelfPin ) ;
}
}
// Make sure the function node is valid to call
CreateFunctionCallStatement ( Context , Node , SelfPin ) ;
}
void FKCHandler_CallFunction : : CheckIfFunctionIsCallable ( UFunction * Function , FKismetFunctionContext & Context , UEdGraphNode * Node )
{
// Verify that the function is a Blueprint callable function (in case a BlueprintCallable specifier got removed)
if ( ! Function - > HasAnyFunctionFlags ( FUNC_BlueprintCallable ) & & ( Function - > GetOuter ( ) ! = Context . NewClass ) )
{
if ( ! IsCalledFunctionFinal ( Node ) & & Function - > GetName ( ) . Find ( CompilerContext . GetSchema ( ) - > FN_ExecuteUbergraphBase . ToString ( ) ) )
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * NSLOCTEXT ( " KismetCompiler " , " ShouldNotCallFromBlueprint_Error " , " Function '%s' called from @@ should not be called from a Blueprint " ) . ToString ( ) , * Function - > GetName ( ) ) , Node ) ;
}
}
}
// Get the name of the function to call from the node
FString FKCHandler_CallFunction : : GetFunctionNameFromNode ( UEdGraphNode * Node ) const
{
UK2Node_CallFunction * CallFuncNode = Cast < UK2Node_CallFunction > ( Node ) ;
if ( CallFuncNode )
{
return CallFuncNode - > FunctionReference . GetMemberName ( ) . ToString ( ) ;
}
else
{
CompilerContext . MessageLog . Error ( * NSLOCTEXT ( " KismetCompiler " , " UnableResolveFunctionName_Error " , " Unable to resolve function name for @@ " ) . ToString ( ) , Node ) ;
return TEXT ( " " ) ;
}
}
# if _MSC_VER
# pragma warning(pop)
# endif
//////////////////////////////////////////////////////////////////////////
# undef LOCTEXT_NAMESPACE