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
 | |
| }
 |