Browse Source

Added validation mode to generator that will confirm that bindings compile and that names are correctly mangled. Fixes to generator to fix compile errors with generated bindings, however they do not compile out of the box as yet.

git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@123 a470b8cb-0e6f-1642-1b45-71e107334c4b
pull/1/head
alexander.corrado 16 years ago
parent
commit
3b0a3f418b
  1. 26
      Mono.VisualC.Code/Atoms/Class.cs
  2. 16
      Mono.VisualC.Code/Atoms/Field.cs
  3. 68
      Mono.VisualC.Code/Atoms/Method.cs
  4. 14
      Mono.VisualC.Code/CodeAtom.cs
  5. 28
      Mono.VisualC.Code/CodeDomExtensions.cs
  6. 5
      Mono.VisualC.Code/CodeUnit.cs
  7. 1
      Mono.VisualC.Code/Mono.VisualC.Code.csproj
  8. 9
      Mono.VisualC.Code/NameTypePair.cs
  9. 23
      Mono.VisualC.Interop/Attributes.cs
  10. 4
      Mono.VisualC.Interop/CppType.cs
  11. 334
      generator/Main.cs
  12. 37
      generator/MethodSignature.cs
  13. 1
      generator/generator.csproj

26
Mono.VisualC.Code/Atoms/Class.cs

