#pragma once #include "CSManagedGCHandle.h" #include "UnrealSharpCore.h" #include "Logging/StructuredLog.h" #include "TypeGenerator/Register/MetaData/CSTypeReferenceMetaData.h" #include "Utils/CSClassUtilities.h" #include "CSAssembly.generated.h" #if !defined(_WIN32) #define __stdcall #endif struct FCSClassInfo; struct FCSManagedMethod; class UCSClass; /** * Represents a managed assembly. * This class is responsible for loading and unloading the assembly, as well as managing all types that are defined in the C# assembly. */ UCLASS() class UCSAssembly : public UObject { GENERATED_BODY() public: void SetAssemblyPath(const FStringView InAssemblyPath); UNREALSHARPCORE_API bool LoadAssembly(bool bIsCollectible = true); UNREALSHARPCORE_API bool UnloadAssembly(); UNREALSHARPCORE_API bool IsValidAssembly() const { return ManagedAssemblyHandle.IsValid() && !ManagedAssemblyHandle->IsNull(); } FName GetAssemblyName() const { return AssemblyName; } const FString& GetAssemblyPath() const { return AssemblyPath; } bool IsLoading() const { return bIsLoading; } TSharedPtr TryFindTypeHandle(const FCSFieldName& FieldName); TSharedPtr GetManagedMethod(const TSharedPtr& TypeHandle, const FString& MethodName); template TSharedPtr FindOrAddTypeInfo(UClass* Field) { if (ICSManagedTypeInterface* ManagedClass = FCSClassUtilities::GetManagedType(Field)) { return ManagedClass->GetManagedTypeInfo(); } FCSFieldName FieldName(Field); return FindOrAddTypeInfo(FieldName); } template TSharedPtr FindOrAddTypeInfo(const FCSFieldName& ClassName) { TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::FindOrAddClassInfo); TSharedPtr& TypeInfo = AllTypes.FindOrAdd(ClassName); // Native types are populated on the go when they are needed for managed code execution. if (!TypeInfo.IsValid()) { UField* Field = TryFindField(ClassName); if (!IsValid(Field)) { UE_LOGFMT(LogUnrealSharp, Error, "Failed to find native class: {0}", *ClassName.GetName()); return nullptr; } TypeInfo = MakeShared(Field, this); } if constexpr (std::is_same_v) { return TypeInfo; } else { return StaticCastSharedPtr(TypeInfo); } } template TSharedPtr FindTypeInfo(const FCSFieldName& FieldName) const { TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::FindClassInfo); static_assert(TIsDerivedFrom::Value, "T must be a FCSManagedTypeInfo-derived type."); const TSharedPtr* TypeInfo = AllTypes.Find(FieldName); if (TypeInfo && TypeInfo->IsValid()) { return StaticCastSharedPtr(*TypeInfo); } return nullptr; } template T* FindType(const FCSFieldName& FieldName) const { TRACE_CPUPROFILER_EVENT_SCOPE(UCSAssembly::FindType); static_assert(TIsDerivedFrom::Value, "T must be a UField-derived type."); TSharedPtr TypeInfo = AllTypes.FindRef(FieldName); if (TypeInfo.IsValid()) { return Cast(TypeInfo->StartBuildingManagedType()); } return TryFindField(FieldName); } // Creates a C# counterpart for the given UObject. TSharedPtr CreateManagedObject(const UObject* Object); TSharedPtr FindOrCreateManagedInterfaceWrapper(UObject* Object, UClass* InterfaceClass); // Add a class that is waiting for its parent class to be loaded before it can be created. void AddPendingClass(const FCSTypeReferenceMetaData& ParentClass, FCSClassInfo* NewClass); TSharedPtr GetManagedAssemblyHandle() const { return ManagedAssemblyHandle; } private: bool ProcessTypeMetadata(); void OnModulesChanged(FName InModuleName, EModuleChangeReason InModuleChangeReason); template T* TryFindField(const FCSFieldName FieldName) const { TRACE_CPUPROFILER_EVENT_SCOPE(UCSManager::TryFindField); static_assert(TIsDerivedFrom::Value, "T must be a UObject-derived type."); if (!FieldName.IsValid()) { UE_LOGFMT(LogUnrealSharp, Warning, "Invalid field name: {0}", *FieldName.GetName()); return nullptr; } UPackage* Package = FieldName.GetPackage(); if (!IsValid(Package)) { UE_LOGFMT(LogUnrealSharp, Warning, "Failed to find package for field: {0}", *FieldName.GetName()); return nullptr; } return FindObject(Package, *FieldName.GetName()); } // All Unreal types that are defined in this assembly. TMap> AllTypes; // All handles allocated by this assembly. Handles to types, methods, objects. TArray> AllocatedManagedHandles; // Handles to all allocated UTypes (UClass/UStruct, etc) that are defined in this assembly. TMap> ManagedClassHandles; // Pending classes that are waiting for their parent class to be loaded by the engine. TMap> PendingClasses; // Handle to the Assembly object in C#. TSharedPtr ManagedAssemblyHandle; // Full path to the assembly file. FString AssemblyPath; // Assembly file name without the path. FName AssemblyName; bool bIsLoading = false; };