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 "KismetCompiler.h"
# include "VariableSetHandler.h"
# define LOCTEXT_NAMESPACE "VariableSetHandler"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_VariableSet
void FKCHandler_VariableSet : : RegisterNet ( FKismetFunctionContext & Context , UEdGraphPin * Net )
{
// This net is a variable write
ResolveAndRegisterScopedTerm ( Context , Net , Context . VariableReferences ) ;
}
void FKCHandler_VariableSet : : RegisterNets ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
if ( UK2Node_Variable * SetterNode = Cast < UK2Node_Variable > ( Node ) )
{
SetterNode - > CheckForErrors ( CompilerContext . GetSchema ( ) , Context . MessageLog ) ;
if ( FBlueprintEditorUtils : : IsPropertyReadOnlyInCurrentBlueprint ( Context . Blueprint , SetterNode - > GetPropertyForVariable ( ) ) )
{
2015-01-30 11:07:49 -05:00
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " BlueprintReadOnlyOrPrivate_Error " , " The property is marked as BlueprintReadOnly or Private. It cannot be modifed in the blueprint. @@ " ) . ToString ( ) , Node ) ;
2014-03-14 14:13:41 -04:00
}
2014-05-09 17:43:40 -04:00
// Report an error that the local variable could not be found
if ( SetterNode - > VariableReference . IsLocalScope ( ) & & SetterNode - > GetPropertyForVariable ( ) = = NULL )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " VariableName " ) , FText : : FromName ( SetterNode - > VariableReference . GetMemberName ( ) ) ) ;
2014-05-30 10:43:00 -04:00
if ( SetterNode - > VariableReference . GetMemberScopeName ( ) ! = Context . Function - > GetName ( ) )
{
Args . Add ( TEXT ( " ScopeName " ) , FText : : FromString ( SetterNode - > VariableReference . GetMemberScopeName ( ) ) ) ;
CompilerContext . MessageLog . Warning ( * FText : : Format ( LOCTEXT ( " LocalVariableNotFoundInScope_Error " , " Unable to find local variable with name '{VariableName}' for @@, scope expected: @@, scope found: {ScopeName} " ) , Args ) . ToString ( ) , Node , Node - > GetGraph ( ) ) ;
}
else
{
CompilerContext . MessageLog . Warning ( * FText : : Format ( LOCTEXT ( " LocalVariableNotFound_Error " , " Unable to find local variable with name '{VariableName}' for @@ " ) , Args ) . ToString ( ) , Node ) ;
}
2014-05-09 17:43:40 -04:00
}
2014-03-14 14:13:41 -04:00
}
for ( int32 PinIndex = 0 ; PinIndex < Node - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * Net = Node - > Pins [ PinIndex ] ;
if ( ! CompilerContext . GetSchema ( ) - > IsMetaPin ( * Net ) & & ( Net - > Direction = = EGPD_Input ) )
{
if ( ValidateAndRegisterNetIfLiteral ( Context , Net ) )
{
RegisterNet ( Context , Net ) ;
}
}
}
}
void FKCHandler_VariableSet : : InnerAssignment ( FKismetFunctionContext & Context , UEdGraphNode * Node , UEdGraphPin * VariablePin , UEdGraphPin * ValuePin )
{
FBPTerminal * * VariableTerm = Context . NetMap . Find ( VariablePin ) ;
if ( VariableTerm = = NULL )
{
VariableTerm = Context . NetMap . Find ( FEdGraphUtilities : : GetNetFromPin ( VariablePin ) ) ;
}
FBPTerminal * * ValueTerm = Context . LiteralHackMap . Find ( ValuePin ) ;
if ( ValueTerm = = NULL )
{
ValueTerm = Context . NetMap . Find ( FEdGraphUtilities : : GetNetFromPin ( ValuePin ) ) ;
}
if ( ( VariableTerm ! = NULL ) & & ( ValueTerm ! = NULL ) )
{
2014-12-17 16:12:53 -05:00
FKismetCompilerUtilities : : CreateObjectAssignmentStatement ( Context , Node , * ValueTerm , * VariableTerm ) ;
2014-03-14 14:13:41 -04:00
if ( ! ( * VariableTerm ) - > IsTermWritable ( ) )
{
[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 the term is not explicitly marked as read-only, then we're attempting to set a variable on a const target
if ( ! ( * VariableTerm ) - > AssociatedVarProperty - > HasAnyPropertyFlags ( CPF_BlueprintReadOnly ) )
{
if ( Context . EnforceConstCorrectness ( ) )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " WriteToReadOnlyContext_Error " , " Variable @@ is read-only within this context and cannot be set to a new value " ) . ToString ( ) , VariablePin ) ;
}
else
{
// Warn, but still allow compilation to succeed
CompilerContext . MessageLog . Warning ( * LOCTEXT ( " WriteToReadOnlyContext_Warning " , " Variable @@ is considered to be read-only within this context and should not be set to a new value " ) . ToString ( ) , VariablePin ) ;
}
}
else
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " WriteConst_Error " , " Cannot write to const @@ " ) . ToString ( ) , VariablePin ) ;
}
2014-03-14 14:13:41 -04:00
}
}
else
{
if ( VariablePin ! = ValuePin )
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " ResolveValueIntoVariablePin_Error " , " Failed to resolve term @@ passed into @@ " ) . ToString ( ) , ValuePin , VariablePin ) ;
}
else
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " ResolveTermPassed_Error " , " Failed to resolve term passed into @@ " ) . ToString ( ) , VariablePin ) ;
}
}
}
2015-04-10 15:11:43 -04:00
void FKCHandler_VariableSet : : GenerateAssigments ( FKismetFunctionContext & Context , UEdGraphNode * Node )
2014-03-14 14:13:41 -04:00
{
// SubCategory is an object type or "" for the stack frame, default scope is Self
// Each input pin is the name of a variable
// Each input pin represents an assignment statement
for ( int32 PinIndex = 0 ; PinIndex < Node - > Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * Pin = Node - > Pins [ PinIndex ] ;
if ( CompilerContext . GetSchema ( ) - > IsMetaPin ( * Pin ) )
{
}
else if ( Pin - > Direction = = EGPD_Input )
{
InnerAssignment ( Context , Node , Pin , Pin ) ;
}
else
{
CompilerContext . MessageLog . Error ( * FString : : Printf ( * LOCTEXT ( " ExpectedOnlyInputPins_Error " , " Expected only input pins on @@ but found @@ " ) . ToString ( ) ) , Node , Pin ) ;
}
}
2015-04-10 15:11:43 -04:00
}
void FKCHandler_VariableSet : : Compile ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
GenerateAssigments ( Context , Node ) ;
2014-03-14 14:13:41 -04:00
// Generate the output impulse from this node
GenerateSimpleThenGoto ( Context , * Node ) ;
}
void FKCHandler_VariableSet : : Transform ( FKismetFunctionContext & Context , UEdGraphNode * Node )
{
// Expands node out to include a (local) call to the RepNotify function if necessary
UK2Node_VariableSet * SetNotify = Cast < UK2Node_VariableSet > ( Node ) ;
if ( ( SetNotify ! = NULL ) )
{
if ( SetNotify - > ShouldFlushDormancyOnSet ( ) )
{
// Create CallFuncNode
UK2Node_CallFunction * CallFuncNode = Node - > GetGraph ( ) - > CreateBlankNode < UK2Node_CallFunction > ( ) ;
CallFuncNode - > FunctionReference . SetExternalMember ( NAME_FlushNetDormancy , AActor : : StaticClass ( ) ) ;
CallFuncNode - > AllocateDefaultPins ( ) ;
// Copy self pin
UEdGraphPin * NewSelfPin = CallFuncNode - > FindPinChecked ( CompilerContext . GetSchema ( ) - > PN_Self ) ;
UEdGraphPin * OldSelfPin = Node - > FindPinChecked ( CompilerContext . GetSchema ( ) - > PN_Self ) ;
NewSelfPin - > CopyPersistentDataFromOldPin ( * OldSelfPin ) ;
// link new CallFuncNode -> Set Node
UEdGraphPin * OldExecPin = Node - > FindPin ( CompilerContext . GetSchema ( ) - > PN_Execute ) ;
check ( OldExecPin ) ;
UEdGraphPin * NewExecPin = CallFuncNode - > GetExecPin ( ) ;
if ( ensure ( NewExecPin ) )
{
NewExecPin - > CopyPersistentDataFromOldPin ( * OldExecPin ) ;
OldExecPin - > BreakAllPinLinks ( ) ;
CallFuncNode - > GetThenPin ( ) - > MakeLinkTo ( OldExecPin ) ;
}
}
if ( SetNotify - > HasLocalRepNotify ( ) )
{
UK2Node_CallFunction * CallFuncNode = Node - > GetGraph ( ) - > CreateBlankNode < UK2Node_CallFunction > ( ) ;
CallFuncNode - > FunctionReference . SetExternalMember ( SetNotify - > GetRepNotifyName ( ) , SetNotify - > GetVariableSourceClass ( ) ) ;
CallFuncNode - > AllocateDefaultPins ( ) ;
// Copy self pin
UEdGraphPin * NewSelfPin = CallFuncNode - > FindPinChecked ( CompilerContext . GetSchema ( ) - > PN_Self ) ;
UEdGraphPin * OldSelfPin = Node - > FindPinChecked ( CompilerContext . GetSchema ( ) - > PN_Self ) ;
NewSelfPin - > CopyPersistentDataFromOldPin ( * OldSelfPin ) ;
// link Set Node -> new CallFuncNode
UEdGraphPin * OldThenPin = Node - > FindPin ( CompilerContext . GetSchema ( ) - > PN_Then ) ;
check ( OldThenPin ) ;
UEdGraphPin * NewThenPin = CallFuncNode - > GetThenPin ( ) ;
if ( ensure ( NewThenPin ) )
{
// Link Set Node -> Notify
NewThenPin - > CopyPersistentDataFromOldPin ( * OldThenPin ) ;
OldThenPin - > BreakAllPinLinks ( ) ;
OldThenPin - > MakeLinkTo ( CallFuncNode - > GetExecPin ( ) ) ;
}
}
}
}
# undef LOCTEXT_NAMESPACE