// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UnrealBuildTool { /// /// Flags indicating the state of each nested conditional block in a stack. /// [Flags] enum PreprocessorBranch { /// /// The current conditional branch is active. /// Active = 0x01, /// /// A branch within the current conditional block has been taken. Any #else/#elif blocks will not be taken. /// Taken = 0x02, /// /// The #else directive for this conditional block has been encountered. An #endif directive is expected next. /// Complete = 0x04, /// /// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive) /// HasIfDirective = 0x10, /// /// The first condition in this branch was an #if directive (as opposed to an #ifdef or #ifndef directive) /// HasIfdefDirective = 0x20, /// /// The first condition in this branch was an #ifndef directive (as opposed to an #ifdef or #if directive) /// HasIfndefDirective = 0x40, /// /// The branch has an #elif directive /// HasElifDirective = 0x80, } /// /// Marshals access to the preprocessor state, ensuring that any changes are tracked within the active transform /// class PreprocessorState { /// /// Stack of conditional branch states /// List Branches = new List(); /// /// Mapping of name to macro definition /// Dictionary NameToMacro = new Dictionary(); /// /// The current transform. Any queries or state modifications will be recorded in this. /// PreprocessorTransform? Transform; /// /// Enumerates the current branches. Do not use this for decision logic; any dependencies will not be tracked. /// public IEnumerable CurrentBranches { get { return Branches; } } /// /// Enumerates the current macros. Do not use this for decision logic; any dependencies will not be tracked. /// public IEnumerable CurrentMacros { get { return NameToMacro.Values; } } /// /// Initialize an empty preprocessor state /// public PreprocessorState() { } /// /// Duplicates another preprocessor state. Throws an exception if a transform is currently being built in the other state. /// /// The preprocessor state to copy public PreprocessorState(PreprocessorState Other) { if(Other.Transform != null) { throw new Exception("Unable to copy another preprocessor state while a transform is being built."); } Branches.AddRange(Other.Branches); foreach(KeyValuePair Pair in Other.NameToMacro) { NameToMacro[Pair.Key] = Pair.Value; } } /// /// Create a new transform object, and assign it to be current /// /// The new transform object public PreprocessorTransform BeginCapture() { Transform = new PreprocessorTransform(); return Transform; } /// /// Detach the current transform /// /// The transform object public PreprocessorTransform? EndCapture() { PreprocessorTransform? PrevTransform = Transform; Transform = null; return PrevTransform; } /// /// Sets a flag indicating that a #pragma once directive was encountered /// public void MarkPragmaOnce() { if(Transform != null) { Transform.bHasPragmaOnce = true; } } /// /// Set a macro to a specific definition /// /// Macro definition public void DefineMacro(PreprocessorMacro Macro) { NameToMacro[Macro.Name] = Macro; if(Transform != null) { Transform.NewMacros[Macro.Name] = Macro; } } /// /// Checks if a macro with the given name is defined /// /// The macro name /// True if the macro is defined public bool IsMacroDefined(Identifier Name) { // Could account for the fact that we don't need the full definition later... PreprocessorMacro? Macro; return TryGetMacro(Name, out Macro); } /// /// Removes a macro definition /// /// Name of the macro public void UndefMacro(Identifier Name) { NameToMacro.Remove(Name); if(Transform != null) { Transform.NewMacros[Name] = null; } } /// /// Attemps to get the definition for a macro /// /// Name of the macro /// Receives the macro definition, or null if it's not defined /// True if the macro is defined, otherwise false public bool TryGetMacro(Identifier Name, [NotNullWhen(true)] out PreprocessorMacro? Macro) { bool bResult = NameToMacro.TryGetValue(Name, out Macro); if(Transform != null && !Transform.NewMacros.ContainsKey(Name)) { Transform.RequiredMacros[Name] = Macro; } return Macro != null; } /// /// Pushes the preprocessor branch onto the stack /// /// The branch state public void PushBranch(PreprocessorBranch Branch) { Branches.Add(Branch); if(Transform != null) { Transform.NewBranches.Add(Branch); } } /// /// Pops a preprocessor branch from the stack /// /// The popped branch state public PreprocessorBranch PopBranch() { PreprocessorBranch Branch; if(!TryPopBranch(out Branch)) { throw new InvalidOperationException("Branch stack is empty"); } return Branch; } /// /// Attempts to pops a preprocessor branch from the stack /// /// On success, receives the preprocessor branch state /// True if a branch was active, else false public bool TryPopBranch(out PreprocessorBranch Branch) { if(Branches.Count == 0) { Branch = 0; return false; } else { Branch = Branches[Branches.Count - 1]; Branches.RemoveAt(Branches.Count - 1); if(Transform != null) { if(Transform.NewBranches.Count > 0) { Transform.NewBranches.RemoveAt(Transform.NewBranches.Count - 1); } else { Transform.RequiredBranches.Add(Branch); Transform.bRequireTopmostActive = null; } } return true; } } /// /// Determines if the branch that the preprocessor is in is active /// /// True if the branch is active, false otherwise public bool IsCurrentBranchActive() { bool bActive = (Branches.Count == 0 || Branches[Branches.Count - 1].HasFlag(PreprocessorBranch.Active)); if(Transform != null && Transform.NewBranches.Count == 0) { Transform.bRequireTopmostActive = bActive; } return bActive; } /// /// Determines if the given transform can apply to the current preprocessor state /// /// The transform to test /// True if the transform can be applied to the current state public bool CanApply(PreprocessorTransform Transform) { // Check all the required branches match for(int Idx = 0; Idx < Transform.RequiredBranches.Count; Idx++) { if(Branches[Branches.Count - Idx - 1] != Transform.RequiredBranches[Idx]) { return false; } } // Check the topmost branch is active if(Transform.bRequireTopmostActive.HasValue) { bool bTopmostActive = (Branches.Count == Transform.RequiredBranches.Count || Branches[Branches.Count - Transform.RequiredBranches.Count - 1].HasFlag(PreprocessorBranch.Active)); if(Transform.bRequireTopmostActive.Value != bTopmostActive) { return false; } } // Check all the required macros match foreach(KeyValuePair RequiredPair in Transform.RequiredMacros) { PreprocessorMacro? Macro; if(NameToMacro.TryGetValue(RequiredPair.Key, out Macro)) { if(RequiredPair.Value == null || !Macro.IsEquivalentTo(RequiredPair.Value)) { return false; } } else { if(RequiredPair.Value != null) { return false; } } } return true; } /// /// Apply the given transform to the current preprocessor state /// /// The transform to apply /// True if the transform was applied to the current state public bool TryToApply(PreprocessorTransform Transform) { if(!CanApply(Transform)) { return false; } // Update the branch state Branches.RemoveRange(Branches.Count - Transform.RequiredBranches.Count, Transform.RequiredBranches.Count); Branches.AddRange(Transform.NewBranches); // Update the macro definitions foreach(KeyValuePair NewMacro in Transform.NewMacros) { if(NewMacro.Value == null) { NameToMacro.Remove(NewMacro.Key); } else { NameToMacro[NewMacro.Key] = NewMacro.Value; } } return true; } } }