EM_Task/CoreUObject/Private/UObject/PropertyObject.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

184 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "UObject/SoftObjectPtr.h"
#include "UObject/UnrealType.h"
#include "Blueprint/BlueprintSupport.h"
#include "UObject/LinkerPlaceholderBase.h"
#include "UObject/LinkerPlaceholderExportObject.h"
#include "UObject/LinkerPlaceholderClass.h"
/*-----------------------------------------------------------------------------
FObjectProperty.
-----------------------------------------------------------------------------*/
IMPLEMENT_FIELD(FObjectProperty)
FString FObjectProperty::GetCPPTypeCustom(FString* ExtendedTypeText, uint32 CPPExportFlags, const FString& InnerNativeTypeName) const
{
ensure(!InnerNativeTypeName.IsEmpty());
return FString::Printf(TEXT("%s*"), *InnerNativeTypeName);
}
FString FObjectProperty::GetCPPTypeForwardDeclaration() const
{
return FString::Printf(TEXT("class %s%s;"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName());
}
FString FObjectProperty::GetCPPMacroType(FString& ExtendedTypeText) const
{
ExtendedTypeText = FString::Printf(TEXT("%s%s"), PropertyClass->GetPrefixCPP(), *PropertyClass->GetName());
return TEXT("OBJECT");
}
EConvertFromTypeResult FObjectProperty::ConvertFromType(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot, uint8* Data, UStruct* DefaultsStruct)
{
static FName NAME_AssetObjectProperty = "AssetObjectProperty"; // old name of soft object property
if (Tag.Type == NAME_SoftObjectProperty || Tag.Type == NAME_AssetObjectProperty)
{
// This property used to be a TSoftObjectPtr<Foo> but is now a raw FObjectProperty Foo*, we can convert without loss of data
FSoftObjectPtr PreviousValue;
Slot << PreviousValue;
UObject* PreviousValueObj = nullptr;
// If we're async loading it's not safe to do a sync load because it may crash or fail to set the variable, so throw an error if it's not already in memory
if (IsInAsyncLoadingThread())
{
PreviousValueObj = PreviousValue.Get();
if (!PreviousValueObj && !PreviousValue.IsNull())
{
UE_LOG(LogClass, Error, TEXT("Failed to convert soft path %s to unloaded object as this is not safe during async loading. Load and resave %s in the editor to fix!"), *PreviousValue.ToString(), *Slot.GetUnderlyingArchive().GetArchiveName());
}
}
else
{
PreviousValueObj = PreviousValue.LoadSynchronous();
}
// Now copy the value into the object's address space
SetPropertyValue_InContainer(Data, PreviousValueObj, Tag.ArrayIndex);
// Validate the type is proper
CheckValidObject(GetPropertyValuePtr_InContainer(Data, Tag.ArrayIndex));
return EConvertFromTypeResult::Converted;
}
else if (Tag.Type == NAME_InterfaceProperty)
{
UObject* ObjectValue;
Slot << ObjectValue;
if (ObjectValue && !ObjectValue->IsA(PropertyClass))
{
UE_LOG(LogClass, Warning, TEXT("Failed to convert interface property %s of %s from Interface to %s"), *this->GetName(), *Slot.GetUnderlyingArchive().GetArchiveName(), *PropertyClass->GetName());
return EConvertFromTypeResult::CannotConvert;
}
SetPropertyValue_InContainer(Data, ObjectValue, Tag.ArrayIndex);
CheckValidObject(GetPropertyValuePtr_InContainer(Data, Tag.ArrayIndex));
return EConvertFromTypeResult::Converted;
}
return EConvertFromTypeResult::UseSerializeItem;
}
void FObjectProperty::SerializeItem(FStructuredArchive::FSlot Slot, void* Value, void const* Defaults) const
{
FArchive& UnderlyingArchive = Slot.GetUnderlyingArchive();
if (UnderlyingArchive.IsObjectReferenceCollector())
{
// Serialize in place
UObject** ObjectPtr = GetPropertyValuePtr(Value);
Slot << (*ObjectPtr);
if (!UnderlyingArchive.IsSaving())
{
CheckValidObject(ObjectPtr);
}
}
else
{
UObject* ObjectValue = GetObjectPropertyValue(Value);
Slot << ObjectValue;
UObject* CurrentValue = GetObjectPropertyValue(Value);
if (ObjectValue != CurrentValue)
{
SetObjectPropertyValue(Value, ObjectValue);
#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
if (ULinkerPlaceholderExportObject* PlaceholderVal = Cast<ULinkerPlaceholderExportObject>(ObjectValue))
{
PlaceholderVal->AddReferencingPropertyValue(this, Value);
}
else if (ULinkerPlaceholderClass* PlaceholderClass = Cast<ULinkerPlaceholderClass>(ObjectValue))
{
PlaceholderClass->AddReferencingPropertyValue(this, Value);
}
// NOTE: we don't remove this from CurrentValue if it is a
// ULinkerPlaceholderExportObject; this is because this property
// could be an array inner, and another member of that array (also
// referenced through this property)... if this becomes a problem,
// then we could inc/decrement a ref count per referencing property
//
// @TODO: if this becomes problematic (because ObjectValue doesn't match
// this property's PropertyClass), then we could spawn another
// placeholder object (of PropertyClass's type), or use null; but
// we'd have to modify ULinkerPlaceholderExportObject::ReplaceReferencingObjectValues()
// to accommodate this (as it depends on finding itself as the set value)
#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
CheckValidObject(Value);
}
}
}
const TCHAR* FObjectProperty::ImportText_Internal(const TCHAR* Buffer, void* Data, int32 PortFlags, UObject* OwnerObject, FOutputDevice* ErrorText) const
{
const TCHAR* Result = TFObjectPropertyBase<UObject*>::ImportText_Internal(Buffer, Data, PortFlags, OwnerObject, ErrorText);
if (Result)
{
CheckValidObject(Data);
#if USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
UObject* ObjectValue = GetObjectPropertyValue(Data);
if (ULinkerPlaceholderClass* PlaceholderClass = Cast<ULinkerPlaceholderClass>(ObjectValue))
{
// we use this tracker mechanism to help record the instance that is
// referencing the placeholder (so we can replace it later on fixup)
FScopedPlaceholderContainerTracker ImportingObjTracker(OwnerObject);
PlaceholderClass->AddReferencingPropertyValue(this, Data);
}
#if USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
else
{
// as far as we know, ULinkerPlaceholderClass is the only type we have to handle through ImportText()
check(!FBlueprintSupport::IsDeferredDependencyPlaceholder(ObjectValue));
}
#endif // USE_DEFERRED_DEPENDENCY_CHECK_VERIFICATION_TESTS
#endif // USE_CIRCULAR_DEPENDENCY_LOAD_DEFERRING
}
return Result;
}
uint32 FObjectProperty::GetValueTypeHashInternal(const void* Src) const
{
return GetTypeHash(GetPropertyValue(Src));
}
UObject* FObjectProperty::GetObjectPropertyValue(const void* PropertyValueAddress) const
{
return GetPropertyValue(PropertyValueAddress);
}
void FObjectProperty::SetObjectPropertyValue(void* PropertyValueAddress, UObject* Value) const
{
SetPropertyValue(PropertyValueAddress, Value);
}