Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/BlueprintBoundEventNodeSpawner.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

185 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BlueprintBoundEventNodeSpawner.h"
#include "GameFramework/Actor.h"
#include "K2Node_ActorBoundEvent.h"
#include "K2Node_ComponentBoundEvent.h"
#include "Settings/EditorStyleSettings.h"
#include "Editor/EditorEngine.h"
#include "ObjectEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "EditorCategoryUtils.h"
#include "BlueprintNodeSpawnerUtils.h"
#define LOCTEXT_NAMESPACE "BlueprintBoundEventNodeSpawner"
/*******************************************************************************
* Static UBlueprintBoundEventNodeSpawner Helpers
******************************************************************************/
namespace BlueprintBoundEventNodeSpawnerImpl
{
static FText GetDefaultMenuName(FMulticastDelegateProperty const* Delegate);
static FText GetDefaultMenuCategory(FMulticastDelegateProperty const* Delegate);
}
//------------------------------------------------------------------------------
static FText BlueprintBoundEventNodeSpawnerImpl::GetDefaultMenuName(FMulticastDelegateProperty const* Delegate)
{
bool const bShowFriendlyNames = GetDefault<UEditorStyleSettings>()->bShowFriendlyNames;
FText const DelegateName = bShowFriendlyNames ? FText::FromString(UEditorEngine::GetFriendlyName(Delegate)) : FText::FromName(Delegate->GetFName());
return FText::Format(LOCTEXT("ComponentEventName", "Add {0}"), DelegateName);
}
//------------------------------------------------------------------------------
static FText BlueprintBoundEventNodeSpawnerImpl::GetDefaultMenuCategory(FMulticastDelegateProperty const* Delegate)
{
FText DelegateCategory = FText::FromString(FObjectEditorUtils::GetCategory(Delegate));
if (DelegateCategory.IsEmpty())
{
DelegateCategory = FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Delegates);
}
return DelegateCategory;
}
/*******************************************************************************
* UBlueprintBoundEventNodeSpawner
******************************************************************************/
//------------------------------------------------------------------------------
UBlueprintBoundEventNodeSpawner* UBlueprintBoundEventNodeSpawner::Create(TSubclassOf<UK2Node_Event> NodeClass, FMulticastDelegateProperty* EventDelegate, UObject* Outer/* = nullptr*/)
{
if (Outer == nullptr)
{
Outer = GetTransientPackage();
}
UBlueprintBoundEventNodeSpawner* NodeSpawner = NewObject<UBlueprintBoundEventNodeSpawner>(Outer);
NodeSpawner->NodeClass = NodeClass;
NodeSpawner->EventDelegate = EventDelegate;
FBlueprintActionUiSpec& MenuSignature = NodeSpawner->DefaultMenuSignature;
MenuSignature.MenuName = BlueprintBoundEventNodeSpawnerImpl::GetDefaultMenuName(EventDelegate);
MenuSignature.Category = BlueprintBoundEventNodeSpawnerImpl::GetDefaultMenuCategory(EventDelegate);
//MenuSignature.Tooltip, will be pulled from the node template
//MenuSignature.Keywords, will be pulled from the node template
MenuSignature.Icon = FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.Event_16x");
return NodeSpawner;
}
//------------------------------------------------------------------------------
UBlueprintBoundEventNodeSpawner::UBlueprintBoundEventNodeSpawner(FObjectInitializer const& ObjectInitializer)
: Super(ObjectInitializer)
, EventDelegate(nullptr)
{
}
//------------------------------------------------------------------------------
FBlueprintNodeSignature UBlueprintBoundEventNodeSpawner::GetSpawnerSignature() const
{
// explicit actions for binding (like this) cannot be reconstructed form a
// signature (since this spawner does not own whatever it will be binding
// to), therefore we return an empty (invalid) signature
return FBlueprintNodeSignature();
}
//------------------------------------------------------------------------------
UEdGraphNode* UBlueprintBoundEventNodeSpawner::Invoke(UEdGraph* ParentGraph, FBindingSet const& Bindings, FVector2D const Location) const
{
UK2Node_Event* EventNode = nullptr;
if (Bindings.Num() > 0)
{
EventNode = CastChecked<UK2Node_Event>(Super::Invoke(ParentGraph, Bindings, Location));
}
return EventNode;
}
//------------------------------------------------------------------------------
UK2Node_Event const* UBlueprintBoundEventNodeSpawner::FindPreExistingEvent(UBlueprint* Blueprint, FBindingSet const& Bindings) const
{
UK2Node_Event const* PreExistingEvent = nullptr;
FBindingObject BoundObject;
if (Bindings.Num() > 0)
{
BoundObject = *Bindings.CreateConstIterator();
}
if (BoundObject != nullptr)
{
if (NodeClass->IsChildOf<UK2Node_ComponentBoundEvent>())
{
PreExistingEvent = FKismetEditorUtilities::FindBoundEventForComponent(Blueprint, EventDelegate->GetFName(), BoundObject.GetFName());
}
else if (NodeClass->IsChildOf<UK2Node_ActorBoundEvent>())
{
PreExistingEvent = FKismetEditorUtilities::FindBoundEventForActor(CastChecked<AActor>(BoundObject.Get<UObject>()), EventDelegate->GetFName());
}
}
return PreExistingEvent;
}
//------------------------------------------------------------------------------
bool UBlueprintBoundEventNodeSpawner::IsBindingCompatible(FBindingObject BindingCandidate) const
{
bool bMatchesNodeType = false;
if (NodeClass->IsChildOf<UK2Node_ComponentBoundEvent>())
{
FObjectProperty const* BindingProperty = BindingCandidate.Get<FObjectProperty>();
bMatchesNodeType = (BindingProperty != nullptr);
}
else if (NodeClass->IsChildOf<UK2Node_ActorBoundEvent>())
{
bMatchesNodeType = BindingCandidate.IsA<AActor>();
}
const FMulticastDelegateProperty* Delegate = GetEventDelegate();
if ( !ensureMsgf(!FBlueprintNodeSpawnerUtils::IsStaleFieldAction(this),
TEXT("Invalid BlueprintBoundEventNodeSpawner (for %s). Was the action database properly updated when this class was compiled?"),
*Delegate->GetOwnerClass()->GetName()))
{
return false;
}
UClass* DelegateOwner = Delegate->GetOwnerClass()->GetAuthoritativeClass();
UClass* BindingClass = FBlueprintNodeSpawnerUtils::GetBindingClass(BindingCandidate)->GetAuthoritativeClass();
return bMatchesNodeType && BindingClass && BindingClass->IsChildOf(DelegateOwner) && !FObjectEditorUtils::IsVariableCategoryHiddenFromClass(Delegate, BindingClass);
}
//------------------------------------------------------------------------------
bool UBlueprintBoundEventNodeSpawner::BindToNode(UEdGraphNode* Node, FBindingObject Binding) const
{
bool bWasBound = false;
if (UK2Node_ComponentBoundEvent* ComponentEventNode = Cast<UK2Node_ComponentBoundEvent>(Node))
{
if (FObjectProperty const* BoundProperty = Binding.Get<FObjectProperty>())
{
ComponentEventNode->InitializeComponentBoundEventParams(BoundProperty, EventDelegate.Get());
bWasBound = true;
Node->ReconstructNode();
}
}
else if (UK2Node_ActorBoundEvent* ActorEventNode = CastChecked<UK2Node_ActorBoundEvent>(Node))
{
if (AActor* BoundActor = Binding.Get<AActor>())
{
ActorEventNode->InitializeActorBoundEventParams(BoundActor, EventDelegate.Get());
bWasBound = true;
Node->ReconstructNode();
}
}
return bWasBound;
}
//------------------------------------------------------------------------------
FMulticastDelegateProperty const* UBlueprintBoundEventNodeSpawner::GetEventDelegate() const
{
return EventDelegate.Get();
}
#undef LOCTEXT_NAMESPACE