mirror of https://github.com/mono/CppSharp.git
7 changed files with 752 additions and 905 deletions
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// Class.cs: Represents a C++ class
|
||||
//
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Reflection; |
||||
using System.Linq; |
||||
using System.CodeDom; |
||||
using System.CodeDom.Compiler; |
||||
|
||||
class Class |
||||
{ |
||||
public Class (Node n) { |
||||
Node = n; |
||||
Methods = new List<Method> (); |
||||
Fields = new List<Field> (); |
||||
} |
||||
|
||||
public Node Node { |
||||
get; set; |
||||
} |
||||
|
||||
public string Name { |
||||
get { |
||||
return Node.Name; |
||||
} |
||||
} |
||||
|
||||
public List<Method> Methods { |
||||
get; set; |
||||
} |
||||
|
||||
public List<Field> Fields { |
||||
get; set; |
||||
} |
||||
|
||||
public CodeTypeDeclaration GenerateClass (Generator g, CodeTypeDeclaration libDecl) { |
||||
var decl = new CodeTypeDeclaration (Name); |
||||
decl.IsPartial = true; |
||||
// FIXME: Inheritance
|
||||
decl.BaseTypes.Add (new CodeTypeReference ("ICppObject")); |
||||
|
||||
var layout = new CodeTypeDeclaration ("_" + Name); |
||||
layout.IsStruct = true; |
||||
layout.TypeAttributes = TypeAttributes.NotPublic; |
||||
decl.Members.Add (layout); |
||||
|
||||
foreach (var f in Fields) { |
||||
CodeMemberField field = new CodeMemberField { Name = f.Name, Type = g.CppTypeToCodeDomType (f.Type) }; |
||||
layout.Members.Add (field); |
||||
} |
||||
|
||||
var iface = new CodeTypeDeclaration ("I" + Name); |
||||
iface.IsInterface = true; |
||||
layout.TypeAttributes = TypeAttributes.NotPublic; |
||||
iface.BaseTypes.Add (new CodeTypeReference ("ICppClassOverridable", new CodeTypeReference [] { new CodeTypeReference (decl.Name) })); |
||||
decl.Members.Add (iface); |
||||
|
||||
var layoutField = new CodeMemberField (new CodeTypeReference (typeof (Type)), "native_layout"); |
||||
layoutField.Attributes = MemberAttributes.Private|MemberAttributes.Static; |
||||
layoutField.InitExpression = new CodeTypeOfExpression (layout.Name); |
||||
decl.Members.Add (layoutField); |
||||
|
||||
var implField = new CodeMemberField (new CodeTypeReference (iface.Name), "impl"); |
||||
implField.Attributes = MemberAttributes.Private|MemberAttributes.Static; |
||||
var getclass = new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (libDecl.Name), "Test"), "GetClass", new CodeTypeReference [] { new CodeTypeReference (iface.Name), new CodeTypeReference (layout.Name), new CodeTypeReference (decl.Name) }); |
||||
implField.InitExpression = new CodeMethodInvokeExpression (getclass, new CodeExpression [] { new CodePrimitiveExpression (Name) }); |
||||
decl.Members.Add (implField); |
||||
//private static IClass impl = global::CppTests.Libs.Test.GetClass <IClass, _Class, Class>("Class");
|
||||
|
||||
var ptrField = new CodeMemberField (new CodeTypeReference ("CppInstancePtr"), "native_ptr"); |
||||
ptrField.Attributes = MemberAttributes.Family; |
||||
decl.Members.Add (ptrField); |
||||
|
||||
var allocCtor = new CodeConstructor () { |
||||
}; |
||||
allocCtor.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference ("CppLibrary"), "dummy")); |
||||
allocCtor.Statements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (null, "native_ptr"), new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (null, "impl"), "Alloc"), new CodeExpression [] { new CodeThisReferenceExpression () }))); |
||||
decl.Members.Add (allocCtor); |
||||
|
||||
var subclassCtor = new CodeConstructor () { |
||||
}; |
||||
subclassCtor.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference ("CppTypeInfo"), "subClass")); |
||||
subclassCtor.Statements.Add (new CodeExpressionStatement (new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeArgumentReferenceExpression ("subClass"), "AddBase"), new CodeExpression [] { new CodeFieldReferenceExpression (new CodeFieldReferenceExpression (null, "impl"), "TypeInfo") }))); |
||||
decl.Members.Add (subclassCtor); |
||||
|
||||
var nativeProperty = new CodeMemberProperty () { |
||||
Name = "Native", |
||||
Type = new CodeTypeReference ("CppInstancePtr"), |
||||
Attributes = MemberAttributes.Public|MemberAttributes.Final |
||||
}; |
||||
nativeProperty.GetStatements.Add (new CodeMethodReturnStatement (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "native_ptr"))); |
||||
decl.Members.Add (nativeProperty); |
||||
|
||||
var disposeMethod = new CodeMemberMethod () { |
||||
Name = "Dispose", |
||||
Attributes = MemberAttributes.Public |
||||
}; |
||||
if (Methods.Any (m => m.IsDestructor)) |
||||
disposeMethod.Statements.Add (new CodeExpressionStatement (new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (null, "impl"), "Destruct"), new CodeExpression [] { new CodeFieldReferenceExpression (null, "Native") }))); |
||||
disposeMethod.Statements.Add (new CodeExpressionStatement (new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (null, "Native"), "Dispose")))); |
||||
decl.Members.Add (disposeMethod); |
||||
|
||||
foreach (Method m in Methods) { |
||||
iface.Members.Add (m.GenerateIFaceMethod (g)); |
||||
decl.Members.Add (m.GenerateWrapperMethod (g)); |
||||
} |
||||
|
||||
return decl; |
||||
} |
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Field.cs: Represents a field of a C++ class
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.CodeDom; |
||||
using System.CodeDom.Compiler; |
||||
|
||||
using Mono.VisualC.Interop; |
||||
|
||||
class Field |
||||
{ |
||||
public Field (string name, CppType type) { |
||||
Name = name; |
||||
Type = type; |
||||
} |
||||
|
||||
public string Name { |
||||
get; set; |
||||
} |
||||
|
||||
public CppType Type { |
||||
get; set; |
||||
} |
||||
} |
@ -0,0 +1,389 @@
@@ -0,0 +1,389 @@
|
||||
//
|
||||
// Generator.cs: C++ Interop Code Generator
|
||||
//
|
||||
//
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Collections.Generic; |
||||
using System.Xml; |
||||
using System.Linq; |
||||
using System.Reflection; |
||||
using System.CodeDom; |
||||
using System.CodeDom.Compiler; |
||||
using Microsoft.CSharp; |
||||
|
||||
using NDesk.Options; |
||||
|
||||
using Mono.VisualC.Interop; |
||||
|
||||
public class Generator |
||||
{ |
||||
// Command line arguments
|
||||
string OutputDir; |
||||
string Namespace; |
||||
string LibBaseName; |
||||
string InputFileName; |
||||
string FilterFile; |
||||
|
||||
CodeDomProvider Provider; |
||||
CodeGeneratorOptions CodeGenOptions; |
||||
|
||||
// Classes to generate code for
|
||||
Dictionary<string, string> Filters; |
||||
|
||||
public static int Main (String[] args) { |
||||
var generator = new Generator (); |
||||
generator.Run (args); |
||||
return 0; |
||||
} |
||||
|
||||
int ParseArguments (String[] args) { |
||||
bool help = false; |
||||
|
||||
var p = new OptionSet { |
||||
{ "h|?|help", "Show this help message", v => help = v != null }, |
||||
{ "o=", "Set the output directory", v => OutputDir = v }, |
||||
{ "ns=|namespace=", "Set the namespace of the generated code", v => Namespace = v }, |
||||
{ "lib=", "The base name of the C++ library, i.e. 'qt' for libqt.so", v =>LibBaseName = v }, |
||||
{ "filters=", "A file containing filter directives for filtering classes", v => FilterFile = v } |
||||
}; |
||||
|
||||
try { |
||||
args = p.Parse (args).ToArray (); |
||||
} catch (OptionException) { |
||||
Console.WriteLine ("Try `generator --help' for more information."); |
||||
return 1; |
||||
} |
||||
|
||||
if (help) { |
||||
p.WriteOptionDescriptions (Console.Error); |
||||
return 1; |
||||
} |
||||
|
||||
if (args.Length != 1) { |
||||
Console.WriteLine ("Usage: generator <options> <input xml file>"); |
||||
return 1; |
||||
} |
||||
|
||||
InputFileName = args [0]; |
||||
|
||||
if (LibBaseName == null) { |
||||
Console.WriteLine ("The --lib= option is required."); |
||||
return 1; |
||||
} |
||||
|
||||
if (OutputDir == null) |
||||
OutputDir = "output"; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void Run (String[] args) { |
||||
if (ParseArguments (args) != 0) { |
||||
Environment.Exit (1); |
||||
} |
||||
|
||||
Node root = LoadXml (InputFileName); |
||||
|
||||
if (FilterFile != null) |
||||
LoadFilters (FilterFile); |
||||
|
||||
CreateClasses (root); |
||||
|
||||
CreateMethods (); |
||||
|
||||
GenerateCode (); |
||||
} |
||||
|
||||
Node LoadXml (string file) { |
||||
XmlReader reader = XmlReader.Create (file, new XmlReaderSettings ()); |
||||
|
||||
Node[] parents = new Node [1024]; |
||||
|
||||
Node root = null; |
||||
|
||||
while (reader.Read()) { |
||||
if (reader.IsStartElement ()) { |
||||
string type = reader.Name; |
||||
|
||||
var attributes = new Dictionary<string, string> (); |
||||
while (reader.MoveToNextAttribute ()) { |
||||
attributes [reader.Name] = reader.Value; |
||||
} |
||||
|
||||
Node n = new Node { |
||||
Id = "", |
||||
Type = type, |
||||
Attributes = attributes, |
||||
Children = new List<Node> () |
||||
}; |
||||
|
||||
if (attributes.ContainsKey ("id")) { |
||||
n.Id = attributes ["id"]; |
||||
Node.IdToNode [n.Id] = n; |
||||
} |
||||
|
||||
if (attributes.ContainsKey ("name")) |
||||
n.Name = attributes ["name"]; |
||||
|
||||
if (parents [reader.Depth - 1] != null) { |
||||
//Console.WriteLine (parents [reader.Depth - 1].type + " -> " + e.type);
|
||||
parents [reader.Depth - 1].Children.Add (n); |
||||
} |
||||
parents [reader.Depth] = n; |
||||
|
||||
if (n.Type == "GCC_XML" && root == null) |
||||
root = n; |
||||
} |
||||
} |
||||
|
||||
return root; |
||||
} |
||||
|
||||
void LoadFilters (string file) { |
||||
Filters = new Dictionary <string, string> (); |
||||
foreach (string s in File.ReadAllLines (file)) { |
||||
Filters [s] = s; |
||||
} |
||||
} |
||||
|
||||
List<Class> Classes; |
||||
Dictionary<Node, Class> NodeToClass; |
||||
|
||||
void CreateClasses (Node root) { |
||||
List<Node> classNodes = root.Children.Where (o => o.Type == "Class" || o.Type == "Struct").ToList (); |
||||
classNodes.RemoveAll (o => o.IsTrue ("incomplete") || !o.HasValue ("name")); |
||||
|
||||
if (Filters != null) |
||||
classNodes.RemoveAll (o => !Filters.ContainsKey (o.Name)); |
||||
|
||||
List<Class> classes = new List<Class> (); |
||||
NodeToClass = new Dictionary <Node, Class> (); |
||||
|
||||
foreach (Node n in classNodes) { |
||||
Console.WriteLine (n.Name); |
||||
|
||||
Class klass = new Class (n); |
||||
classes.Add (klass); |
||||
NodeToClass [n] = klass; |
||||
} |
||||
|
||||
// Compute bases
|
||||
foreach (Class klass in classes) { |
||||
foreach (Node bn in klass.Node.Children.Where (o => o.Type == "Base")) { |
||||
//Class baseClass = nodeToClass [bn.NodeForAttr ("type")];
|
||||
throw new NotImplementedException (); |
||||
} |
||||
} |
||||
|
||||
Classes = classes; |
||||
} |
||||
|
||||
void CreateMethods () { |
||||
foreach (Class klass in Classes) { |
||||
if (!klass.Node.HasValue ("members")) |
||||
continue; |
||||
|
||||
int fieldCount = 0; |
||||
foreach (string id in klass.Node ["members"].Split (' ')) { |
||||
if (id == "") |
||||
continue; |
||||
Node n = Node.IdToNode [id]; |
||||
|
||||
bool ctor = false; |
||||
bool dtor = false; |
||||
|
||||
switch (n.Type) { |
||||
case "Field": |
||||
CppType fieldType = GetType (GetTypeNode (n)); |
||||
string fieldName; |
||||
if (n.Name != "") |
||||
fieldName = n.Name; |
||||
else |
||||
fieldName = "field" + fieldCount++; |
||||
klass.Fields.Add (new Field (fieldName, fieldType)); |
||||
break; |
||||
case "Constructor": |
||||
ctor = true; |
||||
break; |
||||
case "Destructor": |
||||
dtor = true; |
||||
break; |
||||
case "Method": |
||||
break; |
||||
default: |
||||
continue; |
||||
} |
||||
|
||||
if (!n.CheckValue ("access", "public") || (n.HasValue ("overrides") && !dtor)) |
||||
continue; |
||||
|
||||
if (!n.IsTrue ("extern") && !n.IsTrue ("inline")) |
||||
continue; |
||||
|
||||
// FIXME: Casing
|
||||
string name = dtor ? "Destruct" : n.Name; |
||||
|
||||
var method = new Method (n) { |
||||
Name = name, |
||||
IsVirtual = n.IsTrue ("virtual"), |
||||
IsStatic = n.IsTrue ("static"), |
||||
IsConst = n.IsTrue ("const"), |
||||
IsInline = n.IsTrue ("inline"), |
||||
IsArtificial = n.IsTrue ("artificial"), |
||||
IsConstructor = ctor, |
||||
IsDestructor = dtor |
||||
}; |
||||
|
||||
CppType retType; |
||||
if (n.HasValue ("returns")) |
||||
retType = GetType (n.NodeForAttr ("returns")); |
||||
else |
||||
retType = CppTypes.Void; |
||||
if (retType.ElementType == CppTypes.Unknown) |
||||
throw new NotImplementedException (); |
||||
|
||||
method.ReturnType = retType; |
||||
|
||||
bool skip = false; |
||||
int c = 0; |
||||
List<CppType> argTypes = new List<CppType> (); |
||||
foreach (Node arg in n.Children.Where (o => o.Type == "Argument")) { |
||||
string argname; |
||||
if (arg.Name == null || arg.Name == "") |
||||
argname = "arg" + c; |
||||
else |
||||
argname = arg.Name; |
||||
|
||||
CppType argtype = GetType (GetTypeNode (arg)); |
||||
if (argtype.ElementType == CppTypes.Unknown) { |
||||
//Console.WriteLine ("Skipping method " + klass.Name + "::" + member.Name + " () because it has an argument with unknown type '" + TypeNodeToString (arg) + "'.");
|
||||
skip = true; |
||||
} |
||||
|
||||
method.Parameters.Add (new Tuple<string, CppType> (argname, argtype)); |
||||
argTypes.Add (argtype); |
||||
|
||||
c++; |
||||
} |
||||
if (skip) |
||||
continue; |
||||
|
||||
// FIXME: More complete type name check
|
||||
if (ctor && argTypes.Count == 1 && argTypes [0].ElementType == CppTypes.Class && argTypes [0].ElementTypeName == klass.Name && argTypes [0].Modifiers.Count == 2 && argTypes [0].Modifiers.Contains (CppModifiers.Const) && argTypes [0].Modifiers.Contains (CppModifiers.Reference)) |
||||
method.IsCopyCtor = true; |
||||
|
||||
Console.WriteLine ("\t" + klass.Name + "." + method.Name); |
||||
|
||||
klass.Methods.Add (method); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Return a CppType for the type node N, return CppTypes.Unknown for unknown types
|
||||
CppType GetType (Node n) { |
||||
return GetType (n, new CppType ()); |
||||
} |
||||
|
||||
CppType GetType (Node n, CppType modifiers) { |
||||
switch (n.Type) { |
||||
case "ArrayType": |
||||
return GetType (GetTypeNode (n), modifiers.Modify (CppModifiers.Array)); |
||||
case "PointerType": |
||||
return GetType (GetTypeNode (n), modifiers.Modify (CppModifiers.Pointer)); |
||||
case "ReferenceType": |
||||
return GetType (GetTypeNode (n), modifiers.Modify (CppModifiers.Reference)); |
||||
case "FundamentalType": |
||||
return modifiers.CopyTypeFrom (new CppType (n.Name)); |
||||
case "CvQualifiedType": |
||||
if (n.IsTrue ("const")) |
||||
return GetType (GetTypeNode (n), modifiers.Modify (CppModifiers.Const)); |
||||
else |
||||
throw new NotImplementedException (); |
||||
case "Class": |
||||
// FIXME: Missing classes
|
||||
return modifiers.CopyTypeFrom (new CppType (CppTypes.Class, NodeToClass [n].Name)); |
||||
default: |
||||
throw new NotImplementedException (n.Type); |
||||
} |
||||
} |
||||
|
||||
Node GetTypeNode (Node n) { |
||||
return Node.IdToNode [n.Attributes ["type"]]; |
||||
} |
||||
|
||||
public CodeTypeReference CppTypeToCodeDomType (CppType t) { |
||||
CodeTypeReference rtype = null; |
||||
// FIXME: Modifiers
|
||||
switch (t.ElementType) { |
||||
case CppTypes.Void: |
||||
break; |
||||
case CppTypes.Bool: |
||||
rtype = new CodeTypeReference (typeof (bool)); |
||||
break; |
||||
case CppTypes.Int: |
||||
rtype = new CodeTypeReference (typeof (int)); |
||||
break; |
||||
case CppTypes.Class: |
||||
// FIXME: Full name
|
||||
rtype = new CodeTypeReference (t.ElementTypeName); |
||||
break; |
||||
default: |
||||
throw new NotImplementedException (t.ToString ()); |
||||
} |
||||
return rtype; |
||||
} |
||||
|
||||
void GenerateCode () { |
||||
Directory.CreateDirectory (OutputDir); |
||||
|
||||
Provider = new CSharpCodeProvider (); |
||||
CodeGenOptions = new CodeGeneratorOptions { BlankLinesBetweenMembers = false }; |
||||
|
||||
CodeTypeDeclaration libDecl = null; |
||||
|
||||
// Generate Libs class
|
||||
{ |
||||
var cu = new CodeCompileUnit (); |
||||
var ns = new CodeNamespace (Namespace); |
||||
ns.Imports.Add(new CodeNamespaceImport("System")); |
||||
ns.Imports.Add(new CodeNamespaceImport("Mono.VisualC.Interop")); |
||||
cu.Namespaces.Add (ns); |
||||
|
||||
var decl = new CodeTypeDeclaration ("Libs"); |
||||
|
||||
var field = new CodeMemberField (new CodeTypeReference ("CppLibrary"), "Test"); |
||||
field.Attributes = MemberAttributes.Public|MemberAttributes.Static; |
||||
field.InitExpression = new CodeObjectCreateExpression (new CodeTypeReference ("CppLibrary"), new CodeExpression [] { new CodePrimitiveExpression ("Test") }); |
||||
decl.Members.Add (field); |
||||
|
||||
ns.Types.Add (decl); |
||||
|
||||
libDecl = decl; |
||||
|
||||
//Provider.GenerateCodeFromCompileUnit(cu, Console.Out, CodeGenOptions);
|
||||
using (TextWriter w = File.CreateText (Path.Combine (OutputDir, "Libs.cs"))) { |
||||
Provider.GenerateCodeFromCompileUnit(cu, w, CodeGenOptions); |
||||
} |
||||
} |
||||
|
||||
// Generate user classes
|
||||
foreach (Class klass in Classes) { |
||||
var cu = new CodeCompileUnit (); |
||||
var ns = new CodeNamespace (Namespace); |
||||
ns.Imports.Add(new CodeNamespaceImport("System")); |
||||
ns.Imports.Add(new CodeNamespaceImport("Mono.VisualC.Interop")); |
||||
cu.Namespaces.Add (ns); |
||||
|
||||
ns.Types.Add (klass.GenerateClass (this, libDecl)); |
||||
|
||||
//Provider.GenerateCodeFromCompileUnit(cu, Console.Out, CodeGenOptions);
|
||||
using (TextWriter w = File.CreateText (Path.Combine (OutputDir, klass.Name + ".cs"))) { |
||||
// These are reported for the fields of the native layout structures
|
||||
Provider.GenerateCodeFromCompileUnit (new CodeSnippetCompileUnit("#pragma warning disable 0414, 0169"), w, CodeGenOptions); |
||||
Provider.GenerateCodeFromCompileUnit(cu, w, CodeGenOptions); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,893 +0,0 @@
@@ -1,893 +0,0 @@
|
||||
using System; |
||||
using NDesk.Options; |
||||
using System.IO; |
||||
using System.Text; |
||||
using System.Text.RegularExpressions; |
||||
using System.Xml; |
||||
using System.Xml.XPath; |
||||
using System.Xml.Linq; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using Mono.VisualC.Interop; |
||||
using Mono.VisualC.Interop.ABI; |
||||
using Mono.VisualC.Interop.Util; |
||||
using Mono.VisualC.Code; |
||||
using Mono.VisualC.Code.Atoms; |
||||
|
||||
using System.Reflection; |
||||
using System.CodeDom; |
||||
using System.CodeDom.Compiler; |
||||
using Microsoft.CSharp; |
||||
|
||||
namespace Mono.VisualC.Tools.Generator { |
||||
public class Generator { |
||||
public static readonly string [] genericTypeArgs = new string [] { "T", "U", "V", "W", "X", "Y", "Z" }; |
||||
|
||||
public string Source { get; set; } |
||||
public string Dir { get; set; } |
||||
public bool AbiTest { get; set; } |
||||
public bool Validation { get; set; } |
||||
public string Library { get; set; } |
||||
|
||||
private string nspace; |
||||
public string Namespace { |
||||
get { return nspace; } |
||||
set { |
||||
nspace = value; |
||||
Tree.ManagedNamespace = value; |
||||
//enumerations.ManagedNamespace = value;
|
||||
//unions.ManagedNamespace = value;
|
||||
} |
||||
} |
||||
|
||||
public Dictionary<string, string> Classes; |
||||
public HashSet<string> UnknownTypes; |
||||
|
||||
public CodeDomProvider Provider { get; set; } |
||||
public CodeGeneratorOptions Options { get; set; } |
||||
|
||||
private CodeUnit Tree { get; set; } |
||||
//private CodeUnit currentUnit;
|
||||
//private CodeUnit enumerations;
|
||||
//private CodeUnit unions;
|
||||
private Dictionary<string, Property> properties; |
||||
|
||||
private int enumCount = 0; |
||||
private int unionCount = 0; |
||||
|
||||
private HashSet<string> fileList; |
||||
|
||||
public static void Main (string [] args) |
||||
{ |
||||
bool help = false; |
||||
Generator gen = new Generator (); |
||||
gen.Validation = true; |
||||
var p = new OptionSet { { "h|?|help", "Show this help message", v => help = v != null }, { "f=", "Set the xml outputted by gccxml to parse", v => gen.Source = v }, { "o=", "Set the output directory", v => gen.Dir = v }, { "ns=", "Set the namespace", v => gen.Namespace = v }, { "lib=", "Additional reference libraries", v => gen.Library = v }, { "lang=", "Language of the outputted library (C#, VB, etc)", v => gen.Provider = CodeDomProvider.CreateProvider (v) }, { "testabi", "Test the Itanium name mangling abi against the bindings", v => gen.AbiTest = v != null }, { "n", "Don't validate", v => gen.Validation = !(v != null) } }; |
||||
|
||||
List<string> extra = null; |
||||
try { |
||||
extra = p.Parse (args); |
||||
} catch (OptionException) { |
||||
Console.WriteLine ("Try `generator --help' for more information."); |
||||
return; |
||||
} |
||||
|
||||
if (help) { |
||||
p.WriteOptionDescriptions (Console.Error); |
||||
return; |
||||
} |
||||
|
||||
if (gen.Source == null) { |
||||
Console.Error.WriteLine ("-f required"); |
||||
return; |
||||
} |
||||
|
||||
if (gen.Dir == null) |
||||
gen.Dir = "output"; |
||||
Directory.CreateDirectory (gen.Dir); |
||||
|
||||
if (gen.Namespace == null) |
||||
gen.Namespace = "Wrappers"; |
||||
|
||||
if (gen.Library == null) |
||||
gen.Library = "Lib"; |
||||
if (gen.Library.EndsWith (".dll") || gen.Library.EndsWith (".so") || gen.Library.EndsWith (".dylib")) |
||||
gen.Library = gen.Library.Substring (0, gen.Library.LastIndexOf ('.')); |
||||
|
||||
if (gen.Provider == null) |
||||
gen.Provider = new CSharpCodeProvider (); |
||||
|
||||
gen.Options = new CodeGeneratorOptions { BlankLinesBetweenMembers = false }; |
||||
|
||||
gen.Run (); |
||||
} |
||||
|
||||
public Generator () |
||||
{ |
||||
Classes = new Dictionary<string, string> (); |
||||
UnknownTypes = new HashSet<string> (); |
||||
|
||||
Tree = new CodeUnit { ManagedNamespace = Namespace }; |
||||
//enumerations = new CodeUnit { ManagedNamespace = Namespace };
|
||||
//unions = new CodeUnit { ManagedNamespace = Namespace };
|
||||
properties = new Dictionary<string, Property> (); |
||||
|
||||
fileList = new HashSet<string> (); |
||||
} |
||||
|
||||
public void Run () |
||||
{ |
||||
Console.WriteLine ("Generating bindings..."); |
||||
|
||||
Console.WriteLine ("\tLoading XML"); |
||||
XmlDocument xmldoc = new XmlDocument (); |
||||
xmldoc.Load (Source); |
||||
|
||||
Console.WriteLine ("\tPreprocessing"); |
||||
List<Entry> entries = Preprocess (xmldoc); |
||||
|
||||
Console.WriteLine ("\tPreprocessing Classes"); |
||||
List<Entry> removed = PreprocessClasses (entries); |
||||
|
||||
Console.WriteLine ("\tProcessing Classes"); |
||||
ProcessClasses (entries, removed); |
||||
|
||||
// save files
|
||||
Console.WriteLine ("\tSaving Classes"); |
||||
Save (); |
||||
|
||||
Console.WriteLine ("\tGenerating Static lib"); |
||||
GenerateStaticLibField (); |
||||
|
||||
Console.WriteLine ("Generating unknown types"); |
||||
GenerateUnknownTypeStubs (); |
||||
|
||||
|
||||
if (Validation) { |
||||
Console.WriteLine ("Validating bindings..."); |
||||
Assembly asm = Validate (); |
||||
|
||||
if (asm != null && AbiTest) { |
||||
Console.WriteLine ("Testing Itanium ABI name mangling against bindings..."); |
||||
TestAbi (asm); |
||||
} |
||||
} |
||||
//File.Delete (fileList.Where (f => f.StartsWith ("UnknownTypes")).Single ());
|
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
public void Save () |
||||
{ |
||||
//if (enumerations.Atoms.Any ())
|
||||
// SaveFile (enumerations.WrapperToCodeDom (Provider), "Enums");
|
||||
//if (unions.Atoms.Any ())
|
||||
// SaveFile (unions.WrapperToCodeDom (Provider), "Unions");
|
||||
|
||||
SaveFile (Tree.WrapperToCodeDom (Provider), "FlatFile"); |
||||
} |
||||
|
||||
|
||||
/***************************************************************************/ |
||||
|
||||
|
||||
List<Entry> Preprocess (XmlDocument xmldoc) |
||||
{ |
||||
XmlNode node = xmldoc.SelectSingleNode ("/GCC_XML"); |
||||
Entry.typelist = new Dictionary<string, Dictionary<string, Entry>> (); |
||||
Entry.idlist = new Dictionary<string, Entry> (); |
||||
return Preprocess (node); |
||||
} |
||||
|
||||
int typecount = 0; |
||||
List<Entry> Preprocess (XmlNode parent) |
||||
{ |
||||
List<Entry> data = new List<Entry> (); |
||||
foreach (XmlNode node in parent.ChildNodes) { |
||||
Dictionary<string, string> entry = new Dictionary<string, string> (); |
||||
foreach (XmlAttribute att in node.Attributes) { |
||||
entry[att.Name] = att.Value; |
||||
} |
||||
|
||||
Entry e = new Entry { id = "", type = node.Name, name = "", reftype = "", attributes = entry }; |
||||
|
||||
if (entry.ContainsKey ("name")) |
||||
e.id = e.name = entry["name"]; |
||||
|
||||
if (entry.ContainsKey ("id")) |
||||
e.id = entry["id"]; |
||||
|
||||
if (entry.ContainsKey ("type")) |
||||
e.reftype = entry["type"]; |
||||
|
||||
if (node.HasChildNodes) |
||||
e.children = Preprocess (node); |
||||
else |
||||
e.children = new List<Entry> (); |
||||
|
||||
if (!Entry.typelist.ContainsKey (e.type)) |
||||
Entry.typelist[e.type] = new Dictionary<string, Entry> (); |
||||
|
||||
if (entry.ContainsKey ("id")) |
||||
Entry.idlist.Add (e.id, e); |
||||
|
||||
string id = e.id; |
||||
int count = 0; |
||||
while (Entry.typelist[e.type].ContainsKey (id)) { |
||||
id = e.id + "|" + count++; |
||||
} |
||||
Entry.typelist[e.type].Add (id, e); |
||||
data.Add (e); |
||||
} |
||||
return data; |
||||
} |
||||
|
||||
List<Entry> PreprocessClasses (List<Entry> entries) |
||||
{ |
||||
List<string> filter = new List<string>() { |
||||
"QItemSelection" |
||||
}; |
||||
|
||||
|
||||
List<Entry> removed = new List<Entry> (entries.Where (o => (o.type == "Class" || o.type == "Struct") && (o.IsTrue ("incomplete") || !o.HasValue ("name") || (Entry.idlist[o["file"]].name.StartsWith ("/"))))); |
||||
removed.AddRange (entries.Where (o => filter.Contains (o.name)).ToList ()); |
||||
entries.RemoveAll (o => (o.type == "Class" || o.type == "Struct") && (o.IsTrue ("incomplete") || !o.HasValue ("name") || (Entry.idlist[o["file"]].name.StartsWith ("/")))); |
||||
entries.RemoveAll (o => filter.Contains (o.name)); |
||||
|
||||
|
||||
foreach (Entry clas in entries.Where (o => o.type == "Class" || o.type == "Struct")) { |
||||
clas.computedName = clas.name; |
||||
|
||||
string ns = GetNamespace (clas); |
||||
CppType currentType = new CppType (clas.type.ToLower (), ns != null ? ns + "::" + clas.computedName : clas.computedName); |
||||
|
||||
// FIXME: better way to do this (GCC-XML output doesn't seem to leave much choice)
|
||||
var templated = currentType.Modifiers.OfType<CppModifiers.TemplateModifier> ().SingleOrDefault (); |
||||
if (templated != null) { |
||||
clas.isTemplate = true; |
||||
string baseName = currentType.ElementTypeName; |
||||
string computedName = clas.computedName; |
||||
clas.computedName = baseName; |
||||
|
||||
clas.atom = new Class (clas.computedName) { |
||||
StaticCppLibrary = string.Format ("{0}.Libs.{1}", Namespace, Library), |
||||
OriginalType = currentType |
||||
}; |
||||
|
||||
int i = 0; |
||||
foreach (var t in templated.Types) |
||||
clas.Class.TemplateArguments.Add (genericTypeArgs[i++]); |
||||
|
||||
clas.Class.GenericName = String.Format ("{0}<{1}>", baseName, string.Join (",", clas.Class.TemplateArguments.ToArray ())); |
||||
Console.Error.WriteLine ("Warning: Creating generic type {0}<{1}> from the instantiated template {2} (very buggy!!!)", |
||||
baseName, |
||||
string.Join (",", clas.Class.TemplateArguments.ToArray ()), computedName); |
||||
} else { |
||||
clas.atom = new Class (clas.computedName) { |
||||
StaticCppLibrary = string.Format ("{0}.Libs.{1}", Namespace, Library), |
||||
OriginalType = currentType |
||||
}; |
||||
} |
||||
|
||||
// FIXME: Handle when base class name is fully qualified
|
||||
foreach (Entry bs in clas.children.Where (o => o.type == "Base")) { |
||||
clas.Class.Bases.Add (new Class.BaseClass { Name = bs.Base.Class != null ? (bs.Base.Class.GenericName ?? bs.Base.Class.Name) : bs.computedName, Access = bs.CheckValue ("access", "public") ? Access.Public : bs.CheckValue ("access", "protected") ? Access.Protected : Access.Private, IsVirtual = bs.IsTrue ("virtual") }); |
||||
} |
||||
|
||||
IEnumerable<CodeAtom> nested = null; |
||||
if (IsCreated (clas.Class.OriginalType, true, out nested)) |
||||
continue; |
||||
|
||||
GetContainer (clas, Tree).Atoms.AddLast (clas.atom); |
||||
if (nested != null) { |
||||
foreach (var orphan in nested) |
||||
clas.Class.Atoms.AddLast (orphan); |
||||
} |
||||
} |
||||
|
||||
return removed; |
||||
} |
||||
|
||||
void ProcessClasses (List<Entry> entries, List<Entry> removed) |
||||
{ |
||||
foreach (Entry clas in entries.Where (o => o.type == "Class" || o.type == "Struct")) { |
||||
|
||||
var templated = clas.Class.OriginalType.Modifiers.OfType<CppModifiers.TemplateModifier> ().SingleOrDefault (); |
||||
Dictionary<MethodSignature, string> methods = new Dictionary<MethodSignature, string> (); |
||||
|
||||
foreach (Entry member in clas.Members) { |
||||
bool ctor = false; |
||||
bool dtor = false; |
||||
int fieldCount = 0; |
||||
|
||||
switch (member.type) { |
||||
case "Constructor": |
||||
ctor = true; |
||||
break; |
||||
case "Destructor": |
||||
dtor = true; |
||||
break; |
||||
case "Method": |
||||
break; |
||||
case "Field": |
||||
CppType fieldType = findType (clas.Class, member.Base, removed); |
||||
string fieldName = "field" + fieldCount++; |
||||
if (member.name != "") |
||||
fieldName = member.name; |
||||
clas.Class.Atoms.AddLast (new Field (fieldName, fieldType)); |
||||
continue; |
||||
default: |
||||
continue; |
||||
} |
||||
|
||||
// Now we're processing a method...
|
||||
|
||||
if (!member.CheckValue ("access", "public") || (member.HasValue ("overrides") && !dtor) || !member.IsTrue ("extern")) |
||||
continue; |
||||
|
||||
string mname = member.name; |
||||
|
||||
CppType retType = member.HasValue ("returns") ? findType (clas.Class, Entry.idlist[member["returns"]], removed) : CppTypes.Void; |
||||
|
||||
if (templated != null) { |
||||
for (int i = 0; i < templated.Types.Length; i++) |
||||
retType = replaceType (retType, templated.Types[i], genericTypeArgs[i]); |
||||
} |
||||
|
||||
var methodAtom = new Method (dtor ? "Destruct" : mname) { |
||||
RetType = retType, |
||||
IsVirtual = member.IsTrue ("virtual"), |
||||
IsStatic = member.IsTrue ("static"), |
||||
IsConst = member.IsTrue ("const"), |
||||
IsConstructor = ctor, |
||||
IsDestructor = dtor, |
||||
Klass = clas.Class |
||||
}; |
||||
|
||||
if (AbiTest) |
||||
methodAtom.Mangled = new NameTypePair<Type> { Name = member.attributes["mangled"], Type = typeof(ItaniumAbi) }; |
||||
|
||||
List<CppType> argTypes = new List<CppType> (); |
||||
int c = 0; |
||||
foreach (Entry arg in member.children.Where (o => o.type == "Argument")) { |
||||
string argname; |
||||
if (arg.name == "") |
||||
argname = "arg" + c; |
||||
else |
||||
argname = arg.name; |
||||
|
||||
CppType argtype = findType (clas.Class, arg.Base, removed); |
||||
if (templated != null) { |
||||
for (int i = 0; i < templated.Types.Length; i++) |
||||
argtype = replaceType (argtype, templated.Types[i], genericTypeArgs[i]); |
||||
} |
||||
|
||||
methodAtom.Parameters.Add (new NameTypePair<CppType> { Name = argname, Type = argtype }); |
||||
argTypes.Add (argtype); |
||||
|
||||
// tee hee
|
||||
c++; |
||||
} |
||||
|
||||
|
||||
// Try to filter out duplicate methods
|
||||
MethodSignature sig = new MethodSignature (methodAtom.FormattedName, argTypes); |
||||
string conflictingSig; |
||||
if (methods.TryGetValue (sig, out conflictingSig)) { |
||||
// FIXME: add comment to explain why it's commented out
|
||||
string demangled = member.attributes["demangled"]; |
||||
//Console.Error.WriteLine ("Warning: Method \"{0}\" in class {1} omitted because it conflicts with \"{2}\"", demangled, name, conflictingSig);
|
||||
methodAtom.Comment = string.Format ("FIXME:Method \"{0}\" omitted because it conflicts with \"{1}\"", demangled, conflictingSig); |
||||
methodAtom.CommentedOut = true; |
||||
} else |
||||
methods.Add (sig, member.attributes["demangled"]); |
||||
|
||||
// Record unknown types in parameters and return type
|
||||
IsCreated (methodAtom.RetType); |
||||
foreach (var arg in methodAtom.Parameters) |
||||
IsCreated (arg.Type); |
||||
|
||||
// Detect if this is a property...
|
||||
|
||||
// if it's const, returns a value, has no parameters, and there is no other method with the same name
|
||||
// in this class assume it's a property getter (for now?)
|
||||
if (methodAtom.IsConst && !retType.Equals (CppTypes.Void) && !methodAtom.Parameters.Any () && |
||||
clas.Members.Where (o => o.name == mname).FirstOrDefault () == null) { |
||||
var pname = methodAtom.FormattedName; |
||||
Property propertyAtom; |
||||
|
||||
if (properties.TryGetValue (pname, out propertyAtom)) { |
||||
propertyAtom.Getter = methodAtom; |
||||
} else { |
||||
propertyAtom = new Property (pname) { Getter = methodAtom }; |
||||
properties.Add (pname, propertyAtom); |
||||
clas.Class.Atoms.AddLast (propertyAtom); |
||||
} |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// if it's name starts with "set", does not return a value, and has one arg (besides this ptr)
|
||||
// and there is no other method with the same name...
|
||||
if (mname.ToLower ().StartsWith ("set") && retType.Equals (CppTypes.Void) && |
||||
methodAtom.Parameters.Count == 1 && clas.Members.Where (o => o.name == mname).FirstOrDefault () == null) { |
||||
|
||||
string getterName = mname.Substring (3).TrimStart ('_').ToLower (); |
||||
|
||||
string pname = methodAtom.FormattedName.Substring (3); |
||||
Property propertyAtom = null; |
||||
|
||||
// ...AND there is a corresponding getter method that returns the right type, then assume it's a property setter
|
||||
bool doIt = false; |
||||
if (properties.TryGetValue (pname, out propertyAtom)) { |
||||
doIt = propertyAtom.Getter != null && propertyAtom.Getter.RetType.Equals (methodAtom.Parameters[0].Type); |
||||
} else { |
||||
Entry getter = clas.Members.Where (o => o.name == getterName).FirstOrDefault (); |
||||
doIt = (getter != null && findType (clas.Class, Entry.idlist[getter.attributes["returns"]], removed).Equals (methodAtom.Parameters[0].Type)); |
||||
} |
||||
if (doIt) { |
||||
if (propertyAtom != null) { |
||||
propertyAtom.Setter = methodAtom; |
||||
} else { |
||||
propertyAtom = new Property (pname) { Setter = methodAtom }; |
||||
properties.Add (pname, propertyAtom); |
||||
clas.Class.Atoms.AddLast (propertyAtom); |
||||
} |
||||
|
||||
// set the method's arg name to "value" so that the prop setter works right
|
||||
var valueParam = methodAtom.Parameters[0]; |
||||
valueParam.Name = "value"; |
||||
methodAtom.Parameters[0] = valueParam; |
||||
|
||||
continue; |
||||
} |
||||
} |
||||
clas.Class.Atoms.AddLast (methodAtom); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Assembly Validate () |
||||
{ |
||||
var compileParams = new CompilerParameters { GenerateInMemory = true, IncludeDebugInformation = true, WarningLevel = 0, TreatWarningsAsErrors = false }; |
||||
compileParams.ReferencedAssemblies.Add (typeof(CppLibrary).Assembly.Location); |
||||
|
||||
CompilerResults results = Provider.CompileAssemblyFromFile (compileParams, fileList.ToArray ()); |
||||
var errors = results.Errors.Cast<CompilerError> ().Where (e => !e.IsWarning); |
||||
|
||||
int count = 0; |
||||
foreach (var error in errors) { |
||||
|
||||
if (count == 0) { |
||||
foreach (var fix in Postfixes.List) { |
||||
if (fix.TryFix (error.ErrorText, this)) |
||||
return Validate (); |
||||
} |
||||
} |
||||
|
||||
Console.Error.WriteLine ("{0}({1},{2}): error {3}: {4}", error.FileName, error.Line, error.Column, error.ErrorNumber, error.ErrorText); |
||||
count++; |
||||
} |
||||
if (count > 0) |
||||
Console.Error.WriteLine ("Validation failed with {0} compilation errors.", count); |
||||
else |
||||
Console.WriteLine ("Bindings compiled successfully."); |
||||
|
||||
|
||||
return results.CompiledAssembly; |
||||
} |
||||
|
||||
void TestAbi (Assembly assembly) |
||||
{ |
||||
var classes = assembly.GetExportedTypes ().Select (t => new { Name = Regex.Replace (t.Name, "\\`.$", ""), Interface = t.GetNestedType ("I" + t.Name) }).Where (k => k.Interface != null); |
||||
var abi = new ItaniumAbi (); |
||||
|
||||
foreach (var klass in classes) { |
||||
bool klassSuccess = true; |
||||
|
||||
foreach (var method in klass.Interface.GetMethods ()) { |
||||
|
||||
var testAttribute = (AbiTestAttribute)method.GetCustomAttributes (typeof(AbiTestAttribute), false).FirstOrDefault (); |
||||
|
||||
string expected = testAttribute.MangledName; |
||||
string actual = abi.GetMangledMethodName (klass.Name, method); |
||||
|
||||
if (actual != expected) { |
||||
Console.Error.WriteLine ("Error: Expected \"{0}\" but got \"{1}\" when mangling method \"{2}\" in class \"{3}\"", expected, actual, method.ToString (), klass.Name); |
||||
klassSuccess = false; |
||||
} |
||||
} |
||||
|
||||
if (klassSuccess) |
||||
Console.WriteLine ("Successfully mangled all method names in class \"" + klass.Name + "\""); |
||||
} |
||||
|
||||
} |
||||
|
||||
string GetNamespace (Entry entry) |
||||
{ |
||||
string nsname; |
||||
if (Entry.idlist[entry["context"]] == null) |
||||
return null; |
||||
|
||||
Entry ns = entry.Namespace; |
||||
if (ns.type == "Namespace") |
||||
nsname = ns.name; else if (ns.type == "Class" || ns.type == "Struct") |
||||
nsname = new CppType (ns.name).ElementTypeName; |
||||
else |
||||
throw new NotSupportedException ("Unknown context: " + ns.type); |
||||
|
||||
if (nsname == "::") |
||||
return null; |
||||
|
||||
string parent = GetNamespace (ns); |
||||
if (parent != null) |
||||
return parent + "::" + nsname; |
||||
|
||||
return nsname; |
||||
} |
||||
|
||||
CodeContainer GetContainer (Entry entry, CodeContainer def) |
||||
{ |
||||
string nsname; |
||||
if (Entry.idlist[entry.attributes["context"]] == null) |
||||
return null; |
||||
|
||||
Entry ns = entry.Namespace; |
||||
|
||||
if (ns.type == "Namespace") |
||||
nsname = ns.name; else if (ns.type == "Class" || ns.type == "Struct") |
||||
nsname = new CppType (ns.name).ElementTypeName; |
||||
else |
||||
throw new NotSupportedException ("Unknown context: " + ns.type); |
||||
|
||||
if (nsname == "::") |
||||
return def; |
||||
CodeContainer parent = GetContainer (ns, def); |
||||
CodeContainer container = parent.Atoms.OfType<CodeContainer> ().Where (a => a.Name == nsname).SingleOrDefault (); |
||||
|
||||
if (container == null) { |
||||
container = new Namespace (nsname); |
||||
parent.Atoms.AddLast (container); |
||||
} |
||||
|
||||
return container; |
||||
} |
||||
|
||||
public CodeContainer GetPath (string [] pathNames) |
||||
{ |
||||
return GetPath (Tree, pathNames, false); |
||||
} |
||||
|
||||
static CodeContainer GetPath (CodeContainer root, string [] pathNames, bool create) |
||||
{ |
||||
foreach (var item in pathNames) { |
||||
CodeContainer prospect = root.Atoms.OfType<CodeContainer> ().Where (a => a.Name == item).SingleOrDefault (); |
||||
if (prospect == null) { |
||||
if (create) { |
||||
prospect = new Namespace (item); |
||||
root.Atoms.AddLast (prospect); |
||||
} else |
||||
return null; |
||||
} |
||||
|
||||
root = prospect; |
||||
} |
||||
|
||||
return root; |
||||
} |
||||
|
||||
CppType ProcessEnum (Entry entry) |
||||
{ |
||||
string fullName = entry.name; |
||||
if (entry.name == "") { |
||||
fullName = "Enum" + enumCount++; |
||||
string ns = GetNamespace (entry); |
||||
if (ns != null) |
||||
fullName = ns + "::" + entry.name; |
||||
} |
||||
CppType enumType = new CppType (CppTypes.Enum, fullName); |
||||
|
||||
IEnumerable<CodeAtom> dontCare; |
||||
if (entry.name == "" || !IsCreated (enumType, true, out dontCare)) { |
||||
|
||||
Enumeration enumAtom = new Enumeration (fullName); |
||||
foreach (Entry m in entry.children.Where (o => o.type == "EnumValue")) { |
||||
enumAtom.Items.Add (new Enumeration.Item { Name = m.name, Value = Convert.ToInt32 (m.attributes["init"]) }); |
||||
} |
||||
|
||||
CodeContainer nested = GetContainer (entry, Tree); |
||||
//GetContainer (root, enm, currentUnit);
|
||||
nested.Atoms.AddLast (enumAtom); |
||||
} |
||||
|
||||
return enumType; |
||||
} |
||||
|
||||
CppType ProcessUnion (Class clas, Entry entry, List<Entry> removed) |
||||
{ |
||||
string fullName = entry.name; |
||||
if (entry.name == "") { |
||||
fullName = "Union" + unionCount++; |
||||
string ns = GetNamespace (entry); |
||||
if (ns != null) |
||||
fullName = ns + "::" + entry.name; |
||||
} |
||||
|
||||
CppType unionType = new CppType (CppTypes.Union, fullName); |
||||
|
||||
IEnumerable<CodeAtom> orphans = null; |
||||
if (entry.name == "" || !IsCreated (unionType, true, out orphans)) { |
||||
Union unionAtom = new Union (fullName); |
||||
|
||||
foreach (Entry m in entry.Members.Where (o => o.type == "Field")) { |
||||
Field field = new Field (m.name, findType (clas, m.Base, removed)); |
||||
unionAtom.Atoms.AddLast (field); |
||||
} |
||||
|
||||
CodeContainer nested = GetContainer (entry, Tree); |
||||
//GetContainer (root, union, currentUnit);
|
||||
nested.Atoms.AddLast (unionAtom); |
||||
|
||||
if (orphans != null) { |
||||
foreach (var orphan in orphans) |
||||
unionAtom.Atoms.AddLast (orphan); |
||||
} |
||||
} |
||||
return unionType; |
||||
} |
||||
|
||||
void GenerateStaticLibField () |
||||
{ |
||||
string name = "Lib_" + Library; |
||||
if (File.Exists (Path.Combine (Dir, fname (name)))) |
||||
return; |
||||
|
||||
var ccu = new CodeCompileUnit (); |
||||
var ns = new CodeNamespace (Namespace); |
||||
var cls = new CodeTypeDeclaration ("Libs") { TypeAttributes = TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.Class, IsPartial = true, IsClass = true }; |
||||
var field = new CodeMemberField (typeof(CppLibrary), Library) { Attributes = MemberAttributes.Public | MemberAttributes.Static, InitExpression = new CodeObjectCreateExpression (typeof(CppLibrary), new CodePrimitiveExpression (Library)) }; |
||||
cls.Members.Add (field); |
||||
ns.Types.Add (cls); |
||||
ccu.Namespaces.Add (ns); |
||||
|
||||
SaveFile (ccu, name); |
||||
} |
||||
|
||||
void GenerateUnknownTypeStubs () |
||||
{ |
||||
/* |
||||
var ccu = new CodeCompileUnit (); |
||||
var ns = new CodeNamespace (Namespace); |
||||
|
||||
foreach (var type in UnknownTypes) { |
||||
var ctd = type.Value as CodeTypeDeclaration; |
||||
var newNS = type.Value as CodeNamespace; |
||||
|
||||
if (ctd != null) |
||||
ns.Types.Add (ctd); |
||||
else |
||||
ccu.Namespaces.Add (newNS); |
||||
} |
||||
|
||||
ccu.Namespaces.Add (ns); |
||||
SaveFile (ccu, "UnknownTypes"); |
||||
*/ |
||||
var ukt = UnknownTypes.ToArray (); |
||||
foreach (var unknownType in ukt) { |
||||
CodeContainer container = Tree; |
||||
CppType type = new CppType (unknownType); |
||||
IEnumerable<CodeAtom> orphans; |
||||
|
||||
if (IsCreated (type, true, out orphans)) |
||||
continue; |
||||
|
||||
if (type.Namespaces != null) |
||||
container = GetPath (Tree, type.Namespaces, true); |
||||
|
||||
var atom = new Class (type.ElementTypeName); |
||||
|
||||
int i = 0; |
||||
foreach (var param in type.Modifiers.OfType<CppModifiers.TemplateModifier> ()) { |
||||
if (param.Types != null) { |
||||
foreach (var t in param.Types) { |
||||
atom.TemplateArguments.Add (genericTypeArgs[i++]); |
||||
} |
||||
} else { |
||||
atom.TemplateArguments.Add (genericTypeArgs[i++]); |
||||
} |
||||
} |
||||
|
||||
if (orphans != null) { |
||||
foreach (var orphan in orphans) |
||||
atom.Atoms.AddLast (orphan); |
||||
} |
||||
|
||||
container.Atoms.AddLast (atom); |
||||
} |
||||
} |
||||
|
||||
void SaveFile (CodeCompileUnit ccu, string baseName) |
||||
{ |
||||
string name = Path.Combine (Dir, fname (baseName)); |
||||
if (File.Exists (name) && fileList.Contains (name)) |
||||
File.Delete (name); |
||||
if (File.Exists (name) && !fileList.Contains (name)) { |
||||
int i = 1; |
||||
while (File.Exists (Path.Combine (Dir, fname (baseName + i)))) |
||||
i++; |
||||
name = Path.Combine (Dir, fname (baseName + i)); |
||||
} |
||||
Console.WriteLine ("Generating " + name); |
||||
|
||||
var sw = File.CreateText (name); |
||||
Provider.GenerateCodeFromCompileUnit (ccu, sw, Options); |
||||
sw.Flush (); |
||||
sw.Close (); |
||||
sw.Dispose (); |
||||
|
||||
if (!fileList.Contains (name)) |
||||
fileList.Add (name); |
||||
} |
||||
|
||||
static CppType replaceType (CppType inType, CppType toReplace, string tn) |
||||
{ |
||||
// FIXME: The order of some modifiers is not significant.. is this a problem?
|
||||
if ( /* inType.ElementType == toReplace.ElementType && */((inType.Namespaces != null && toReplace.Namespaces != null && inType.Namespaces.SequenceEqual (toReplace.Namespaces)) || inType.Namespaces == null && toReplace.Namespaces == null) && inType.ElementTypeName == toReplace.ElementTypeName && inType.Modifiers.StartsWith (toReplace.Modifiers)) |
||||
return new CppType (CppTypes.Typename, tn, inType.Modifiers.Skip (toReplace.Modifiers.Count).ToArray ()); |
||||
|
||||
foreach (var tempMod in inType.Modifiers.OfType<CppModifiers.TemplateModifier> ()) |
||||
for (int i = 0; i < tempMod.Types.Length; i++) |
||||
tempMod.Types[i] = replaceType (tempMod.Types[i], toReplace, tn); |
||||
|
||||
return inType; |
||||
} |
||||
|
||||
// returns true if the type was already created
|
||||
bool IsCreated (CppType type) |
||||
{ |
||||
IEnumerable<CodeAtom> dontCare; |
||||
return IsCreated (type, false, out dontCare); |
||||
} |
||||
bool IsCreated (CppType type, bool toBeCreated, out IEnumerable<CodeAtom> orphanedAtoms) |
||||
{ |
||||
orphanedAtoms = null; |
||||
if (type.ElementTypeName == null || type.ElementTypeName == "" || type.ElementType == CppTypes.Typename) |
||||
return true; |
||||
|
||||
// check template parameters recursively
|
||||
foreach (var paramType in type.Modifiers.OfType<CppModifiers.TemplateModifier> ().Where (t => t.Types != null).SelectMany (t => t.Types)) |
||||
IsCreated (paramType); |
||||
|
||||
bool typeFound = false; |
||||
type.ElementType = CppTypes.Unknown; |
||||
for (int i = 0; i < type.Modifiers.Count; i++) { |
||||
if (type.Modifiers[i] != CppModifiers.Template) |
||||
type.Modifiers.RemoveAt (i); |
||||
} |
||||
|
||||
string qualifiedName = type.ToString (); |
||||
bool alreadyUnknown = UnknownTypes.Contains (qualifiedName); |
||||
|
||||
CodeContainer place = Tree; |
||||
if (type.Namespaces != null) |
||||
place = GetPath (type.Namespaces); |
||||
|
||||
if (place != null) { |
||||
typeFound = place.Atoms.OfType<Class> ().Where (c => c.Name == type.ElementTypeName).Any () || place.Atoms.OfType<Enumeration> ().Where (e => e.Name == type.ElementTypeName).Any () || place.Atoms.OfType<Union> ().Where (u => u.Name == type.ElementTypeName).Any (); |
||||
|
||||
var sameNamedNamespace = place.Atoms.OfType<Namespace> ().Where (ns => ns.Name == type.ElementTypeName).SingleOrDefault (); |
||||
if (sameNamedNamespace != null) { |
||||
orphanedAtoms = sameNamedNamespace.Atoms; |
||||
|
||||
// FIXME: This could potentially be very slow
|
||||
Tree.Atoms.Remove (sameNamedNamespace); |
||||
//currentUnit.Atoms.Remove (sameNamedNamespace);
|
||||
//enumerations.Atoms.Remove (sameNamedNamespace);
|
||||
//unions.Atoms.Remove (sameNamedNamespace);
|
||||
} |
||||
} |
||||
|
||||
if (!typeFound && !toBeCreated && !alreadyUnknown) { |
||||
/*CodeObject codeObject; |
||||
|
||||
var ctd = new CodeTypeDeclaration (type.ElementTypeName) { |
||||
TypeAttributes = TypeAttributes.Public, |
||||
IsClass = true |
||||
}; |
||||
|
||||
codeObject = ctd; |
||||
|
||||
if (type.Namespaces != null) { |
||||
var ns = new CodeNamespace (Namespace + "." + string.Join (".", type.Namespaces)); |
||||
ns.Types.Add (ctd); |
||||
codeObject = ns; |
||||
} |
||||
|
||||
var template = type.Modifiers.OfType<CppModifiers.TemplateModifier> ().SingleOrDefault (); |
||||
if (template != null) { |
||||
for (int i = 0; i < template.Types.Length; i++) |
||||
ctd.TypeParameters.Add (genericTypeArgs [i]); |
||||
} |
||||
*/ |
||||
UnknownTypes.Add (qualifiedName); |
||||
} else if ((typeFound || toBeCreated) && alreadyUnknown) |
||||
UnknownTypes.Remove (qualifiedName); |
||||
|
||||
return typeFound; |
||||
} |
||||
|
||||
CppType findType (Class clas, Entry entry, List<Entry> removed) |
||||
{ |
||||
if (entry == null) |
||||
return CppTypes.Unknown; |
||||
return findType (clas, entry, removed, new CppType ()); |
||||
} |
||||
|
||||
CppType findType (Class clas, Entry entry, List<Entry> removed, CppType modifiers) |
||||
{ |
||||
if (entry == null) |
||||
return CppTypes.Unknown; |
||||
string name = entry.name == "" ? "unknown" : entry.name; |
||||
|
||||
switch (entry.type) { |
||||
case "ArrayType": |
||||
return findType (clas, entry.Base, removed, modifiers.Modify (CppModifiers.Array)); |
||||
case "PointerType": |
||||
return findType (clas, entry.Base, removed, modifiers.Modify (CppModifiers.Pointer)); |
||||
case "ReferenceType": |
||||
return findType (clas, entry.Base, removed, modifiers.Modify (CppModifiers.Reference)); |
||||
case "CvQualifiedType": |
||||
return findType (clas, entry.Base, removed, modifiers.Modify ((from c in entry.attributes |
||||
where c.Key == "const" && c.Value == "1" |
||||
select CppModifiers.Const).DefaultIfEmpty (CppModifiers.Volatile).First ())); |
||||
|
||||
case "Typedef": |
||||
return findType (clas, entry.Base, removed, modifiers); |
||||
|
||||
case "FundamentalType": |
||||
return modifiers.CopyTypeFrom (new CppType (name)); |
||||
case "Class": |
||||
if (removed.Contains (entry)) |
||||
return modifiers.CopyTypeFrom (CppTypes.Unknown); |
||||
if (entry.Class.GenericName != null) { |
||||
if (clas == entry.Class) { |
||||
return modifiers.CopyTypeFrom (new CppType (CppTypes.Class, entry.Class.GenericName)); |
||||
} else { |
||||
return modifiers.CopyTypeFrom (CppTypes.Unknown); |
||||
} |
||||
} else |
||||
return modifiers.CopyTypeFrom (new CppType (CppTypes.Class, name)); |
||||
case "Struct": |
||||
if (removed.Contains (entry)) |
||||
return modifiers.CopyTypeFrom (CppTypes.Unknown); |
||||
return modifiers.CopyTypeFrom (new CppType (CppTypes.Struct, name)); |
||||
case "Union": |
||||
if (removed.Contains (entry)) |
||||
return modifiers.CopyTypeFrom (CppTypes.Unknown); |
||||
return modifiers.CopyTypeFrom (ProcessUnion (clas, entry, removed)); |
||||
case "Enumeration": |
||||
if (removed.Contains (entry)) |
||||
return modifiers.CopyTypeFrom (CppTypes.Unknown); |
||||
return modifiers.CopyTypeFrom (ProcessEnum (entry)); |
||||
|
||||
// FIXME: support function pointers betters
|
||||
case "FunctionType": |
||||
return modifiers.CopyTypeFrom (CppTypes.Void); |
||||
case "MethodType": |
||||
return modifiers.CopyTypeFrom (CppTypes.Void); |
||||
case "Constructor": |
||||
return CppTypes.Unknown; |
||||
} |
||||
|
||||
throw new NotImplementedException ("Unknown type node: " + entry.type); |
||||
} |
||||
|
||||
string fname (string name) |
||||
{ |
||||
return name.Replace ("<", "_").Replace (">", "_").Replace (":", "_").Replace ("*", "_").Replace (",", "_").Replace (" ", "_") + "." + Provider.FileExtension; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// Method.cs: Represents a C++ method
|
||||
//
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.CodeDom; |
||||
using System.CodeDom.Compiler; |
||||
|
||||
using Mono.VisualC.Interop; |
||||
|
||||
class Method |
||||
{ |
||||
public Method (Node node) { |
||||
Node = node; |
||||
Parameters = new List<Tuple<string, CppType>> (); |
||||
} |
||||
|
||||
public Node Node { |
||||
get; set; |
||||
} |
||||
|
||||
public string Name { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsVirtual { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsStatic { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsConst { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsInline { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsArtificial { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsConstructor { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsDestructor { |
||||
get; set; |
||||
} |
||||
|
||||
public bool IsCopyCtor { |
||||
get; set; |
||||
} |
||||
|
||||
public CppType ReturnType { |
||||
get; set; |
||||
} |
||||
|
||||
public List<Tuple<string, CppType>> Parameters { |
||||
get; set; |
||||
} |
||||
|
||||
public CodeMemberMethod GenerateIFaceMethod (Generator g) { |
||||
var method = new CodeMemberMethod () { |
||||
Name = Name |
||||
}; |
||||
|
||||
if (!IsStatic) |
||||
method.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference ("CppInstancePtr"), "this")); |
||||
|
||||
CodeTypeReference rtype = g.CppTypeToCodeDomType (ReturnType); |
||||
if (rtype != null) |
||||
method.ReturnType = rtype; |
||||
|
||||
foreach (var p in Parameters) { |
||||
CppType ptype = p.Item2; |
||||
var param = new CodeParameterDeclarationExpression (g.CppTypeToCodeDomType (ptype), p.Item1); |
||||
if (!IsVirtual && !ptype.ToString ().Equals (string.Empty)) |
||||
param.CustomAttributes.Add (new CodeAttributeDeclaration ("MangleAsAttribute", new CodeAttributeArgument (new CodePrimitiveExpression (ptype.ToString ())))); |
||||
// FIXME: Structs too
|
||||
if (ptype.ElementType == CppTypes.Class && !ptype.Modifiers.Contains (CppModifiers.Reference) && !ptype.Modifiers.Contains (CppModifiers.Pointer)) |
||||
param.CustomAttributes.Add (new CodeAttributeDeclaration ("ByVal")); |
||||
method.Parameters.Add (param); |
||||
} |
||||
|
||||
// FIXME: Copy ctor
|
||||
|
||||
if (IsVirtual) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Virtual")); |
||||
if (IsConstructor) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Constructor")); |
||||
if (IsDestructor) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Destructor")); |
||||
if (IsConst) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Const")); |
||||
if (IsInline) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Inline")); |
||||
if (IsArtificial) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Artificial")); |
||||
if (IsCopyCtor) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("CopyConstructor")); |
||||
if (IsStatic) |
||||
method.CustomAttributes.Add (new CodeAttributeDeclaration ("Static")); |
||||
|
||||
return method; |
||||
} |
||||
|
||||
public CodeMemberMethod GenerateWrapperMethod (Generator g) { |
||||
CodeMemberMethod method; |
||||
|
||||
if (IsConstructor) |
||||
method = new CodeConstructor () { |
||||
Name = Name |
||||
}; |
||||
else |
||||
method = new CodeMemberMethod () { |
||||
Name = Name |
||||
}; |
||||
method.Attributes = MemberAttributes.Public; |
||||
if (IsStatic) |
||||
method.Attributes |= MemberAttributes.Static; |
||||
|
||||
CodeTypeReference rtype = g.CppTypeToCodeDomType (ReturnType); |
||||
if (rtype != null) |
||||
method.ReturnType = rtype; |
||||
|
||||
foreach (var p in Parameters) { |
||||
var param = new CodeParameterDeclarationExpression (g.CppTypeToCodeDomType (p.Item2), p.Item1); |
||||
method.Parameters.Add (param); |
||||
} |
||||
|
||||
if (IsConstructor) { |
||||
//this.native_ptr = impl.Alloc(this);
|
||||
method.Statements.Add (new CodeAssignStatement (new CodeFieldReferenceExpression (null, "native_ptr"), new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (null, "impl"), "Alloc"), new CodeExpression [] { new CodeThisReferenceExpression () }))); |
||||
} |
||||
|
||||
// Call the iface method
|
||||
CodeExpression[] args = new CodeExpression [Parameters.Count + (IsStatic ? 0 : 1)]; |
||||
if (!IsStatic) |
||||
args [0] = new CodeFieldReferenceExpression (null, "Native"); |
||||
for (int i = 0; i < Parameters.Count; ++i) |
||||
args [i + (IsStatic ? 0 : 1)] = new CodeArgumentReferenceExpression (Parameters [i].Item1); |
||||
var call = new CodeMethodInvokeExpression (new CodeMethodReferenceExpression (new CodeFieldReferenceExpression (null, "impl"), Name), args); |
||||
|
||||
if (rtype == null || IsConstructor) |
||||
method.Statements.Add (call); |
||||
else |
||||
method.Statements.Add (new CodeMethodReturnStatement (call)); |
||||
|
||||
return method; |
||||
} |
||||
} |
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Node.cs:
|
||||
//
|
||||
//
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
|
||||
//
|
||||
// This class represents an XML node read from the output of gccxml
|
||||
//
|
||||
class Node { |
||||
|
||||
// The XML element type
|
||||
public string Type { |
||||
get; set; |
||||
} |
||||
|
||||
// The value of the 'id' attribute
|
||||
public string Id { |
||||
get; set; |
||||
} |
||||
|
||||
// The value of the 'name' attribute or null
|
||||
public string Name { |
||||
get; set; |
||||
} |
||||
|
||||
// Attributes
|
||||
public Dictionary<string, string> Attributes { |
||||
get; set; |
||||
} |
||||
|
||||
// The children nodes of this node
|
||||
public List<Node> Children { |
||||
get; set; |
||||
} |
||||
|
||||
public string this [string key] { |
||||
get { |
||||
return Attributes [key]; |
||||
} |
||||
} |
||||
|
||||
// Maps ids to nodes
|
||||
public static Dictionary<string, Node> IdToNode = new Dictionary <string, Node> (); |
||||
|
||||
// For an attribute which contains an id, return the corresponding Node
|
||||
public Node NodeForAttr (string attr) { |
||||
return IdToNode [Attributes [attr]]; |
||||
} |
||||
|
||||
public bool IsTrue (string key) { |
||||
return Attributes.ContainsKey (key) && Attributes[key] == "1"; |
||||
} |
||||
|
||||
public bool HasValue (string key) { |
||||
return Attributes.ContainsKey (key) && Attributes[key] != ""; |
||||
} |
||||
|
||||
public bool CheckValue (string key, string name) { |
||||
return Attributes.ContainsKey (key) && Attributes[key] == name; |
||||
} |
||||
} |
Loading…
Reference in new issue