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