382 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "CSAssembly.h"
 | 
						|
#include "UnrealSharpCore.h"
 | 
						|
#include "Misc/Paths.h"
 | 
						|
#include "CSManager.h"
 | 
						|
#include "CSUnrealSharpSettings.h"
 | 
						|
#include "Logging/StructuredLog.h"
 | 
						|
#include "TypeGenerator/CSClass.h"
 | 
						|
#include "TypeGenerator/CSEnum.h"
 | 
						|
#include "TypeGenerator/CSInterface.h"
 | 
						|
#include "TypeGenerator/CSScriptStruct.h"
 | 
						|
#include "TypeGenerator/Register/MetaData/CSClassMetaData.h"
 | 
						|
#include "TypeGenerator/Register/MetaData/CSDelegateMetaData.h"
 | 
						|
#include "TypeGenerator/Register/MetaData/CSEnumMetaData.h"
 | 
						|
#include "TypeGenerator/Register/MetaData/CSInterfaceMetaData.h"
 | 
						|
#include "TypeGenerator/Register/MetaData/CSStructMetaData.h"
 | 
						|
#include "TypeGenerator/Register/TypeInfo/CSClassInfo.h"
 | 
						|
#include "Utils/CSClassUtilities.h"
 | 
						|
 | 
						|
void UCSAssembly::SetAssemblyPath(const FStringView InAssemblyPath)
 | 
						|
{
 | 
						|
	if (!AssemblyPath.IsEmpty())
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	AssemblyPath = FPaths::ConvertRelativePathToFull(InAssemblyPath.GetData());
 | 
						|
 | 
						|
#if defined(_WIN32)
 | 
						|
	// Replace forward slashes with backslashes
 | 
						|
	AssemblyPath.ReplaceInline(TEXT("/"), TEXT("\\"));
 | 
						|
#endif
 | 
						|
 | 
						|
	AssemblyName = *FPaths::GetBaseFilename(AssemblyPath);
 | 
						|
}
 | 
						|
 | 
						|
