mirror of https://github.com/mono/CppSharp.git
19 changed files with 433 additions and 432 deletions
@ -0,0 +1,178 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using CppSharp.AST; |
||||||
|
using Type = CppSharp.AST.Type; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.AST |
||||||
|
{ |
||||||
|
public class ASTRecord |
||||||
|
{ |
||||||
|
public ASTRecord Parent; |
||||||
|
public object Object; |
||||||
|
|
||||||
|
public bool GetParent<T>(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; |
||||||
|
} |
||||||
|
|
||||||
|
// Pushes ancestors into the stack until it is found one of type T.
|
||||||
|
public bool GetAncestors<T>(ref Stack<object> ancestors) |
||||||
|
{ |
||||||
|
ancestors.Push(this); |
||||||
|
|
||||||
|
T value; |
||||||
|
if (GetParent(out value)) |
||||||
|
{ |
||||||
|
ancestors.Push(value); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return Parent != null |
||||||
|
&& Parent.GetAncestors<T>(ref ancestors); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ASTRecord<T> : ASTRecord |
||||||
|
{ |
||||||
|
public T Value |
||||||
|
{ |
||||||
|
get { return (T) Object; } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class ASTRecordStack |
||||||
|
{ |
||||||
|
private readonly Stack<ASTRecord> recordStack; |
||||||
|
|
||||||
|
public ASTRecordStack() |
||||||
|
{ |
||||||
|
recordStack = new Stack<ASTRecord>(); |
||||||
|
} |
||||||
|
|
||||||
|
public ASTRecord<T> Push<T>(T value) |
||||||
|
{ |
||||||
|
ASTRecord parent = null; |
||||||
|
|
||||||
|
if (recordStack.Count > 0) |
||||||
|
parent = recordStack.Peek(); |
||||||
|
|
||||||
|
var record = new ASTRecord<T>() |
||||||
|
{ |
||||||
|
Parent = parent, |
||||||
|
Object = value |
||||||
|
}; |
||||||
|
recordStack.Push(record); |
||||||
|
return record; |
||||||
|
} |
||||||
|
|
||||||
|
public void Pop() |
||||||
|
{ |
||||||
|
recordStack.Pop(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static class ASTRecordExtensions |
||||||
|
{ |
||||||
|
public static bool IsBaseClass(this ASTRecord record) |
||||||
|
{ |
||||||
|
Class decl; |
||||||
|
return record.GetParent(out decl) && decl.BaseClass == record.Object; |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsFieldValueType(this ASTRecord record) |
||||||
|
{ |
||||||
|
var ancestors = new Stack<object>(); |
||||||
|
if(!record.GetAncestors<Field>(ref ancestors)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var field = (Field)ancestors.Pop(); |
||||||
|
|
||||||
|
Class decl; |
||||||
|
if (!field.Type.Desugar().IsTagDecl(out decl)) |
||||||
|
return false; |
||||||
|
|
||||||
|
return decl.IsValueType; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public class RecordCollector : AstVisitor |
||||||
|
{ |
||||||
|
public readonly ISet<ASTRecord<Declaration>> Declarations; |
||||||
|
private readonly ASTRecordStack recordStack; |
||||||
|
|
||||||
|
public TranslationUnit translationUnit; |
||||||
|
|
||||||
|
public ISet<object> Visited2 { get; private set; } |
||||||
|
public bool AlreadyVisited2(object o) |
||||||
|
{ |
||||||
|
return !Visited2.Add(o); |
||||||
|
} |
||||||
|
|
||||||
|
public RecordCollector(TranslationUnit translationUnit) |
||||||
|
{ |
||||||
|
this.translationUnit = translationUnit; |
||||||
|
Declarations = new HashSet<ASTRecord<Declaration>>(); |
||||||
|
recordStack = new ASTRecordStack(); |
||||||
|
Visited2 = new HashSet<object>(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitDeclaration(Declaration decl) |
||||||
|
{ |
||||||
|
if(translationUnit.FileName.Contains("Font")) |
||||||
|
Console.Write(""); |
||||||
|
|
||||||
|
if (decl.IsIncomplete && decl.CompleteDeclaration != null) |
||||||
|
decl = decl.CompleteDeclaration; |
||||||
|
|
||||||
|
if(AlreadyVisited2(decl)) |
||||||
|
return ShouldVisitChilds(decl); |
||||||
|
Visited.Remove(decl); // So Class can be revisited
|
||||||
|
|
||||||
|
Declarations.Add(recordStack.Push(decl)); |
||||||
|
decl.Visit(this); |
||||||
|
recordStack.Pop(); |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitType(Type type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
type = type.Desugar(); |
||||||
|
|
||||||
|
if(AlreadyVisited2(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 (decl.Namespace == null) |
||||||
|
return true; |
||||||
|
|
||||||
|
// No need to continue visiting after a declaration of
|
||||||
|
// another translation unit is encountered.
|
||||||
|
return decl.Namespace.TranslationUnit == translationUnit; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,207 +0,0 @@ |
|||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.IO; |
|
||||||
using CppSharp.AST; |
|
||||||
|
|
||||||
namespace CppSharp.Generators.CLI |
|
||||||
{ |
|
||||||
public struct CLIForwardReference |
|
||||||
{ |
|
||||||
public Declaration Declaration; |
|
||||||
public Namespace Namespace; |
|
||||||
public string Text; |
|
||||||
} |
|
||||||
|
|
||||||
public class CLIForwardReferencePrinter : IDeclVisitor<bool> |
|
||||||
{ |
|
||||||
public readonly IList<string> Includes; |
|
||||||
public readonly IList<CLIForwardReference> Refs; |
|
||||||
private readonly TypeRefsVisitor TypeRefs; |
|
||||||
private TypeReference currentTypeReference; |
|
||||||
|
|
||||||
public CLIForwardReferencePrinter(TypeRefsVisitor typeRefs) |
|
||||||
{ |
|
||||||
Includes = new List<string>(); |
|
||||||
Refs = new List<CLIForwardReference>(); |
|
||||||
TypeRefs = typeRefs; |
|
||||||
} |
|
||||||
|
|
||||||
public void Process() |
|
||||||
{ |
|
||||||
foreach (var typeRef in TypeRefs.References) |
|
||||||
{ |
|
||||||
currentTypeReference = typeRef; |
|
||||||
typeRef.Declaration.Visit(this); |
|
||||||
} |
|
||||||
|
|
||||||
foreach (var baseClass in TypeRefs.Bases) |
|
||||||
VisitBaseClass(baseClass); |
|
||||||
} |
|
||||||
|
|
||||||
public void VisitBaseClass(Class @class) |
|
||||||
{ |
|
||||||
if (@class.IsIncomplete) |
|
||||||
@class = @class.CompleteDeclaration as Class; |
|
||||||
|
|
||||||
if (@class == null) |
|
||||||
return; |
|
||||||
|
|
||||||
Includes.Add(GetHeaderFromDecl(@class)); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitDeclaration(Declaration decl) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitClassDecl(Class @class) |
|
||||||
{ |
|
||||||
var completeDecl = @class.CompleteDeclaration as Class; |
|
||||||
if (@class.IsIncomplete && completeDecl != null) |
|
||||||
return VisitClassDecl(completeDecl); |
|
||||||
|
|
||||||
if (@class.IsValueType) |
|
||||||
{ |
|
||||||
Refs.Add(new CLIForwardReference() |
|
||||||
{ |
|
||||||
Declaration = @class, |
|
||||||
Namespace = currentTypeReference.Namespace, |
|
||||||
Text = string.Format("value struct {0};", @class.Name) |
|
||||||
}); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
Refs.Add(new CLIForwardReference() |
|
||||||
{ |
|
||||||
Declaration = @class, |
|
||||||
Namespace = currentTypeReference.Namespace, |
|
||||||
Text = string.Format("ref class {0};", @class.Name) |
|
||||||
}); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitFieldDecl(Field field) |
|
||||||
{ |
|
||||||
Class @class; |
|
||||||
if (field.Type.IsTagDecl(out @class)) |
|
||||||
{ |
|
||||||
if (@class.IsValueType) |
|
||||||
Includes.Add(GetHeaderFromDecl(@class)); |
|
||||||
else |
|
||||||
VisitClassDecl(@class); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
Enumeration @enum; |
|
||||||
if (field.Type.IsTagDecl(out @enum)) |
|
||||||
return VisitEnumDecl(@enum); |
|
||||||
|
|
||||||
Includes.Add(GetHeaderFromDecl(field)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitFunctionDecl(Function function) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitMethodDecl(Method method) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitParameterDecl(Parameter parameter) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public string GetHeaderFromDecl(Declaration decl) |
|
||||||
{ |
|
||||||
var @namespace = decl.Namespace; |
|
||||||
var unit = @namespace.TranslationUnit; |
|
||||||
|
|
||||||
if (unit.Ignore) |
|
||||||
return string.Empty; |
|
||||||
|
|
||||||
if (unit.IsSystemHeader) |
|
||||||
return string.Empty; |
|
||||||
|
|
||||||
return Path.GetFileNameWithoutExtension(unit.FileName); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitTypedefDecl(TypedefDecl typedef) |
|
||||||
{ |
|
||||||
FunctionType function; |
|
||||||
if (typedef.Type.IsPointerTo<FunctionType>(out function)) |
|
||||||
{ |
|
||||||
Includes.Add(GetHeaderFromDecl(typedef)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitEnumDecl(Enumeration @enum) |
|
||||||
{ |
|
||||||
Includes.Add(GetHeaderFromDecl(@enum)); |
|
||||||
|
|
||||||
if (@enum.Type.IsPrimitiveType(PrimitiveType.Int32)) |
|
||||||
{ |
|
||||||
Refs.Add(new CLIForwardReference() |
|
||||||
{ |
|
||||||
Declaration = @enum, |
|
||||||
Namespace = currentTypeReference.Namespace, |
|
||||||
Text = string.Format("enum struct {0};", @enum.Name) |
|
||||||
}); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
Refs.Add(new CLIForwardReference() |
|
||||||
{ |
|
||||||
Declaration = @enum, |
|
||||||
Namespace = currentTypeReference.Namespace, |
|
||||||
Text = string.Format("enum struct {0} : {1};", @enum.Name, @enum.Type) |
|
||||||
}); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitVariableDecl(Variable variable) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitClassTemplateDecl(ClassTemplate template) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitFunctionTemplateDecl(FunctionTemplate template) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitMacroDefinition(MacroDefinition macro) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitNamespace(Namespace @namespace) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitEvent(Event @event) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
|
|
||||||
public bool VisitProperty(Property property) |
|
||||||
{ |
|
||||||
throw new NotImplementedException(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,162 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using CppSharp.AST; |
||||||
|
using CppSharp.Generators.AST; |
||||||
|
using CppSharp.Types; |
||||||
|
|
||||||
|
namespace CppSharp.Generators.CLI |
||||||
|
{ |
||||||
|
public class CLITypeReference |
||||||
|
{ |
||||||
|
public Include Include; |
||||||
|
public string FowardReference; |
||||||
|
} |
||||||
|
|
||||||
|
public class CLITypeReferenceCollector : AstVisitor |
||||||
|
{ |
||||||
|
private Dictionary<Declaration, CLITypeReference> typeReferences; |
||||||
|
private readonly ITypeMapDatabase TypeMapDatabase; |
||||||
|
private TranslationUnit TranslationUnit; |
||||||
|
|
||||||
|
public IEnumerable<CLITypeReference> TypeReferences |
||||||
|
{ |
||||||
|
get { return typeReferences.Values; } |
||||||
|
} |
||||||
|
|
||||||
|
public CLITypeReferenceCollector(ITypeMapDatabase typeMapDatabase) |
||||||
|
{ |
||||||
|
TypeMapDatabase = typeMapDatabase; |
||||||
|
typeReferences = new Dictionary<Declaration,CLITypeReference>(); |
||||||
|
} |
||||||
|
|
||||||
|
public CLITypeReference GetTypeReference(Declaration decl) |
||||||
|
{ |
||||||
|
if(typeReferences.ContainsKey(decl)) |
||||||
|
return typeReferences[decl]; |
||||||
|
|
||||||
|
var @ref = new CLITypeReference(); |
||||||
|
typeReferences.Add(decl, @ref); |
||||||
|
return @ref; |
||||||
|
} |
||||||
|
|
||||||
|
public void Process(Namespace @namespace) |
||||||
|
{ |
||||||
|
var collector = new RecordCollector(@namespace.TranslationUnit); |
||||||
|
@namespace.Visit(collector); |
||||||
|
|
||||||
|
TranslationUnit = @namespace.TranslationUnit; |
||||||
|
|
||||||
|
foreach (var record in collector.Declarations) |
||||||
|
{ |
||||||
|
record.Value.Visit(this); |
||||||
|
GenerateInclude(record); |
||||||
|
ProcessTypeMap(record); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void ProcessTypeMap(ASTRecord<Declaration> record) |
||||||
|
{ |
||||||
|
TypeMap typeMap; |
||||||
|
if (!TypeMapDatabase.FindTypeMap(record.Value, out typeMap)) return; |
||||||
|
|
||||||
|
// Typemap must explicitly set the include file when one is required.
|
||||||
|
GetTypeReference(record.Value).Include.File = ""; |
||||||
|
|
||||||
|
typeMap.Declaration = record.Value; |
||||||
|
typeMap.CLITypeReference(this, record); |
||||||
|
} |
||||||
|
|
||||||
|
private void GenerateInclude(ASTRecord<Declaration> record) |
||||||
|
{ |
||||||
|
var decl = record.Value; |
||||||
|
if(decl.Namespace == null) |
||||||
|
return; |
||||||
|
|
||||||
|
var declFile = decl.Namespace.TranslationUnit.FileName; |
||||||
|
|
||||||
|
if(decl.Namespace.TranslationUnit.IsSystemHeader) |
||||||
|
return; |
||||||
|
|
||||||
|
if(decl.Ignore) |
||||||
|
return; |
||||||
|
|
||||||
|
if(IsBuiltinTypedef(decl)) |
||||||
|
return; |
||||||
|
|
||||||
|
if (declFile.Contains("String")) |
||||||
|
return; |
||||||
|
|
||||||
|
GetTypeReference(decl).Include = new Include() |
||||||
|
{ |
||||||
|
File = declFile, |
||||||
|
Kind = Include.IncludeKind.Quoted, |
||||||
|
InHeader = IsIncludeInHeader(record) |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
private bool IsBuiltinTypedef(Declaration decl) |
||||||
|
{ |
||||||
|
var typedefDecl = decl as TypedefDecl; |
||||||
|
if(typedefDecl == null) return false; |
||||||
|
if(typedefDecl.Type is BuiltinType) return true; |
||||||
|
|
||||||
|
var typedefType = typedefDecl.Type as TypedefType; |
||||||
|
if(typedefType == null) return false; |
||||||
|
if(typedefType.Declaration == null) return false; |
||||||
|
|
||||||
|
return typedefType.Declaration.Type is BuiltinType; |
||||||
|
} |
||||||
|
|
||||||
|
private bool IsIncludeInHeader(ASTRecord<Declaration> record) |
||||||
|
{ |
||||||
|
return record.IsBaseClass() || record.IsFieldValueType(); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitDeclaration(Declaration decl) |
||||||
|
{ |
||||||
|
return ShouldVisitDecl(decl); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitClassDecl(Class @class) |
||||||
|
{ |
||||||
|
if(!ShouldVisitDecl(@class)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (@class.IsIncomplete && @class.CompleteDeclaration != null) |
||||||
|
@class = (Class) @class.CompleteDeclaration; |
||||||
|
|
||||||
|
var keywords = @class.IsValueType? "value struct" : "ref class"; |
||||||
|
var @ref = string.Format("{0} {1};", keywords, @class.Name); |
||||||
|
|
||||||
|
GetTypeReference(@class).FowardReference = @ref; |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool VisitEnumDecl(Enumeration @enum) |
||||||
|
{ |
||||||
|
if(!ShouldVisitDecl(@enum)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var @base = ""; |
||||||
|
if(!@enum.Type.IsPrimitiveType(PrimitiveType.Int32)) |
||||||
|
@base = string.Format(" : {0}", @enum.Type); |
||||||
|
|
||||||
|
var @ref = string.Format("enum struct {0}{1};", @enum.Name, @base); |
||||||
|
|
||||||
|
GetTypeReference(@enum).FowardReference = @ref; |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private bool ShouldVisitDecl(Declaration decl) |
||||||
|
{ |
||||||
|
if(decl.Namespace != null && decl.Namespace.TranslationUnit.IsSystemHeader) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (decl.Ignore) |
||||||
|
return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -1,30 +0,0 @@ |
|||||||
using CppSharp.AST; |
|
||||||
|
|
||||||
namespace CppSharp.Passes |
|
||||||
{ |
|
||||||
public class CheckTypeReferencesPass : TranslationUnitPass |
|
||||||
{ |
|
||||||
TypeRefsVisitor typeRefs; |
|
||||||
|
|
||||||
public override bool VisitTranslationUnit(TranslationUnit unit) |
|
||||||
{ |
|
||||||
if (unit.Ignore) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (unit.IsSystemHeader) |
|
||||||
return false; |
|
||||||
|
|
||||||
typeRefs = new TypeRefsVisitor(); |
|
||||||
return typeRefs.VisitTranslationUnit(unit); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static class CheckTypeReferencesExtensions |
|
||||||
{ |
|
||||||
public static void CheckTypeReferences(this PassBuilder builder) |
|
||||||
{ |
|
||||||
var pass = new CheckTypeReferencesPass(); |
|
||||||
builder.AddPass(pass); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue