Files
UnrealEngine/Engine/Source/Programs/Shared/EpicGames.UHT/Utils/UhtReferenceCollector.cs
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

253 lines
7.4 KiB
C#

// 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
{
/// <summary>
/// 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.
/// </summary>
public interface IUhtReferenceCollector
{
/// <summary>
/// Add a cross module reference to a given object type.
/// </summary>
/// <param name="obj">Object/type being referenced</param>
/// <param name="type">Type of reference required</param>
void AddCrossModuleReference(UhtObject? obj, UhtSingletonType type);
/// <summary>
/// Add an object declaration
/// </summary>
/// <param name="obj">Object in question</param>
/// <param name="type">Type of reference required</param>
void AddDeclaration(UhtObject obj, UhtSingletonType type);
/// <summary>
/// Add a field as a singleton for exporting
/// </summary>
/// <param name="field">Field to be added</param>
void AddSingleton(UhtField field);
/// <summary>
/// Add a field as a type being exported
/// </summary>
/// <param name="field">Field to be added</param>
void AddExportType(UhtField field);
/// <summary>
/// Add a forward declaration. This is the preferred way of adding a forward declaration since the exporter will
/// handle all the formatting requirements.
/// </summary>
/// <param name="field"></param>
void AddForwardDeclaration(UhtField field);
/// <summary>
/// Add a forward declaration. The string can contain multiple declarations but must only exist on one line.
/// </summary>
/// <param name="namespaceObj">The namespace to place the string</param>
/// <param name="declaration">The declarations to add</param>
void AddForwardDeclaration(UhtNamespace? namespaceObj, string? declaration);
}
/// <summary>
/// Maintains a list of referenced object indices.
/// </summary>
public class UhtUniqueReferenceCollection
{
private const int Shift = 2;
private const int Mask = 0x3;
/// <summary>
/// Returns whether there is anything in this collection
/// </summary>
public bool IsEmpty => Uniques.Count == 0;
/// <summary>
/// List of all unique reference keys. Use UngetKey to get the object index and the flag.
/// </summary>
private HashSet<int> Uniques { get; } = new HashSet<int>();
/// <summary>
/// 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.
/// </summary>
/// <param name="obj">Object being referenced</param>
/// <param name="type">The type of reference required for linking.</param>
/// <returns>Integer key value.</returns>
public static int GetKey(UhtObject obj, UhtSingletonType type)
{
return obj.AlternateObject != null ? GetKey(obj.AlternateObject, type) : (obj.ObjectTypeIndex << Shift) | ((int)type);
}
/// <summary>
/// Given a key, return the object index and registered flag
/// </summary>
/// <param name="key">The key in question</param>
public static (int objectIndex, UhtSingletonType type) UngetKey(int key)
{
return (key >> Shift, (UhtSingletonType)(key & Mask));
}
/// <summary>
/// Add the given object to the references
/// </summary>
/// <param name="obj">Object to be added</param>
/// <param name="type">Type of reference required</param>
public void Add(UhtObject? obj, UhtSingletonType type)
{
if (obj != null)
{
Uniques.Add(GetKey(obj, type));
}
}
/// <summary>
/// Return the collection of references sorted by the API string returned by the delegate.
/// </summary>
/// <param name="referenceToString">Function to invoke to return the requested object API string, taking the object index and the UhtSingletonType.</param>
/// <returns>Read only memory region of all the string.</returns>
public IReadOnlyList<string> GetSortedReferences(Func<int, UhtSingletonType, string?> referenceToString)
{
// Collect the unsorted array
List<string> 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;
}
}
/// <summary>
/// Standard implementation of the reference collector interface
/// </summary>
public class UhtReferenceCollector : IUhtReferenceCollector
{
/// <summary>
/// Collection of unique cross module references
/// </summary>
public UhtUniqueReferenceCollection CrossModule { get; set; } = new();
/// <summary>
/// Collection of unique declarations
/// </summary>
public UhtUniqueReferenceCollection Declaration { get; set; } = new();
/// <summary>
/// Collection of singletons
/// </summary>
public List<UhtField> Singletons { get; } = [];
/// <summary>
/// Collection of types to export
/// </summary>
public List<UhtField> ExportTypes { get; } = [];
/// <summary>
/// Collection of fields needing forward declarations
/// </summary>
public HashSet<UhtField> ForwardDeclarations { get; } = [];
/// <summary>
/// Collection of forward declarations in text form
/// </summary>
public HashSet<string> ForwardDeclarationStrings { get; } = [];
/// <summary>
/// Collection of referenced headers
/// </summary>
public HashSet<UhtHeaderFile> ReferencedHeaders { get; } = [];
/// <inheritdoc/>
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);
}
}
/// <inheritdoc/>
public void AddDeclaration(UhtObject obj, UhtSingletonType type)
{
Declaration.Add(obj, type);
}
/// <inheritdoc/>
public void AddSingleton(UhtField field)
{
Singletons.Add(field);
}
/// <inheritdoc/>
public void AddExportType(UhtField field)
{
ExportTypes.Add(field);
}
/// <inheritdoc/>
public void AddForwardDeclaration(UhtField field)
{
ForwardDeclarations.Add(field);
}
/// <inheritdoc/>
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));
}
}
}
}
}