// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using EpicGames.Core; using EpicGames.UHT.Exporters.CodeGen; using EpicGames.UHT.Types; namespace EpicGames.UHT.Utils { /// /// Interface used to collect all the objects referenced by a given type. /// Not all types such as UhtPackage and UhtHeaderFile support collecting /// references due to assorted reasons. /// public interface IUhtReferenceCollector { /// /// Add a cross module reference to a given object type. /// /// Object/type being referenced /// Type of reference required void AddCrossModuleReference(UhtObject? obj, UhtSingletonType type); /// /// Add an object declaration /// /// Object in question /// Type of reference required void AddDeclaration(UhtObject obj, UhtSingletonType type); /// /// Add a field as a singleton for exporting /// /// Field to be added void AddSingleton(UhtField field); /// /// Add a field as a type being exported /// /// Field to be added void AddExportType(UhtField field); /// /// Add a forward declaration. This is the preferred way of adding a forward declaration since the exporter will /// handle all the formatting requirements. /// /// void AddForwardDeclaration(UhtField field); /// /// Add a forward declaration. The string can contain multiple declarations but must only exist on one line. /// /// The namespace to place the string /// The declarations to add void AddForwardDeclaration(UhtNamespace? namespaceObj, string? declaration); } /// /// Maintains a list of referenced object indices. /// public class UhtUniqueReferenceCollection { private const int Shift = 2; private const int Mask = 0x3; /// /// Returns whether there is anything in this collection /// public bool IsEmpty => Uniques.Count == 0; /// /// List of all unique reference keys. Use UngetKey to get the object index and the flag. /// private HashSet Uniques { get; } = new HashSet(); /// /// Return an encoded key that represents the object and type of reference. /// If the object has the alternate object set (i.e. native interfaces), then /// that object's index is used to generate the key. /// /// Object being referenced /// The type of reference required for linking. /// Integer key value. public static int GetKey(UhtObject obj, UhtSingletonType type) { return obj.AlternateObject != null ? GetKey(obj.AlternateObject, type) : (obj.ObjectTypeIndex << Shift) | ((int)type); } /// /// Given a key, return the object index and registered flag /// /// The key in question public static (int objectIndex, UhtSingletonType type) UngetKey(int key) { return (key >> Shift, (UhtSingletonType)(key & Mask)); } /// /// Add the given object to the references /// /// Object to be added /// Type of reference required public void Add(UhtObject? obj, UhtSingletonType type) { if (obj != null) { Uniques.Add(GetKey(obj, type)); } } /// /// Return the collection of references sorted by the API string returned by the delegate. /// /// Function to invoke to return the requested object API string, taking the object index and the UhtSingletonType. /// Read only memory region of all the string. public IReadOnlyList GetSortedReferences(Func referenceToString) { // Collect the unsorted array List sorted = new(Uniques.Count); foreach(int key in Uniques) { (int objectIndex, UhtSingletonType type) = UngetKey(key); if (referenceToString(objectIndex, type) is string decl) { sorted.Add(decl); } } // Sort the array sorted.Sort(StringComparerUE.OrdinalIgnoreCase); // Remove duplicates. In some instances the different keys might return the same string. // This removes those duplicates if (sorted.Count > 1) { int priorOut = 0; for (int index = 1; index < sorted.Count; ++index) { if (sorted[index] != sorted[priorOut]) { ++priorOut; sorted[priorOut] = sorted[index]; } } if (priorOut < sorted.Count - 1) { sorted.RemoveRange(priorOut + 1, sorted.Count - priorOut - 1); } } return sorted; } } /// /// Standard implementation of the reference collector interface /// public class UhtReferenceCollector : IUhtReferenceCollector { /// /// Collection of unique cross module references /// public UhtUniqueReferenceCollection CrossModule { get; set; } = new(); /// /// Collection of unique declarations /// public UhtUniqueReferenceCollection Declaration { get; set; } = new(); /// /// Collection of singletons /// public List Singletons { get; } = []; /// /// Collection of types to export /// public List ExportTypes { get; } = []; /// /// Collection of fields needing forward declarations /// public HashSet ForwardDeclarations { get; } = []; /// /// Collection of forward declarations in text form /// public HashSet ForwardDeclarationStrings { get; } = []; /// /// Collection of referenced headers /// public HashSet ReferencedHeaders { get; } = []; /// public void AddCrossModuleReference(UhtObject? obj, UhtSingletonType type) { CrossModule.Add(obj, type); if (obj != null && obj is not UhtPackage && type == UhtSingletonType.Registered) { ReferencedHeaders.Add(obj.HeaderFile); } } /// public void AddDeclaration(UhtObject obj, UhtSingletonType type) { Declaration.Add(obj, type); } /// public void AddSingleton(UhtField field) { Singletons.Add(field); } /// public void AddExportType(UhtField field) { ExportTypes.Add(field); } /// public void AddForwardDeclaration(UhtField field) { ForwardDeclarations.Add(field); } /// public void AddForwardDeclaration(UhtNamespace? namespaceObj, string? declaration) { if (!String.IsNullOrEmpty(declaration)) { if (namespaceObj == null || namespaceObj.IsGlobal) { ForwardDeclarationStrings.Add(declaration); } else { // This is hardly used so just wrap the declaration in the namespace instead of // trying to group them all together in a multiple line namespace declaration. StringBuilder builder = new(); namespaceObj.AppendSingleLine(builder, (builder) => builder.Append(declaration)); } } } } }