bool UCSAssembly::LoadAssembly(bool bisCollectible)
 | 
						|
{
 | 
						|
	TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString(TEXT("UCSAssembly::LoadAssembly: " + AssemblyName.ToString())));
 | 
						|
 | 
						|
	if (IsValidAssembly())
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Display, TEXT("%s is already loaded"), *AssemblyPath);
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!FPaths::FileExists(AssemblyPath))
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Display, TEXT("%s doesn't exist"), *AssemblyPath);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	bIsLoading = true;
 | 
						|
	FGCHandle NewHandle = UCSManager::Get().GetManagedPluginsCallbacks().LoadPlugin(*AssemblyPath, bisCollectible);
 | 
						|
	NewHandle.Type = GCHandleType::WeakHandle;
 | 
						|
 | 
						|
	if (NewHandle.IsNull())
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to load: %s"), *AssemblyPath);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	
 | 
						|
	ManagedAssemblyHandle = MakeShared<FGCHandle>(NewHandle);
 | 
						|
	FModuleManager::Get().OnModulesChanged().AddUObject(this, &UCSAssembly::OnModulesChanged);
 | 
						|
 | 
						|
	if (ProcessTypeMetadata())
 | 
						|
	{
 | 
						|
		for (const TPair<FCSFieldName, TSharedPtr<FCSManagedTypeInfo>>& NameToTypeInfo : AllTypes)
 | 
						|
		{
 | 
						|
			NameToTypeInfo.Value->StartBuildingManagedType();
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	bIsLoading = false;
 | 
						|
	UCSManager::Get().OnManagedAssemblyLoadedEvent().Broadcast(AssemblyName);
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
template <typename T, typename MetaDataType>
 | 
						|
void RegisterMetaData(UCSAssembly* OwningAssembly, const TSharedPtr<FJsonValue>& MetaData,
 | 
						|
	TMap<FCSFieldName,
 | 
						|
	TSharedPtr<FCSManagedTypeInfo>>& Map,
 | 
						|
	UClass* FieldType,
 | 
						|
	TFunction<void(TSharedPtr<FCSManagedTypeInfo>)> OnRebuild = nullptr)
 | 
						|
{
 | 
						|
	const TSharedPtr<FJsonObject>& MetaDataObject = MetaData->AsObject();
 | 
						|
 | 
						|
	const FString Name= MetaDataObject->GetStringField(TEXT("Name"));
 | 
						|
	const FString Namespace = MetaDataObject->GetStringField(TEXT("Namespace"));
 | 
						|
	const FCSFieldName FullName(*Name, *Namespace);
 | 
						|
 | 
						|
	TSharedPtr<FCSManagedTypeInfo> ExistingValue = Map.FindRef(FullName);
 | 
						|
 | 
						|
	if (ExistingValue.IsValid())
 | 
						|
	{
 | 
						|
		// Parse fresh metadata and update the existing info
 | 
						|
		MetaDataType NewMeta;
 | 
						|
		NewMeta.SerializeFromJson(MetaDataObject);
 | 
						|
 | 
						|
		if (ExistingValue->GetStructureState() == HasChangedStructure || NewMeta != *ExistingValue->GetTypeMetaData<MetaDataType>())
 | 
						|
		{
 | 
						|
			TSharedPtr<MetaDataType> MetaDataPtr = MakeShared<MetaDataType>(NewMeta);
 | 
						|
			ExistingValue->SetTypeMetaData(MetaDataPtr);
 | 
						|
			ExistingValue->SetStructureState(HasChangedStructure);
 | 
						|
			
 | 
						|
			if (OnRebuild)
 | 
						|
			{
 | 
						|
				OnRebuild(ExistingValue);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		TSharedPtr<MetaDataType> ParsedMeta = MakeShared<MetaDataType>();
 | 
						|
		ParsedMeta->SerializeFromJson(MetaDataObject);
 | 
						|
		
 | 
						|
		TSharedPtr<T> NewValue = MakeShared<T>(ParsedMeta, OwningAssembly, FieldType);
 | 
						|
		Map.Add(FullName, NewValue);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool UCSAssembly::ProcessTypeMetadata()
 | 
						|
{
 | 
						|
	TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::ProcessTypeMetadata);
 | 
						|
 | 
						|
	const FString MetadataPath = FPaths::ChangeExtension(AssemblyPath, "metadata.json");
 | 
						|
	if (!FPaths::FileExists(MetadataPath))
 | 
						|
	{
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	FString JsonString;
 | 
						|
	if (!FFileHelper::LoadFileToString(JsonString, *MetadataPath))
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to load MetaDataPath at: %s"), *MetadataPath);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	TSharedPtr<FJsonObject> JsonObject;
 | 
						|
	if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(JsonString), JsonObject) || !JsonObject.IsValid())
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Fatal, TEXT("Failed to parse JSON at: %s"), *MetadataPath);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	
 | 
						|
	UCSManager& Manager = UCSManager::Get();
 | 
						|
 | 
						|
	const TArray<TSharedPtr<FJsonValue>>& StructMetaData = JsonObject->GetArrayField(TEXT("StructMetaData"));
 | 
						|
	for (const TSharedPtr<FJsonValue>& MetaData : StructMetaData)
 | 
						|
	{
 | 
						|
		RegisterMetaData<FCSManagedTypeInfo, FCSStructMetaData>(this, MetaData, AllTypes, UCSScriptStruct::StaticClass());
 | 
						|
	}
 | 
						|
 | 
						|
	const TArray<TSharedPtr<FJsonValue>>& EnumMetaData = JsonObject->GetArrayField(TEXT("EnumMetaData"));
 | 
						|
	for (const TSharedPtr<FJsonValue>& MetaData : EnumMetaData)
 | 
						|
	{
 | 
						|
		RegisterMetaData<FCSManagedTypeInfo, FCSEnumMetaData>(this, MetaData, AllTypes, UCSEnum::StaticClass());
 | 
						|
	}
 | 
						|
 | 
						|
	const TArray<TSharedPtr<FJsonValue>>& InterfacesMetaData = JsonObject->GetArrayField(TEXT("InterfacesMetaData"));
 | 
						|
	for (const TSharedPtr<FJsonValue>& MetaData : InterfacesMetaData)
 | 
						|
	{
 | 
						|
		RegisterMetaData<FCSManagedTypeInfo, FCSInterfaceMetaData>(this, MetaData, AllTypes, UCSInterface::StaticClass());
 | 
						|
	}
 | 
						|
 | 
						|
	const TArray<TSharedPtr<FJsonValue>>& DelegatesMetaData = JsonObject->GetArrayField(TEXT("DelegateMetaData"));
 | 
						|
	for (const TSharedPtr<FJsonValue>& MetaData : DelegatesMetaData)
 | 
						|
	{
 | 
						|
		RegisterMetaData<FCSManagedTypeInfo, FCSDelegateMetaData>(this, MetaData, AllTypes, UDelegateFunction::StaticClass());
 | 
						|
	}
 | 
						|
 | 
						|
	const TArray<TSharedPtr<FJsonValue>>& ClassesMetaData = JsonObject->GetArrayField(TEXT("ClassMetaData"));
 | 
						|
	for (const TSharedPtr<FJsonValue>& MetaData : ClassesMetaData)
 | 
						|
	{
 | 
						|
		RegisterMetaData<FCSClassInfo, FCSClassMetaData>(this, MetaData, AllTypes, UCSClass::StaticClass(),
 | 
						|
         [&Manager](const TSharedPtr<FCSManagedTypeInfo>& ClassInfo)
 | 
						|
         {
 | 
						|
             // Structure has been changed. We must trigger full reload on all managed classes that derive from this class.
 | 
						|
             TArray<UClass*> DerivedClasses;
 | 
						|
             GetDerivedClasses(ClassInfo->GetFieldChecked<UClass>(), DerivedClasses);
 | 
						|
 | 
						|
             for (UClass* DerivedClass : DerivedClasses)
 | 
						|
             {
 | 
						|
                 if (!Manager.IsManagedType(DerivedClass))
 | 
						|
                 {
 | 
						|
                     continue;
 | 
						|
                 }
 | 
						|
 | 
						|
                 UCSClass* ManagedClass = static_cast<UCSClass*>(DerivedClass);
 | 
						|
                 TSharedPtr<FCSClassInfo> ChildClassInfo = ManagedClass->GetManagedTypeInfo<FCSClassInfo>();
 | 
						|
                 ChildClassInfo->SetStructureState(HasChangedStructure);
 | 
						|
             }
 | 
						|
         });
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
bool UCSAssembly::UnloadAssembly()
 | 
						|
{
 | 
						|
	if (!IsValidAssembly())
 | 
						|
	{
 | 
						|
		// Assembly is already unloaded.
 | 
						|
		UE_LOGFMT(LogUnrealSharp, Display, "{0} is already unloaded", *AssemblyName.ToString());
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	TRACE_CPUPROFILER_EVENT_SCOPE_TEXT(*FString(TEXT("UCSAssembly::UnloadAssembly: " + AssemblyName.ToString())));
 | 
						|
 | 
						|
	FGCHandleIntPtr AssemblyHandle = ManagedAssemblyHandle->GetHandle();
 | 
						|
	for (TSharedPtr<FGCHandle>& Handle : AllocatedManagedHandles)
 | 
						|
	{
 | 
						|
		Handle->Dispose(AssemblyHandle);
 | 
						|
		Handle.Reset();
 | 
						|
	}
 | 
						|
 | 
						|
	ManagedClassHandles.Reset();
 | 
						|
	AllocatedManagedHandles.Reset();
 | 
						|
 | 
						|
	// Don't need the assembly handle anymore, we use the path to unload the assembly.
 | 
						|
	ManagedAssemblyHandle->Dispose(ManagedAssemblyHandle->GetHandle());
 | 
						|
	ManagedAssemblyHandle.Reset();
 | 
						|
 | 
						|
    UCSManager::Get().OnManagedAssemblyUnloadedEvent().Broadcast(AssemblyName);
 | 
						|
	return UCSManager::Get().GetManagedPluginsCallbacks().UnloadPlugin(*AssemblyPath);
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<FGCHandle> UCSAssembly::TryFindTypeHandle(const FCSFieldName& FieldName)
 | 
						|
{
 | 
						|
	if (!IsValidAssembly())
 | 
						|
	{
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	if (TSharedPtr<FGCHandle>* Handle = ManagedClassHandles.Find(FieldName))
 | 
						|
	{
 | 
						|
		return *Handle;
 | 
						|
	}
 | 
						|
 | 
						|
	FString FullName = FieldName.GetFullName().ToString();
 | 
						|
	uint8* TypeHandle = FCSManagedCallbacks::ManagedCallbacks.LookupManagedType(ManagedAssemblyHandle->GetPointer(), *FullName);
 | 
						|
 | 
						|
	if (!TypeHandle)
 | 
						|
	{
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	TSharedPtr<FGCHandle> AllocatedHandle = MakeShared<FGCHandle>(TypeHandle, GCHandleType::WeakHandle);
 | 
						|
	AllocatedManagedHandles.Add(AllocatedHandle);
 | 
						|
	ManagedClassHandles.Add(FieldName, AllocatedHandle);
 | 
						|
	return AllocatedHandle;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<FGCHandle> UCSAssembly::GetManagedMethod(const TSharedPtr<FGCHandle>& TypeHandle, const FString& MethodName)
 | 
						|
{
 | 
						|
	if (!TypeHandle.IsValid())
 | 
						|
	{
 | 
						|
		UE_LOGFMT(LogUnrealSharp, Error, "Type handle is invalid for method %s", *MethodName);
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	uint8* MethodHandle = FCSManagedCallbacks::ManagedCallbacks.LookupManagedMethod(TypeHandle->GetPointer(), *MethodName);
 | 
						|
 | 
						|
	if (MethodHandle == nullptr)
 | 
						|
	{
 | 
						|
		UE_LOG(LogUnrealSharp, Error, TEXT("Failed to find managed method for %s"), *MethodName);
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	TSharedPtr<FGCHandle> AllocatedHandle = MakeShared<FGCHandle>(MethodHandle, GCHandleType::WeakHandle);
 | 
						|
	AllocatedManagedHandles.Add(AllocatedHandle);
 | 
						|
	return AllocatedHandle;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<FGCHandle> UCSAssembly::CreateManagedObject(const UObject* Object)
 | 
						|
{
 | 
						|
	TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::CreateManagedObject);
 | 
						|
	
 | 
						|
	// Only managed/native classes have a C# counterpart.
 | 
						|
	UClass* Class = FCSClassUtilities::GetFirstNonBlueprintClass(Object->GetClass());
 | 
						|
	TSharedPtr<FCSManagedTypeInfo> TypeInfo = FindOrAddTypeInfo(Class);
 | 
						|
	TSharedPtr<FGCHandle> TypeHandle = TypeInfo->GetManagedTypeHandle();
 | 
						|
 | 
						|
	TCHAR* Error = nullptr;
 | 
						|
	FGCHandle NewManagedObject = FCSManagedCallbacks::ManagedCallbacks.CreateNewManagedObject(Object, TypeHandle->GetPointer(), &Error);
 | 
						|
	NewManagedObject.Type = GCHandleType::StrongHandle;
 | 
						|
 | 
						|
	if (NewManagedObject.IsNull())
 | 
						|
	{
 | 
						|
		// This should never happen. Potential issues: IL errors, typehandle is invalid.
 | 
						|
		UE_LOGFMT(LogUnrealSharp, Fatal, "Failed to create managed counterpart for {0}:\n{1}", *Object->GetName(), Error);
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	TSharedPtr<FGCHandle> Handle = MakeShared<FGCHandle>(NewManagedObject);
 | 
						|
	AllocatedManagedHandles.Add(Handle);
 | 
						|
 | 
						|
	uint32 ObjectID = Object->GetUniqueID();
 | 
						|
	UCSManager::Get().ManagedObjectHandles.AddByHash(ObjectID, ObjectID, Handle);
 | 
						|
 | 
						|
	return Handle;
 | 
						|
}
 | 
						|
 | 
						|
TSharedPtr<FGCHandle> UCSAssembly::FindOrCreateManagedInterfaceWrapper(UObject* Object, UClass* InterfaceClass)
 | 
						|
{
 | 
						|
	TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::FindOrCreateManagedInterfaceWrapper);
 | 
						|
 | 
						|
	UClass* NonBlueprintClass = FCSClassUtilities::GetFirstNonBlueprintClass(InterfaceClass);
 | 
						|
	TSharedPtr<FCSManagedTypeInfo> ClassInfo = FindOrAddTypeInfo(NonBlueprintClass);
 | 
						|
	TSharedPtr<FGCHandle> TypeHandle = ClassInfo->GetManagedTypeHandle();
 | 
						|
	
 | 
						|
	uint32 ObjectID = Object->GetUniqueID();
 | 
						|
    TMap<uint32, TSharedPtr<FGCHandle>>& TypeMap = UCSManager::Get().ManagedInterfaceWrappers.FindOrAddByHash(ObjectID, ObjectID);
 | 
						|
	
 | 
						|
	uint32 TypeId = InterfaceClass->GetUniqueID();
 | 
						|
	if (TSharedPtr<FGCHandle>* Existing = TypeMap.FindByHash(TypeId, TypeId))
 | 
						|
	{
 | 
						|
		return *Existing;
 | 
						|
	}
 | 
						|
 | 
						|
    TSharedPtr<FGCHandle>* ObjectHandle = UCSManager::Get().ManagedObjectHandles.FindByHash(ObjectID, ObjectID);
 | 
						|
	if (ObjectHandle == nullptr)
 | 
						|
	{
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
    
 | 
						|
	FGCHandle NewManagedObjectWrapper = FCSManagedCallbacks::ManagedCallbacks.CreateNewManagedObjectWrapper((*ObjectHandle)->GetPointer(), TypeHandle->GetPointer());
 | 
						|
	NewManagedObjectWrapper.Type = GCHandleType::StrongHandle;
 | 
						|
 | 
						|
	if (NewManagedObjectWrapper.IsNull())
 | 
						|
	{
 | 
						|
		// This should never happen. Potential issues: IL errors, typehandle is invalid.
 | 
						|
		UE_LOGFMT(LogUnrealSharp, Fatal, "Failed to create managed counterpart for {0}", *Object->GetName());
 | 
						|
		return nullptr;
 | 
						|
	}
 | 
						|
 | 
						|
	TSharedPtr<FGCHandle> Handle = MakeShared<FGCHandle>(NewManagedObjectWrapper);
 | 
						|
	AllocatedManagedHandles.Add(Handle);
 | 
						|
	
 | 
						|
	TypeMap.AddByHash(TypeId, TypeId, Handle);
 | 
						|
	return Handle;
 | 
						|
}
 | 
						|
 | 
						|
void UCSAssembly::AddPendingClass(const FCSTypeReferenceMetaData& ParentClass, FCSClassInfo* NewClass)
 | 
						|
{
 | 
						|
	TSet<FCSClassInfo*>& PendingClass = PendingClasses.FindOrAdd(ParentClass);
 | 
						|
	PendingClass.Add(NewClass);
 | 
						|
}
 | 
						|
 | 
						|
void UCSAssembly::OnModulesChanged(FName InModuleName, EModuleChangeReason InModuleChangeReason)
 | 
						|
{
 | 
						|
	if (InModuleChangeReason != EModuleChangeReason::ModuleLoaded)
 | 
						|
	{
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	int32 NumPendingClasses = PendingClasses.Num();
 | 
						|
	for (auto Itr = PendingClasses.CreateIterator(); Itr; ++Itr)
 | 
						|
	{
 | 
						|
		UClass* Class = Itr.Key().GetOwningClass();
 | 
						|
 | 
						|
		if (!Class)
 | 
						|
		{
 | 
						|
			// Class still not loaded from this module.
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		for (FCSClassInfo* PendingClass : Itr.Value())
 | 
						|
		{
 | 
						|
			PendingClass->StartBuildingManagedType();
 | 
						|
		}
 | 
						|
 | 
						|
		Itr.RemoveCurrent();
 | 
						|
	}
 | 
						|
 | 
						|
#if WITH_EDITOR
 | 
						|
	if (NumPendingClasses != PendingClasses.Num())
 | 
						|
	{
 | 
						|
		UCSManager::Get().OnProcessedPendingClassesEvent().Broadcast();
 | 
						|
	}
 | 
						|
#endif
 | 
						|
}
 |