Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_MCDelegate.cpp
jordan hoffmann 0e53056bc6 Clang now optimizes away this==nullptr. Calls to IsChildOf from a null UObject pointer will cause undefined behavior. This is a retroactive attempt to pad potentially dangerous calls to IsChildOf with a null check in the following directories:
- Plugins/BlueprintContext
- Editor/GlueprintGraph
- Editor/GraphEditor
- Editor/Kismet
- Editor/KismetCompiler
- Editor/UnrealEd/Private/Kismet2

note: if you're seeing this CL in the perforce history because you're trying to figure out why there's a null check that doesn't make sense, This is why. The goal of this CL is to preserve the behavior before IsChildOf changed rather than analyze whether that behavior makes sense. Use your best judgement

#rb marc.audy
#preflight 6299023a6438e3c731307a69

[CL 20474984 by jordan hoffmann in ue5-main branch]
2022-06-02 16:09:18 -04:00

471 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/Class.h"
#include "Templates/Casts.h"
#include "UObject/UnrealType.h"
#include "Engine/Blueprint.h"
#include "Engine/MemberReference.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "EdGraph/EdGraphSchema.h"
#include "EdGraphSchema_K2.h"
#include "K2Node.h"
#include "K2Node_CallFunction.h"
#include "K2Node_BaseMCDelegate.h"
#include "K2Node_AddDelegate.h"
#include "K2Node_CallDelegate.h"
#include "K2Node_ClearDelegate.h"
#include "K2Node_RemoveDelegate.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiler.h"
#include "DelegateNodeHandlers.h"
struct FK2Node_BaseMCDelegateHelper
{
static FName DelegatePinName;
};
FName FK2Node_BaseMCDelegateHelper::DelegatePinName(TEXT("Delegate"));
/////// UK2Node_BaseMCDelegate ///////////
UK2Node_BaseMCDelegate::UK2Node_BaseMCDelegate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UK2Node_BaseMCDelegate::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
if(FProperty* Property = GetProperty())
{
if(!Property->HasAllPropertyFlags(CPF_BlueprintAssignable))
{
MessageLog.Error(*NSLOCTEXT("K2Node", "BaseMCDelegateNotAssignable", "Event Dispatcher is not 'BlueprintAssignable' @@").ToString(), this);
}
}
}
bool UK2Node_BaseMCDelegate::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
UEdGraphSchema const* Schema = TargetGraph->GetSchema();
EGraphType GraphType = Schema->GetGraphType(TargetGraph);
bool const bIsCompatible = (GraphType == GT_Ubergraph) || (GraphType == GT_Function);
return bIsCompatible&& Super::IsCompatibleWithGraph(TargetGraph);
}
UK2Node::ERedirectType UK2Node_BaseMCDelegate::DoPinsMatchForReconstruction(const UEdGraphPin* NewPin, int32 NewPinIndex, const UEdGraphPin* OldPin, int32 OldPinIndex) const
{
ERedirectType OrginalResult = Super::DoPinsMatchForReconstruction(NewPin, NewPinIndex, OldPin, OldPinIndex);
if ((ERedirectType::ERedirectType_None == OrginalResult) && NewPin && OldPin)
{
if ((OldPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Delegate) &&
(NewPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Delegate) &&
(NewPin->PinName == OldPin->PinName))
{
return ERedirectType_Name;
}
}
return OrginalResult;
}
void UK2Node_BaseMCDelegate::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
UClass* PropertyOwnerClass = DelegateReference.GetMemberParentClass(GetBlueprintClassFromNode());
if (PropertyOwnerClass != nullptr)
{
PropertyOwnerClass = PropertyOwnerClass->GetAuthoritativeClass();
}
UEdGraphPin* SelfPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, PropertyOwnerClass, UEdGraphSchema_K2::PN_Self);
if (SelfPin)
{
SelfPin->PinFriendlyName = NSLOCTEXT("K2Node", "BaseMCDelegateSelfPinName", "Target");
}
}
UFunction* UK2Node_BaseMCDelegate::GetDelegateSignature(bool bForceNotFromSkelClass) const
{
UClass* OwnerClass = DelegateReference.GetMemberParentClass(GetBlueprintClassFromNode());
if (bForceNotFromSkelClass)
{
OwnerClass = (OwnerClass != nullptr) ? OwnerClass->GetAuthoritativeClass() : nullptr;
}
else if (UBlueprintGeneratedClass* BpClassOwner = Cast<UBlueprintGeneratedClass>(OwnerClass))
{
UBlueprint* DelegateBlueprint = Cast<UBlueprint>(BpClassOwner->ClassGeneratedBy);
// favor the skeleton class, because the generated class may not
// have the delegate yet (hasn't been compiled with it), or it could
// be out of date
UClass* SkeletonClass = DelegateBlueprint ? DelegateBlueprint->SkeletonGeneratedClass : nullptr;
OwnerClass = SkeletonClass ? SkeletonClass : OwnerClass;
}
FMemberReference ReferenceToUse;
ReferenceToUse.SetDirect(DelegateReference.GetMemberName(), DelegateReference.GetMemberGuid(), OwnerClass, /*bIsConsideredSelfContext =*/false);
FMulticastDelegateProperty* DelegateProperty = ReferenceToUse.ResolveMember<FMulticastDelegateProperty>();
return (DelegateProperty != nullptr) ? DelegateProperty->SignatureFunction : nullptr;
}
UEdGraphPin* UK2Node_BaseMCDelegate::GetDelegatePin() const
{
return FindPin(FK2Node_BaseMCDelegateHelper::DelegatePinName);
}
FString UK2Node_BaseMCDelegate::GetDocumentationLink() const
{
UClass* ParentClass = nullptr;
if (DelegateReference.IsSelfContext())
{
if (HasValidBlueprint())
{
UField* Delegate = FindUField<UField>(GetBlueprint()->GeneratedClass, DelegateReference.GetMemberName());
if (Delegate)
{
ParentClass = Delegate->GetOwnerClass();
}
}
}
else
{
ParentClass = DelegateReference.GetMemberParentClass(GetBlueprintClassFromNode());
}
if (ParentClass)
{
return FString( TEXT("Shared/") ) + ParentClass->GetName();
}
return FString();
}
FString UK2Node_BaseMCDelegate::GetDocumentationExcerptName() const
{
return DelegateReference.GetMemberName().ToString();
}
void UK2Node_BaseMCDelegate::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
const bool bAllowMultipleSelfs = AllowMultipleSelfs(true);
if(bAllowMultipleSelfs)
{
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
check(Schema);
UEdGraphPin* MultiSelf = Schema->FindSelfPin(*this, EEdGraphPinDirection::EGPD_Input);
check(MultiSelf);
const bool bProperInputToExpandForEach =
(MultiSelf->LinkedTo.Num()) &&
(NULL != MultiSelf->LinkedTo[0]) &&
(MultiSelf->LinkedTo[0]->PinType.IsArray());
if(bProperInputToExpandForEach)
{
if(MultiSelf->LinkedTo.Num() > 1)
{
CompilerContext.MessageLog.Error(*NSLOCTEXT("K2Node", "BaseMCDelegateMultiArray", "Event Dispatcher does not accept multi-array-self @@").ToString(), this);
}
else
{
UK2Node_CallFunction::CallForEachElementInArrayExpansion(this, MultiSelf, CompilerContext, SourceGraph);
}
}
}
}
bool UK2Node_BaseMCDelegate::IsAuthorityOnly() const
{
const FMulticastDelegateProperty* DelegateProperty = DelegateReference.ResolveMember<FMulticastDelegateProperty>(GetBlueprintClassFromNode());
return DelegateProperty && DelegateProperty->HasAnyPropertyFlags(CPF_BlueprintAuthorityOnly);
}
bool UK2Node_BaseMCDelegate::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{
const UBlueprint* SourceBlueprint = GetBlueprint();
FProperty* MCProperty = GetProperty();
UClass* PropertySourceClass = MCProperty ? MCProperty->GetOwnerClass() : nullptr;
const bool bPropertyResult = (PropertySourceClass != NULL) && (PropertySourceClass->ClassGeneratedBy != SourceBlueprint);
if (bPropertyResult && OptionalOutput)
{
OptionalOutput->AddUnique(PropertySourceClass);
}
UFunction* Signature = GetDelegateSignature(true);
UClass* SignatureSourceClass = Signature ? Signature->GetOwnerClass() : nullptr;
const bool bSignatureResult = (SignatureSourceClass != NULL) && (SignatureSourceClass->ClassGeneratedBy != SourceBlueprint);
if (bSignatureResult && OptionalOutput)
{
OptionalOutput->AddUnique(Signature);
}
const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
return bSignatureResult || bPropertyResult || bSuperResult;
}
void UK2Node_BaseMCDelegate::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "EventDelegate" ) ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), GetPropertyName().ToString() ));
}
void UK2Node_BaseMCDelegate::AutowireNewNode(UEdGraphPin* FromPin)
{
const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema());
// Since nodes no longer have a sense of scope when they're placed, look at the connection we're coming from, and use that to coerce the Target pin
if (FromPin && K2Schema)
{
bool bConnected = false;
// Only do the fixup if we're going coming from an output pin, which implies a contextual drag
if (FromPin->Direction == EGPD_Output)
{
if (FromPin->PinType.PinSubCategoryObject.IsValid() && FromPin->PinType.PinSubCategoryObject->IsA(UClass::StaticClass()))
{
FProperty* DelegateProperty = DelegateReference.ResolveMember<FMulticastDelegateProperty>(GetBlueprintClassFromNode());
if (DelegateProperty)
{
UClass* DelegateOwner = DelegateProperty->GetOwnerClass();
const UClass* PinSubCategoryObject = dynamic_cast<UClass*>(FromPin->PinType.PinSubCategoryObject.Get());
if (FromPin->PinType.PinSubCategoryObject == DelegateOwner || (PinSubCategoryObject && PinSubCategoryObject->IsChildOf(DelegateOwner)))
{
// If we get here, then the property delegate is also available on the FromPin's class.
// Fix up the type by propagating it from the FromPin to our target pin
UEdGraphPin* TargetPin = FindPin(UEdGraphSchema_K2::PN_Self);
TargetPin->PinType.PinSubCategory = NAME_None;
TargetPin->PinType.PinSubCategoryObject = DelegateOwner;
// And finally, try to establish the connection
if (K2Schema->TryCreateConnection(FromPin, TargetPin))
{
bConnected = true;
DelegateReference.SetFromField<FProperty>(DelegateProperty, false);
TargetPin->bHidden = false;
FromPin->GetOwningNode()->NodeConnectionListChanged();
this->NodeConnectionListChanged();
}
}
}
}
}
if (!bConnected)
{
Super::AutowireNewNode(FromPin);
}
}
}
bool UK2Node_BaseMCDelegate::HasDeprecatedReference() const
{
// Check if the referenced delegate is deprecated.
return DelegateReference.IsDeprecated();
}
FEdGraphNodeDeprecationResponse UK2Node_BaseMCDelegate::GetDeprecationResponse(EEdGraphNodeDeprecationType DeprecationType) const
{
FEdGraphNodeDeprecationResponse Response = Super::GetDeprecationResponse(DeprecationType);
if (DeprecationType == EEdGraphNodeDeprecationType::NodeHasDeprecatedReference)
{
if (FProperty* DelegateProperty = DelegateReference.ResolveMember<FProperty>(GetBlueprintClassFromNode()))
{
FString DetailedMessage = DelegateProperty->GetMetaData(FBlueprintMetadata::MD_DeprecationMessage);
Response.MessageText = FBlueprintEditorUtils::GetDeprecatedMemberUsageNodeWarning(GetPropertyDisplayName(), FText::FromString(DetailedMessage));
}
}
return Response;
}
/////// UK2Node_AddDelegate ///////////
UK2Node_AddDelegate::UK2Node_AddDelegate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UK2Node_AddDelegate::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
UEdGraphNode::FCreatePinParams PinParams;
PinParams.bIsReference = true;
PinParams.bIsConst = true;
if(UEdGraphPin* DelegatePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Delegate, FK2Node_BaseMCDelegateHelper::DelegatePinName, PinParams))
{
FMemberReference::FillSimpleMemberReference<UFunction>(GetDelegateSignature(), DelegatePin->PinType.PinSubCategoryMemberReference);
DelegatePin->PinFriendlyName = NSLOCTEXT("K2Node", "PinFriendlyDelegatetName", "Event");
}
}
FText UK2Node_AddDelegate::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyName"), GetPropertyDisplayName());
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "AddDelegate", "Bind Event to {PropertyName}"), Args), this);
}
return CachedNodeTitle;
}
FNodeHandlingFunctor* UK2Node_AddDelegate::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_AddRemoveDelegate(CompilerContext, KCST_AddMulticastDelegate);
}
void UK2Node_AddDelegate::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "AddDelegate" ) ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), GetPropertyName().ToString() ));
}
/////// UK2Node_ClearDelegate ///////////
UK2Node_ClearDelegate::UK2Node_ClearDelegate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FText UK2Node_ClearDelegate::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyName"), GetPropertyDisplayName());
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "ClearDelegate", "Unbind all Events from {PropertyName}"), Args), this);
}
return CachedNodeTitle;
}
FNodeHandlingFunctor* UK2Node_ClearDelegate::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_ClearDelegate(CompilerContext);
}
/////// UK2Node_RemoveDelegate ///////////
UK2Node_RemoveDelegate::UK2Node_RemoveDelegate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UK2Node_RemoveDelegate::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
UEdGraphNode::FCreatePinParams PinParams;
PinParams.bIsReference = true;
PinParams.bIsConst = true;
if(UEdGraphPin* DelegatePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Delegate, FK2Node_BaseMCDelegateHelper::DelegatePinName, PinParams))
{
FMemberReference::FillSimpleMemberReference<UFunction>(GetDelegateSignature(), DelegatePin->PinType.PinSubCategoryMemberReference);
DelegatePin->PinFriendlyName = NSLOCTEXT("K2Node", "PinFriendlyDelegatetName", "Event");
}
}
FText UK2Node_RemoveDelegate::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyName"), GetPropertyDisplayName());
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "RemoveDelegate", "Unbind Event from {PropertyName}"), Args), this);
}
return CachedNodeTitle;
}
FNodeHandlingFunctor* UK2Node_RemoveDelegate::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_AddRemoveDelegate(CompilerContext, KCST_RemoveMulticastDelegate);
}
/////// UK2Node_CallDelegate ///////////
UK2Node_CallDelegate::UK2Node_CallDelegate(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
bool UK2Node_CallDelegate::CreatePinsForFunctionInputs(const UFunction* Function)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// Create the inputs and outputs
bool bAllPinsGood = true;
for (TFieldIterator<FProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
FProperty* Param = *PropIt;
const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm);
if (bIsFunctionInput)
{
UEdGraphPin* Pin = CreatePin(EGPD_Input, NAME_None, Param->GetFName());
const bool bPinGood = K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType);
bAllPinsGood = bAllPinsGood && bPinGood;
}
}
return bAllPinsGood;
}
void UK2Node_CallDelegate::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
CreatePinsForFunctionInputs(GetDelegateSignature());
}
FText UK2Node_CallDelegate::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (CachedNodeTitle.IsOutOfDate(this))
{
FFormatNamedArguments Args;
Args.Add(TEXT("PropertyName"), GetPropertyDisplayName());
// FText::Format() is slow, so we cache this to save on performance
CachedNodeTitle.SetCachedText(FText::Format(NSLOCTEXT("K2Node", "CallDelegate", "Call {PropertyName}"), Args), this);
}
return CachedNodeTitle;
}
void UK2Node_CallDelegate::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
UK2Node::ValidateNodeDuringCompilation(MessageLog);
if(FProperty* Property = GetProperty())
{
if(!Property->HasAllPropertyFlags(CPF_BlueprintCallable))
{
MessageLog.Error(*NSLOCTEXT("K2Node", "BaseMCDelegateNotCallable", "Event Dispatcher is not 'BlueprintCallable' @@").ToString(), this);
}
}
}
FNodeHandlingFunctor* UK2Node_CallDelegate::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_CallDelegate(CompilerContext);
}
FName UK2Node_CallDelegate::GetCornerIcon() const
{
return TEXT("Graph.Message.MessageIcon");
}