@ -45,7 +45,7 @@ namespace Mono.VisualC.Code.Atoms {
return wrapper; return wrapper;
} }
private CodeTypeDeclaration CreateWrapperClass () public CodeTypeDeclaration CreateWrapperClass ()
{ {
var wrapper = new CodeTypeDeclaration (Name) { TypeAttributes = TypeAttributes.Public }; var wrapper = new CodeTypeDeclaration (Name) { TypeAttributes = TypeAttributes.Public };
foreach (var arg in TemplateArguments) foreach (var arg in TemplateArguments)
@ -102,18 +102,25 @@ namespace Mono.VisualC.Code.Atoms {
atom.Visit (wrapper); atom.Visit (wrapper);
} }
if (dispose == null) if (dispose == null) {
wrapper.Members.Add (CreateDestructorlessDispose ()); dispose = CreateDestructorlessDispose ();
else if (hasOverrides) wrapper.Members.Add (dispose);
}
if (hasOverrides)
dispose.Attributes |= MemberAttributes.Override; dispose.Attributes |= MemberAttributes.Override;
return wrapper; return wrapper;
} }
private CodeTypeDeclaration CreateInterface (CodeTypeDeclaration wrapper) public CodeTypeDeclaration CreateInterface ()
{
return CreateInterface (null);
}
public CodeTypeDeclaration CreateInterface (CodeTypeDeclaration wrapper)
{ {
var iface = new CodeTypeDeclaration ("I" + Name) { var iface = new CodeTypeDeclaration ("I" + Name) {
TypeAttributes = TypeAttributes.Interface | TypeAttributes.NestedPublic, TypeAttributes = TypeAttributes.Interface | (wrapper != null? TypeAttributes.NestedPublic : TypeAttributes.Public),
Attributes = MemberAttributes.Public, Attributes = MemberAttributes.Public,
IsInterface = true IsInterface = true
}; };
@ -121,7 +128,10 @@ namespace Mono.VisualC.Code.Atoms {
foreach (var arg in TemplateArguments) foreach (var arg in TemplateArguments)
iface.TypeParameters.Add (arg); iface.TypeParameters.Add (arg);
iface.BaseTypes.Add (new CodeTypeReference (typeof (ICppClassOverridable<>).Name, wrapper.TypeReference ())); if (wrapper != null)
iface.BaseTypes.Add (new CodeTypeReference (typeof (ICppClassOverridable<>).Name, wrapper.TypeReference ()));
else
iface.BaseTypes.Add (new CodeTypeReference (typeof (ICppClass).Name));
foreach (var atom in Atoms) foreach (var atom in Atoms)
atom.Visit (iface); atom.Visit (iface);
@ -129,7 +139,7 @@ namespace Mono.VisualC.Code.Atoms {
return iface; return iface;
} }
private CodeTypeDeclaration CreateNativeLayout () public CodeTypeDeclaration CreateNativeLayout ()
{ {
var native = new CodeTypeDeclaration ("_" + Name) { var native = new CodeTypeDeclaration ("_" + Name) {
TypeAttributes = TypeAttributes.NestedPrivate | TypeAttributes.SequentialLayout, TypeAttributes = TypeAttributes.NestedPrivate | TypeAttributes.SequentialLayout,

16
Mono.VisualC.Code/Atoms/Field.cs

@ -36,15 +36,21 @@ namespace Mono.VisualC.Code.Atoms {
return field; return field;
} }
// FIXME: Handle fixed size arrays? Can't really see a good way to do that yet.
public CodeTypeReference TypeReference { public CodeTypeReference TypeReference {
get { get {
if (Type.ElementType == CppTypes.Typename)
return new CodeTypeReference (Type.ElementTypeName, CodeTypeReferenceOptions.GenericTypeParameter);
if (Type.Modifiers.Contains (CppModifiers.Pointer) || Type.Modifiers.Contains (CppModifiers.Reference)) if (Type.Modifiers.Count > 0) {
return new CodeTypeReference (typeof (IntPtr)); CppModifiers lastModifier = Type.Modifiers [Type.Modifiers.Count - 1];
if (lastModifier == CppModifiers.Pointer || lastModifier == CppModifiers.Reference)
return new CodeTypeReference (typeof (IntPtr));
}
if (Type.ElementType == CppTypes.Enum || Type.ElementType == CppTypes.Union)
return new CodeTypeReference (Type.ElementTypeName);
// FIXME: Handle arrays (fixed size?) if (Type.ElementType == CppTypes.Typename)
return new CodeTypeReference (Type.ElementTypeName, CodeTypeReferenceOptions.GenericTypeParameter);
Type managedType = Type.ToManagedType (); Type managedType = Type.ToManagedType ();
if (managedType != null) if (managedType != null)

68
Mono.VisualC.Code/Atoms/Method.cs

@ -6,14 +6,11 @@ using System.Collections.Generic;
using System.CodeDom; using System.CodeDom;
using Mono.VisualC.Interop; using Mono.VisualC.Interop;
using Mono.VisualC.Interop.ABI;
namespace Mono.VisualC.Code.Atoms { namespace Mono.VisualC.Code.Atoms {
public class Method : CodeContainer { public class Method : CodeContainer {
public struct Parameter {
public string Name;
public CppType Type;
}
public string Name { get; set; } public string Name { get; set; }
public Access Access { get; set; } public Access Access { get; set; }
@ -24,23 +21,55 @@ namespace Mono.VisualC.Code.Atoms {
public bool IsDestructor { get; set; } public bool IsDestructor { get; set; }
public CppType RetType { get; set; } public CppType RetType { get; set; }
public IList<Parameter> Parameters { get; set; } public IList<NameTypePair<CppType>> Parameters { get; set; }
// for testing:
// FIXME: make this Nullable, auto implemented property and remove bool field once this won't cause gmcs to crash
public NameTypePair<Type> Mangled {
get {
if (!hasMangledInfo) throw new InvalidOperationException ("No mangle info present.");
return mangled;
}
set {
mangled = value;
hasMangledInfo = true;
}
}
private NameTypePair<Type> mangled;
private bool hasMangledInfo = false;
public Method (string name) public Method (string name)
{ {
Name = name; Name = name;
Parameters = new List<Parameter> (); Parameters = new List<NameTypePair<CppType>> ();
} }
internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl) internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl)
{ {
if (decl.IsClass) { if (decl.IsClass) {
// FIXME: add commented methods to wrappers too? for now, I say let's reduce code clutter.
if (CommentedOut)
return null;
var method = CreateWrapperMethod (); var method = CreateWrapperMethod ();
decl.Members.Add (method); decl.Members.Add (method);
return method; return method;
} else if (decl.IsInterface) } else if (decl.IsInterface) {
decl.Members.Add (CreateInterfaceMethod ()); CodeTypeMember member;
if (CommentedOut) {
member = new CodeSnippetTypeMember ();
member.Comments.Add (new CodeCommentStatement (Comment));
member.Comments.Add (new CodeCommentStatement (CreateInterfaceMethod ().CommentOut (current_code_provider)));
} else
member = CreateInterfaceMethod ();
decl.Members.Add (member);
}
return null; return null;
} }
@ -52,7 +81,7 @@ namespace Mono.VisualC.Code.Atoms {
arguments.Add (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "Native")); arguments.Add (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "Native"));
foreach (var param in Parameters) { foreach (var param in Parameters) {
// FIXME: handle typenames // FIXME: handle typenames better
if (param.Type.ElementType != CppTypes.Typename) { if (param.Type.ElementType != CppTypes.Typename) {
Type managedType = param.Type.ToManagedType (); Type managedType = param.Type.ToManagedType ();
if (managedType != null && managedType.IsByRef) { if (managedType != null && managedType.IsByRef) {
@ -93,7 +122,13 @@ namespace Mono.VisualC.Code.Atoms {
else else
method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (CppInstancePtr).Name, "this")); method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (CppInstancePtr).Name, "this"));
AddParameters (method, true); if (hasMangledInfo)
method.CustomAttributes.Add (new CodeAttributeDeclaration ("ValidateBindings",
new CodeAttributeArgument (new CodePrimitiveExpression (Mangled.Name)),
new CodeAttributeArgument ("Abi", new CodeTypeOfExpression (Mangled.Type))));
foreach (var param in GetParameterDeclarations (true))
method.Parameters.Add (param);
return method; return method;
} }
@ -128,7 +163,9 @@ namespace Mono.VisualC.Code.Atoms {
else if (IsVirtual && !IsDestructor) else if (IsVirtual && !IsDestructor)
method.CustomAttributes.Add (new CodeAttributeDeclaration ("OverrideNative")); method.CustomAttributes.Add (new CodeAttributeDeclaration ("OverrideNative"));
AddParameters (method, false); foreach (var param in GetParameterDeclarations (false))
method.Parameters.Add (param);
return method; return method;
} }
@ -152,7 +189,12 @@ namespace Mono.VisualC.Code.Atoms {
} }
} }
private void AddParameters (CodeMemberMethod method, bool includeMangleAttribute) public IEnumerable<CodeParameterDeclarationExpression> GetParameterDeclarations ()
{
return GetParameterDeclarations (false);
}
private IEnumerable<CodeParameterDeclarationExpression> GetParameterDeclarations (bool includeMangleAttribute)
{ {
foreach (var param in Parameters) { foreach (var param in Parameters) {
CodeParameterDeclarationExpression paramDecl; CodeParameterDeclarationExpression paramDecl;
@ -174,7 +216,7 @@ namespace Mono.VisualC.Code.Atoms {
if (includeMangleAttribute && !IsVirtual && !paramStr.Equals (string.Empty)) if (includeMangleAttribute && !IsVirtual && !paramStr.Equals (string.Empty))
paramDecl.CustomAttributes.Add (new CodeAttributeDeclaration ("MangleAs", new CodeAttributeArgument (new CodePrimitiveExpression (paramStr)))); paramDecl.CustomAttributes.Add (new CodeAttributeDeclaration ("MangleAs", new CodeAttributeArgument (new CodePrimitiveExpression (paramStr))));
method.Parameters.Add (paramDecl); yield return paramDecl;
} }
} }

14
Mono.VisualC.Code/CodeAtom.cs

@ -4,13 +4,25 @@ using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler;
namespace Mono.VisualC.Code { namespace Mono.VisualC.Code {
public abstract class CodeAtom { public abstract class CodeAtom {
public string Comment { get; set; }
public bool CommentedOut { get; set; }
protected CodeDomProvider current_code_provider;
internal protected virtual void Visit (object obj) internal protected virtual void Visit (object obj)
{ {
Visit (obj, current_code_provider);
}
internal protected virtual void Visit (object obj, CodeDomProvider provider)
{
current_code_provider = provider;
object result = obj; object result = obj;
while (result != null) { while (result != null) {
@ -21,6 +33,8 @@ namespace Mono.VisualC.Code {
if (result is CodeStatementCollection) { result = InsideCodeStatementCollection (result as CodeStatementCollection); continue; } if (result is CodeStatementCollection) { result = InsideCodeStatementCollection (result as CodeStatementCollection); continue; }
break; break;
} }
current_code_provider = null;
} }
internal protected virtual object InsideCodeCompileUnit (CodeCompileUnit ccu) internal protected virtual object InsideCodeCompileUnit (CodeCompileUnit ccu)

28
Mono.VisualC.Code/CodeDomExtensions.cs

@ -1,14 +1,42 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler;
using Mono.VisualC.Interop; using Mono.VisualC.Interop;
namespace Mono.VisualC.Code { namespace Mono.VisualC.Code {
internal delegate void CodeGenMethod<T> (T codeObject, TextWriter writer, CodeGeneratorOptions cgo);
public static class CodeDomExtensions { public static class CodeDomExtensions {
public static CodeComment CommentOut (this CodeTypeMember code, CodeDomProvider provider)
{
// FIXME: Not implemented ini mono
//return CommentOut (provider.GenerateCodeFromMember, code);
return new CodeComment ();
}
public static CodeComment CommentOut (this CodeStatement code, CodeDomProvider provider)
{
return CommentOut (provider.GenerateCodeFromStatement, code);
}
public static CodeComment CommentOut (this CodeExpression code, CodeDomProvider provider)
{
return CommentOut (provider.GenerateCodeFromExpression, code);
}
private static CodeComment CommentOut<T> (CodeGenMethod<T> method, T codeObject)
{
StringWriter output = new StringWriter ();
CodeGeneratorOptions opts = new CodeGeneratorOptions ();
method (codeObject, output, opts);
return new CodeComment (output.ToString ());
}
public static CodeTypeReference TypeReference (this CodeTypeDeclaration ctd) public static CodeTypeReference TypeReference (this CodeTypeDeclaration ctd)
{ {
return new CodeTypeReference (ctd.Name, ctd.TypeParameterReferences ()); return new CodeTypeReference (ctd.Name, ctd.TypeParameterReferences ());

5
Mono.VisualC.Code/CodeUnit.cs

@ -4,6 +4,7 @@ using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.CodeDom; using System.CodeDom;
using System.CodeDom.Compiler;
namespace Mono.VisualC.Code { namespace Mono.VisualC.Code {
public class CodeUnit : CodeContainer { public class CodeUnit : CodeContainer {
@ -14,10 +15,10 @@ namespace Mono.VisualC.Code {
{ {
} }
public virtual CodeCompileUnit WrapperToCodeDom () public virtual CodeCompileUnit WrapperToCodeDom (CodeDomProvider provider)
{ {
CodeCompileUnit ccu = new CodeCompileUnit (); CodeCompileUnit ccu = new CodeCompileUnit ();
Visit (ccu); Visit (ccu, provider);
return ccu; return ccu;
} }

1
Mono.VisualC.Code/Mono.VisualC.Code.csproj

@ -46,6 +46,7 @@
<Compile Include="Atoms\Property.cs" /> <Compile Include="Atoms\Property.cs" />
<Compile Include="Atoms\Field.cs" /> <Compile Include="Atoms\Field.cs" />
<Compile Include="Atoms\Union.cs" /> <Compile Include="Atoms\Union.cs" />
<Compile Include="NameTypePair.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>

9
Mono.VisualC.Code/NameTypePair.cs

@ -0,0 +1,9 @@
using System;
namespace Mono.VisualC.Code {
public struct NameTypePair<TType> {
public string Name;
public TType Type;
}
}

23
Mono.VisualC.Interop/Attributes.cs

@ -10,6 +10,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Diagnostics;
namespace Mono.VisualC.Interop { namespace Mono.VisualC.Interop {
@ -29,6 +30,7 @@ namespace Mono.VisualC.Interop {
public class StaticAttribute : Attribute {} public class StaticAttribute : Attribute {}
// used for the const "this" - for example: int value () const; // used for the const "this" - for example: int value () const;
// use MangleAsAttribute for const parameters
[AttributeUsage (AttributeTargets.Method)] [AttributeUsage (AttributeTargets.Method)]
public class ConstAttribute : Attribute {} public class ConstAttribute : Attribute {}
@ -56,10 +58,25 @@ namespace Mono.VisualC.Interop {
this.MangleType = new CppType (cppTypeSpec); this.MangleType = new CppType (cppTypeSpec);
} }
} }
// for testing:
[AttributeUsage (AttributeTargets.Method)]
public class ValidateBindingsAttribute : Attribute {
public string MangledName { get; set; }
public Type Abi { get; set; }
public ValidateBindingsAttribute (string mangledName)
{
MangledName = mangledName;
}
}
#endregion #endregion
#region Wrapper method attributes
[AttributeUsage (AttributeTargets.Method)] [AttributeUsage (AttributeTargets.Method)]
public class OverrideNativeAttribute : Attribute {} public class OverrideNativeAttribute : Attribute {}
#endregion
} }
namespace Mono.VisualC.Interop.ABI { namespace Mono.VisualC.Interop.ABI {
@ -105,6 +122,12 @@ using Mono.VisualC.Interop;
return mangleType; return mangleType;
} }
[Conditional ("VALIDATE")]
public virtual void ValidateBindings (MemberInfo member)
{
throw new NotImplementedException ();
}
} }
} }

4
Mono.VisualC.Interop/CppType.cs

@ -31,6 +31,7 @@ namespace Mono.VisualC.Interop {
Int, Int,
Float, Float,
Double, Double,
WChar_T,
// for template type parameters // for template type parameters
Typename Typename
} }
@ -51,7 +52,8 @@ namespace Mono.VisualC.Interop {
(t) => t.ElementType == CppTypes.Void && t.Modifiers.Contains (CppModifiers.Pointer)? typeof (IntPtr) : null, (t) => t.ElementType == CppTypes.Void && t.Modifiers.Contains (CppModifiers.Pointer)? typeof (IntPtr) : null,
// single pointer to char gets string // single pointer to char gets string
(t) => t.ElementType == CppTypes.Char && t.Modifiers.Count (m => m == CppModifiers.Pointer) == 1? typeof (string) : null, // same with wchar_t (needs marshaling attribute though!)
(t) => (t.ElementType == CppTypes.Char || t.ElementType == CppTypes.WChar_T) && t.Modifiers.Count (m => m == CppModifiers.Pointer) == 1? typeof (string) : null,
// pointer to pointer to char gets string[] // pointer to pointer to char gets string[]
(t) => t.ElementType == CppTypes.Char && t.Modifiers.Count (m => m == CppModifiers.Pointer) == 2? typeof (string).MakeArrayType () : null, (t) => t.ElementType == CppTypes.Char && t.Modifiers.Count (m => m == CppModifiers.Pointer) == 2? typeof (string).MakeArrayType () : null,

334
generator/Main.cs

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Mono.VisualC.Interop; using Mono.VisualC.Interop;
using Mono.VisualC.Interop.ABI;
using Mono.VisualC.Interop.Util; using Mono.VisualC.Interop.Util;
using Mono.VisualC.Code; using Mono.VisualC.Code;
using Mono.VisualC.Code.Atoms; using Mono.VisualC.Code.Atoms;
@ -25,17 +26,9 @@ namespace CPPInterop {
public static readonly string [] genericTypeArgs = new string [] { "T", "U", "V", "W", "X", "Y", "Z" }; public static readonly string [] genericTypeArgs = new string [] { "T", "U", "V", "W", "X", "Y", "Z" };
public string Source { get; set; } public string Source { get; set; }
private CodeUnit currentUnit;
public Dictionary<string, string> Classes;
private CodeUnit enumerations;
public HashSet<string> Enumerations;
private CodeUnit unions;
public HashSet<string> Unions;
public string Dir {get; set;} public string Dir {get; set;}
public bool ShouldValidate { get; set; }
public string Library {get; set;}
private string nspace; private string nspace;
public string Namespace { public string Namespace {
@ -47,18 +40,28 @@ namespace CPPInterop {
} }
} }
public string Library {get; set;} public Dictionary<string, string> Classes;
public HashSet<string> Enumerations;
public HashSet<string> Unions;
public Dictionary<string,CodeTypeDeclaration> UnknownTypes;
public CodeDomProvider Provider { get; set; } public CodeDomProvider Provider { get; set; }
public CodeGeneratorOptions Options { get; set; } public CodeGeneratorOptions Options { get; set; }
private CodeUnit currentUnit;
private CodeUnit enumerations;
private CodeUnit unions;
private Dictionary<string,Property> properties; private Dictionary<string,Property> properties;
private HashSet<string> fileList;
public static void Main (string[] args) public static void Main (string[] args)
{ {
bool help = false; bool help = false;
Generator gen = new Generator (); Generator gen = new Generator ();
var p = new OptionSet () { var p = new OptionSet () {
{ "h|?|help", v => help = v != null }, { "h|?|help", v => help = v != null },
{ "validate", v => gen.ShouldValidate = v != null },
{ "f=", v => gen.Source = v }, { "f=", v => gen.Source = v },
{ "o=", v => gen.Dir = v }, { "o=", v => gen.Dir = v },
{ "ns=", v => gen.Namespace = v }, { "ns=", v => gen.Namespace = v },
@ -109,27 +112,41 @@ namespace CPPInterop {
public Generator () public Generator ()
{ {
Classes = new Dictionary<string, string>(); Classes = new Dictionary<string, string>();
UnknownTypes = new Dictionary<string,CodeTypeDeclaration> ();
Enumerations = new HashSet<string> (); Enumerations = new HashSet<string> ();
enumerations = new CodeUnit { ManagedNamespace = Namespace };
Unions = new HashSet<string> (); Unions = new HashSet<string> ();
unions = new CodeUnit { ManagedNamespace = Namespace };
enumerations = new CodeUnit { ManagedNamespace = Namespace };
unions = new CodeUnit { ManagedNamespace = Namespace };
properties = new Dictionary<string, Property> (); properties = new Dictionary<string, Property> ();
fileList = new HashSet<string> ();
} }
public void Run () public void Run ()
{ {
Console.WriteLine ("Generating bindings...");
XmlDocument xmldoc = new XmlDocument (); XmlDocument xmldoc = new XmlDocument ();
xmldoc.Load (Source); xmldoc.Load (Source);
// FIXME: Support namespaces!!!
//XmlNodeList namespaces = xmldoc.SelectNodes ("/GCC_XML/Namespace[@name != '::' and @name != '' and @name != 'std']"); //XmlNodeList namespaces = xmldoc.SelectNodes ("/GCC_XML/Namespace[@name != '::' and @name != '' and @name != 'std']");
ProcessClasses (xmldoc); ProcessClasses (xmldoc);
GenerateStaticLibField (); GenerateStaticLibField ();
SaveFile (enumerations.WrapperToCodeDom (), "Enums"); if (enumerations.Atoms.Any ())
SaveFile (unions.WrapperToCodeDom (), "Unions"); SaveFile (enumerations.WrapperToCodeDom (Provider), "Enums");
if (unions.Atoms.Any ())
SaveFile (unions.WrapperToCodeDom (Provider), "Unions");
if (ShouldValidate) {
GenerateUnknownTypeStubs ();
Validate ();
//File.Delete (fileList.Where (f => f.StartsWith ("UnknownTypes")).Single ());
}
} }
void ProcessClasses (XmlDocument xmldoc) void ProcessClasses (XmlDocument xmldoc)
@ -172,7 +189,14 @@ namespace CPPInterop {
for (int i = 0; i < replaceArgs.Length; i++) for (int i = 0; i < replaceArgs.Length; i++)
classAtom.TemplateArguments.Add (genericTypeArgs [i]); classAtom.TemplateArguments.Add (genericTypeArgs [i]);
} }
currentUnit.Atoms.AddLast (classAtom); currentUnit.Atoms.AddLast (classAtom);
Classes.Add (name, fname (name));
CppType currentType = new CppType (CppTypes.Class, name);;
if (replaceArgs != null)
currentType.Modify (new CppModifiers.TemplateModifier (replaceArgs));
CheckType (currentType);
foreach (XmlNode baseNode in clas.SelectNodes ("Base")) { foreach (XmlNode baseNode in clas.SelectNodes ("Base")) {
classAtom.Bases.Add (new Class.BaseClass { classAtom.Bases.Add (new Class.BaseClass {
@ -185,7 +209,8 @@ namespace CPPInterop {
} }
//string size = clas.Attributes["size"].Value; //string size = clas.Attributes["size"].Value;
var members = clas.Attributes["members"].Value.Split (' ').Where (id => !id.Equals (string.Empty)); var members = clas.Attributes["members"].Value.Split (' ').Where (id => !id.Equals (string.Empty)).ToArray ();
Dictionary<MethodSignature,string> methods = new Dictionary<MethodSignature, string> ();
foreach (string id in members) { foreach (string id in members) {
XmlNode n = xmldoc.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']"); XmlNode n = xmldoc.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']");
@ -205,23 +230,25 @@ namespace CPPInterop {
break; break;
case "Field": case "Field":
CppType fieldType = findType (xmldoc.DocumentElement, n.Attributes["type"]); CppType fieldType = findType (xmldoc.DocumentElement, n.Attributes["type"]);
string fname = "field" + fieldCount++; string fieldName = "field" + fieldCount++;
if (n.Attributes ["name"] != null && n.Attributes ["name"].Value != "") if (n.Attributes ["name"] != null && n.Attributes ["name"].Value != "")
fname = n.Attributes ["name"].Value; fieldName = n.Attributes ["name"].Value;
classAtom.Atoms.AddLast (new Field (fname, fieldType)); classAtom.Atoms.AddLast (new Field (fieldName, fieldType));
continue; continue;
default: default:
continue; continue;
} }
if (n.Attributes["access"] == null || n.Attributes["access"].Value != "public" || // Now we're processing a method...
(n.Attributes["overrides"] != null && n.Attributes["overrides"].Value != "" && !dtor) ||
n.Attributes["extern"] == null || n.Attributes["extern"].Value != "1") if (n.Attributes ["access"] == null || n.Attributes ["access"].Value != "public" ||
(n.Attributes ["overrides"] != null && n.Attributes ["overrides"].Value != "" && !dtor) ||
n.Attributes ["extern"] == null || n.Attributes ["extern"].Value != "1")
continue; continue;
string mname = n.Attributes["name"].Value; string mname = n.Attributes ["name"].Value;
CppType retType = findType (xmldoc.DocumentElement, n.Attributes["returns"]); CppType retType = findType (xmldoc.DocumentElement, n.Attributes ["returns"]);
if (replaceArgs != null) { if (replaceArgs != null) {
for (int i = 0; i < replaceArgs.Length; i++) for (int i = 0; i < replaceArgs.Length; i++)
retType = replaceType (retType, replaceArgs [i], genericTypeArgs [i]); retType = replaceType (retType, replaceArgs [i], genericTypeArgs [i]);
@ -229,35 +256,63 @@ namespace CPPInterop {
var methodAtom = new Method (dtor? "Destruct" : mname) { var methodAtom = new Method (dtor? "Destruct" : mname) {
RetType = retType, RetType = retType,
IsVirtual = n.Attributes["virtual"] != null && n.Attributes["virtual"].Value == "1", IsVirtual = n.Attributes ["virtual"] != null && n.Attributes ["virtual"].Value == "1",
IsStatic = n.Attributes["static"] != null && n.Attributes["static"].Value == "1", IsStatic = n.Attributes ["static"] != null && n.Attributes ["static"].Value == "1",
IsConst = n.Attributes["const"] != null && n.Attributes["const"].Value == "1", IsConst = n.Attributes ["const"] != null && n.Attributes ["const"].Value == "1",
IsConstructor = ctor, IsConstructor = ctor,
IsDestructor = dtor IsDestructor = dtor
}; };
if (ShouldValidate)
methodAtom.Mangled = new NameTypePair<Type> { Name = n.Attributes ["mangled"].Value, Type = typeof (ItaniumAbi) };
XmlNodeList argNodes = n.SelectNodes ("Argument");
CppType [] argTypes = new CppType [argNodes.Count];
int c = 0; int c = 0;
foreach (XmlNode arg in n.SelectNodes ("Argument")) { foreach (XmlNode arg in argNodes) {
string argname; string argname;
if (arg.Attributes["name"] == null) if (arg.Attributes ["name"] == null)
argname = "arg" + c; argname = "arg" + c;
else else
argname = arg.Attributes["name"].Value; argname = arg.Attributes ["name"].Value;
CppType argtype = findType (xmldoc.DocumentElement, arg.Attributes["type"].Value); CppType argtype = findType (xmldoc.DocumentElement, arg.Attributes ["type"].Value);
if (replaceArgs != null) { if (replaceArgs != null) {
for (int i = 0; i < replaceArgs.Length; i++) for (int i = 0; i < replaceArgs.Length; i++)
argtype = replaceType (argtype, replaceArgs [i], genericTypeArgs [i]); argtype = replaceType (argtype, replaceArgs [i], genericTypeArgs [i]);
} }
methodAtom.Parameters.Add (new Method.Parameter { Name = argname, Type = argtype }); methodAtom.Parameters.Add (new NameTypePair<CppType> { Name = argname, Type = argtype });
argTypes [c] = argtype;
// tee hee // tee hee
c++; c++;
} }
// if it's const, returns a value, and has no parameters, assume it's a property getter (for now?) // Try to filter out duplicate methods
if (methodAtom.IsConst && !retType.Equals (CppTypes.Void) && !methodAtom.Parameters.Any ()) { MethodSignature sig = new MethodSignature { Name = methodAtom.FormattedName, Arguments = argTypes };
string conflictingSig;
if (methods.TryGetValue (sig, out conflictingSig)) {
// FIXME: add comment to explain why it's commented out
string demangled = n.Attributes ["demangled"].Value;
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, n.Attributes ["demangled"].Value);
// Record unknown types in parameters and return type
CheckType (methodAtom.RetType);
foreach (var arg in methodAtom.Parameters)
CheckType (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 () &&
findMethod (xmldoc.DocumentElement, members, "@id != '" + id + "' and @name = '" + mname + "'") == null) {
var pname = methodAtom.FormattedName; var pname = methodAtom.FormattedName;
Property propertyAtom; Property propertyAtom;
@ -273,17 +328,17 @@ namespace CPPInterop {
} }
// if it's name starts with "set", does not return a value, and has one arg (besides this ptr) // if it's name starts with "set", does not return a value, and has one arg (besides this ptr)
// AND there is a corresponding getter method, then assume it's a property setter // and there is no other method with the same name...
if (mname.ToLower ().StartsWith ("set") && retType.Equals (CppTypes.Void) && methodAtom.Parameters.Count == 1) { if (mname.ToLower ().StartsWith ("set") && retType.Equals (CppTypes.Void) && methodAtom.Parameters.Count == 1 &&
findMethod (xmldoc.DocumentElement, members, "@id != '" + id + "' and @name = '" + mname + "'") == null) {
string getterName = "translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '" + string getterName = "translate(@name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') = '" +
mname.Substring (3).TrimStart ('_').ToLower () + "'"; mname.Substring (3).TrimStart ('_').ToLower () + "'";
string isMember = "@id = '" + string.Join ("' or @id = '", members.ToArray ()) + "'";
string pname = methodAtom.FormattedName.Substring (3); string pname = methodAtom.FormattedName.Substring (3);
Property propertyAtom = null; Property propertyAtom = null;
// FIXME: This xpath is probably very slow if the class has many members // ...AND there is a corresponding getter method, then assume it's a property setter
if (properties.TryGetValue (pname, out propertyAtom) || if (properties.TryGetValue (pname, out propertyAtom) || findMethod (xmldoc.DocumentElement, members, getterName) != null) {
xmldoc.SelectSingleNode (string.Format ("/GCC_XML/Method[{0} and ({1})]", getterName, isMember)) != null) {
if (propertyAtom != null) { if (propertyAtom != null) {
propertyAtom.Setter = methodAtom; propertyAtom.Setter = methodAtom;
@ -294,7 +349,7 @@ namespace CPPInterop {
} }
// set the method's arg name to "value" so that the prop setter works right // set the method's arg name to "value" so that the prop setter works right
Method.Parameter valueParam = methodAtom.Parameters [0]; var valueParam = methodAtom.Parameters [0];
valueParam.Name = "value"; valueParam.Name = "value";
methodAtom.Parameters [0] = valueParam; methodAtom.Parameters [0] = valueParam;
@ -305,66 +360,33 @@ namespace CPPInterop {
classAtom.Atoms.AddLast (methodAtom); classAtom.Atoms.AddLast (methodAtom);
} }
Classes.Add (name, sanitize (name) + "." + Provider.FileExtension);
SaveFile (currentUnit.WrapperToCodeDom (), name);
}
}
XmlNode find (XmlNode root, XmlAttribute att)
{
if (att != null)
return find (root, att.Value);
return null;
}
XmlNode find (XmlNode root, string id) SaveFile (currentUnit.WrapperToCodeDom (Provider), name);
{ }
XmlNode n = root.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']");
//if (n.Name == "Typedef")
// return n;
if (n.Attributes["type"] != null)
return find (root, n.Attributes["type"].Value);
return n;
}
CppType findType (XmlNode root, XmlAttribute att)
{
if (att != null)
return findType (root, att.Value);
return CppTypes.Void;
}
CppType findType (XmlNode root, string id)
{
return findType (root, id, new CppType ());
} }
CppType findType (XmlNode root, string id, CppType modifiers) void Validate ()
{ {
XmlNode n = root.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']"); Console.WriteLine ("Validating bindings...");
string name = "unknown"; var compileParams = new CompilerParameters {
if (n.Attributes ["name"] != null) GenerateInMemory = true,
name = n.Attributes ["name"].Value; TreatWarningsAsErrors = false
switch (n.Name) { };
case "ArrayType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Array)); compileParams.ReferencedAssemblies.Add (typeof (CppLibrary).Assembly.CodeBase);
case "PointerType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Pointer));
case "ReferenceType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Reference));
case "CvQualifiedType":
return findType (root, n.Attributes ["type"].Value,
modifiers.Modify (n.Attributes ["const"] != null && n.Attributes ["const"].Value == "1"? CppModifiers.Const : CppModifiers.Volatile));
case "Typedef": return findType (root, n.Attributes ["type"].Value, modifiers); CompilerResults results = Provider.CompileAssemblyFromFile (compileParams, fileList.ToArray ());
case "FundamentalType": return modifiers.ApplyTo (new CppType (name)); if (results.Errors.Count > 0) {
case "Class": return modifiers.ApplyTo (new CppType (CppTypes.Class, name)); foreach (CompilerError error in results.Errors)
case "Struct": return modifiers.ApplyTo (new CppType (CppTypes.Struct, name)); Console.Error.WriteLine ("{0}({1},{2}): error {3}: {4}", error.FileName, error.Line, error.Column, error.ErrorNumber, error.ErrorText);
case "Union": return modifiers.ApplyTo (ProcessUnion (root, n));
case "Enumeration": return modifiers.ApplyTo (ProcessEnum (n));
// FIXME: support function pointers betters Console.Error.WriteLine ("Validation failed with {0} compilation errors.", results.Errors.Count);
case "FunctionType": return modifiers.ApplyTo (CppTypes.Void);
} }
throw new NotImplementedException ("Unknown type node: " + n.Name); //Type [] types = validationAssembly.GetExportedTypes ();
// do stuff...
} }
CppType ProcessEnum (XmlNode enm) CppType ProcessEnum (XmlNode enm)
@ -431,6 +453,10 @@ namespace CPPInterop {
void GenerateStaticLibField () void GenerateStaticLibField ()
{ {
string name = "Lib_" + Library;
if (File.Exists (Path.Combine (Dir, fname (name))))
return;
var ccu = new CodeCompileUnit (); var ccu = new CodeCompileUnit ();
var ns = new CodeNamespace (Namespace); var ns = new CodeNamespace (Namespace);
var cls = new CodeTypeDeclaration ("Libs") { var cls = new CodeTypeDeclaration ("Libs") {
@ -446,16 +472,41 @@ namespace CPPInterop {
ns.Types.Add (cls); ns.Types.Add (cls);
ccu.Namespaces.Add (ns); ccu.Namespaces.Add (ns);
SaveFile (ccu, "Lib_" + Library); SaveFile (ccu, name);
}
void GenerateUnknownTypeStubs ()
{
var ccu = new CodeCompileUnit ();
var ns = new CodeNamespace (Namespace);
foreach (var type in UnknownTypes)
ns.Types.Add (type.Value);
ccu.Namespaces.Add (ns);
SaveFile (ccu, "UnknownTypes");
} }
void SaveFile (CodeCompileUnit ccu, string baseName) void SaveFile (CodeCompileUnit ccu, string baseName)
{ {
var sw = File.CreateText (Path.Combine (Dir, sanitize (baseName) + "." + Provider.FileExtension)); string name = Path.Combine (Dir, fname (baseName));
if (File.Exists (name) && fileList.Contains (name))
return;
if (File.Exists (name) && !fileList.Contains (name)) {
int i = 1;
while (File.Exists (Path.Combine (Dir, fname (baseName + i))))
i++;
name = fname (baseName + i);
}
var sw = File.CreateText (Path.Combine (Dir, name));
Provider.GenerateCodeFromCompileUnit (ccu, sw, Options); Provider.GenerateCodeFromCompileUnit (ccu, sw, Options);
sw.Flush (); sw.Flush ();
sw.Close (); sw.Close ();
sw.Dispose (); sw.Dispose ();
if (!fileList.Contains (name))
fileList.Add (name);
} }
static CppType replaceType (CppType inType, CppType toReplace, string tn) static CppType replaceType (CppType inType, CppType toReplace, string tn)
@ -473,9 +524,100 @@ namespace CPPInterop {
return inType; return inType;
} }
static string sanitize (string name) // FIXME: Do something trickier than just throw ElementTypeName in here?
void CheckType (CppType cpptype)
{
string type = cpptype.ElementTypeName;
if (type == null) return;
bool typeFound = Classes.ContainsKey (type) || Enumerations.Contains (type) || Unions.Contains (type);
bool alreadyUnknown = UnknownTypes.ContainsKey (type);
if (!typeFound && !alreadyUnknown) {
var ctd = new CodeTypeDeclaration (type) {
TypeAttributes = TypeAttributes.Public,
IsClass = true,
IsPartial = true
};
if (cpptype.Modifiers.Contains (CppModifiers.Template)) {
var template = cpptype.Modifiers.OfType<CppModifiers.TemplateModifier> ().Single ();
for (int i = 0; i < template.Types.Length; i++)
ctd.TypeParameters.Add (genericTypeArgs [i]);
}
UnknownTypes.Add (type, ctd);
} else if (typeFound && alreadyUnknown)
UnknownTypes.Remove (type);
}
static XmlNode find (XmlNode root, XmlAttribute att)
{
if (att != null)
return find (root, att.Value);
return null;
}
static XmlNode find (XmlNode root, string id)
{
XmlNode n = root.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']");
//if (n.Name == "Typedef")
// return n;
if (n.Attributes["type"] != null)
return find (root, n.Attributes["type"].Value);
return n;
}
static XmlNode findMethod (XmlNode root, string [] members, string predicate)
{
string isMember = "@id = '" + string.Join ("' or @id = '", members) + "'";
return root.SelectSingleNode (string.Format ("/GCC_XML/Method[({0}) and ({1})]", predicate, isMember));
}
CppType findType (XmlNode root, XmlAttribute att)
{
if (att != null)
return findType (root, att.Value);
return CppTypes.Void;
}
CppType findType (XmlNode root, string id)
{
return findType (root, id, new CppType ());
}
CppType findType (XmlNode root, string id, CppType modifiers)
{
XmlNode n = root.SelectSingleNode ("/GCC_XML/*[@id='" + id + "']");
string name = "unknown";
if (n.Attributes ["name"] != null)
name = n.Attributes ["name"].Value;
switch (n.Name) {
case "ArrayType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Array));
case "PointerType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Pointer));
case "ReferenceType": return findType (root, n.Attributes ["type"].Value, modifiers.Modify (CppModifiers.Reference));
case "CvQualifiedType":
return findType (root, n.Attributes ["type"].Value,
modifiers.Modify (n.Attributes ["const"] != null && n.Attributes ["const"].Value == "1"? CppModifiers.Const : CppModifiers.Volatile));
case "Typedef": return findType (root, n.Attributes ["type"].Value, modifiers);
case "FundamentalType": return modifiers.ApplyTo (new CppType (name));
case "Class": return modifiers.ApplyTo (new CppType (CppTypes.Class, name));
case "Struct": return modifiers.ApplyTo (new CppType (CppTypes.Struct, name));
case "Union": return modifiers.ApplyTo (ProcessUnion (root, n));
case "Enumeration": return modifiers.ApplyTo (ProcessEnum (n));
// FIXME: support function pointers betters
case "FunctionType": return modifiers.ApplyTo (CppTypes.Void);
}
throw new NotImplementedException ("Unknown type node: " + n.Name);
}
string fname (string name)
{ {
return name.Replace ("<", "_").Replace (">", "_").Replace(":", "_").Replace("*", "_").Replace (",", "_").Replace(" ", "_"); return name.Replace ("<", "_").Replace (">", "_").Replace(":", "_").Replace("*", "_").Replace (",", "_").Replace(" ", "_") + "." + Provider.FileExtension;
} }
} }
} }

37
generator/MethodSignature.cs

@ -0,0 +1,37 @@
using System;
using System.Linq;
using Mono.VisualC.Interop;
using Mono.VisualC.Interop.Util;
namespace CPPInterop {
// This is the best I could come up with to prevent duplicate managed
// signatures. The problem is, most of the types don't exist yet.
public struct MethodSignature {
public string Name;
public CppType [] Arguments;
public override bool Equals (object obj)
{
if (obj == null)
return false;
if (obj.GetType () != typeof(MethodSignature))
return false;
MethodSignature other = (MethodSignature)obj;
return Name == other.Name &&
Arguments != null? Arguments.SequenceEqual (other.Arguments) :
other.Arguments == null;
}
public override int GetHashCode ()
{
unchecked {
return (Name != null? Name.GetHashCode () : 0) ^
(Arguments != null? Arguments.SequenceHashCode () : 0);
}
}
}
}

1
generator/generator.csproj

@ -30,6 +30,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="Options.cs" /> <Compile Include="Options.cs" />
<Compile Include="MethodSignature.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System"> <Reference Include="System">

Loading…
Cancel
Save