mirror of https://github.com/mono/CppSharp.git
7 changed files with 752 additions and 905 deletions
@ -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 @@ |
|||||||
|
//
|
||||||
|
// 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 @@ |
|||||||
|
//
|
||||||
|
// 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 @@ |
|||||||
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 @@ |
|||||||
|
//
|
||||||
|
// 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 @@ |
|||||||
|
//
|
||||||
|
// 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