using System; using System.Collections.Generic; using System.Linq; using CppSharp.AST; using Type = CppSharp.AST.Type; namespace CppSharp.Generators.AST { public class ASTRecord { public ASTRecord Parent; public object Object; public bool Visited; public bool GetParent(out T @out) { @out = default(T); if (Parent == null) return false; var v = Parent.Object; if (!(v is T)) return false; @out = (T) v; return true; } // Finds the first ancestor of type T. public ASTRecord FindAncestor() { if (Parent == null) return null; if (Parent.Object is T) return Parent; return Parent.FindAncestor(); } // Pushes ancestors into the stack until it has found one of type T. public bool GetAncestors(ref Stack ancestors) { ancestors.Push(this); T value; if (GetParent(out value)) { ancestors.Push(value); return true; } return Parent != null && Parent.GetAncestors(ref ancestors); } } public class ASTRecord : ASTRecord { public T Value { get { return (T) Object; } } public override string ToString() { return Value.ToString(); } } public class ASTRecordStack { private readonly Stack recordStack; public ASTRecordStack() { recordStack = new Stack(); } public ASTRecord Push(T value) { ASTRecord parent = null; if (recordStack.Count > 0) parent = recordStack.Peek(); var record = new ASTRecord() { Parent = parent, Object = value }; recordStack.Push(record); return record; } public void Pop() { recordStack.Pop(); } public bool Contains(object decl) { return recordStack.Any(rec => decl == rec.Object); } public bool Contains(Func func) { return recordStack.Any(func); } public bool IsBeingVisited(Declaration decl) { var record = recordStack.FirstOrDefault(rec => decl == rec.Object); if (record != null) { var isBeingVisited = record.Visited; record.Visited = true; return isBeingVisited; } return false; } } static class ASTRecordExtensions { public static bool IsBaseClass(this ASTRecord record) { Class decl; if (!record.GetParent(out decl)) return false; var recordDecl = record.Object as Class; return recordDecl != null && recordDecl == decl.BaseClass; } public static bool IsFieldValueType(this ASTRecord record) { var ancestors = new Stack(); if(!record.GetAncestors(ref ancestors)) return false; var field = (Field)ancestors.Pop(); Class decl; return field.Type.Desugar().IsTagDecl(out decl) && decl.IsValueType; } } public class RecordCollector : AstVisitor { public readonly ISet> Declarations; private readonly ASTRecordStack recordStack; private readonly TranslationUnit translationUnit; public RecordCollector(TranslationUnit translationUnit) { this.translationUnit = translationUnit; Declarations = new HashSet>(); recordStack = new ASTRecordStack(); } public override bool VisitDeclaration(Declaration decl) { if (decl.IsIncomplete && decl.CompleteDeclaration != null) decl = decl.CompleteDeclaration; if (recordStack.Contains(decl)) return ShouldVisitChilds(decl); Declarations.Add(recordStack.Push(decl)); decl.Visit(this); recordStack.Pop(); return false; } public override bool VisitType(Type type, TypeQualifiers quals) { type = type.Desugar(); if(recordStack.Contains(type)) return true; recordStack.Push(type); type.Visit(this); recordStack.Pop(); return false; } public bool ShouldVisitChilds(Declaration decl) { if (decl == translationUnit) return true; if (decl is TranslationUnit) return false; if (recordStack.Contains(record => record.Object is Type)) return false; if (recordStack.IsBeingVisited(decl)) return false; if (decl.Namespace == null) return true; // No need to continue visiting after a declaration of another // translation unit is encountered. return decl.Namespace.TranslationUnit == translationUnit; } } }