Generator bug fixes. Now fully compiles bindings from qapplication.xml and will start testing mangled names against GCC-XML output before crashing. Preliminary support for C++ namespaces.

git-svn-id: https://mono-soc-2010.googlecode.com/svn/trunk/cppinterop@132 a470b8cb-0e6f-1642-1b45-71e107334c4b
This commit is contained in:
alexander.corrado 2010-08-16 20:17:30 +00:00
Родитель 3b0a3f418b
Коммит 1a27eb3119
24 изменённых файлов: 848 добавлений и 250 удалений

Просмотреть файл

@ -24,7 +24,6 @@ namespace Mono.VisualC.Code.Atoms {
public bool IsVirtual;
}
public string Name { get; set; }
public string StaticCppLibrary { get; set; }
public Definition DefinedAs { get; set; }
@ -40,17 +39,33 @@ namespace Mono.VisualC.Code.Atoms {
internal protected override object InsideCodeNamespace (CodeNamespace ns)
{
var wrapper = CreateWrapperClass ();
ns.Types.Add (wrapper);
return wrapper;
ns.Types.Add (CreateWrapperClass ());
return null;
}
internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl)
{
if (!decl.IsClass)
return null;
decl.Members.Add (CreateWrapperClass ());
return null;
}
public CodeTypeDeclaration CreateWrapperClass ()
{
var wrapper = new CodeTypeDeclaration (Name) { TypeAttributes = TypeAttributes.Public };
var wrapper = new CodeTypeDeclaration (Name) {
Attributes = MemberAttributes.Public,
TypeAttributes = TypeAttributes.Public
};
foreach (var arg in TemplateArguments)
wrapper.TypeParameters.Add (arg);
if (Atoms.Count == 0) {
wrapper.Comments.Add (new CodeCommentStatement ("FIXME: This type is a stub."));
return wrapper;
}
var iface = CreateInterface (wrapper);
wrapper.Members.Add (iface);
@ -88,17 +103,21 @@ namespace Mono.VisualC.Code.Atoms {
native.TypeReference (),
wrapper.TypeReference ()
};
var getClassMethod = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (StaticCppLibrary), "GetClass", types);
var getClassMethod = new CodeMethodReferenceExpression (new CodeTypeReferenceExpression (new CodeTypeReference (StaticCppLibrary, CodeTypeReferenceOptions.GlobalReference)), "GetClass", types);
implField.InitExpression = new CodeMethodInvokeExpression (getClassMethod, new CodePrimitiveExpression (Name));
}
wrapper.Members.Add (implField);
// always add native subclass ctor
wrapper.Members.Add (CreateNativeSubclassConstructor (hasOverrides));
CodeMemberMethod dispose = null;
foreach (var atom in Atoms) {
Method method = atom as Method;
if (method != null && method.IsDestructor)
if (method != null && method.IsDestructor) {
dispose = (CodeMemberMethod)method.InsideCodeTypeDeclaration (wrapper);
else
atom.Visit (dispose);
} else
atom.Visit (wrapper);
}
@ -153,6 +172,26 @@ namespace Mono.VisualC.Code.Atoms {
return native;
}
private CodeConstructor CreateNativeSubclassConstructor (bool callBase)
{
var ctor = new CodeConstructor {
Name = this.Name,
Attributes = MemberAttributes.Assembly
};
ctor.Parameters.Add (new CodeParameterDeclarationExpression (typeof (CppTypeInfo).Name, "subClass"));
// FIXME: Again, will this always work?
var implTypeInfo = new CodeFieldReferenceExpression (new CodeFieldReferenceExpression { FieldName = "impl" }, "TypeInfo");
if (callBase)
ctor.BaseConstructorArgs.Add (implTypeInfo);
var addBase = new CodeMethodInvokeExpression (new CodeArgumentReferenceExpression ("subClass"), "AddBase", implTypeInfo);
ctor.Statements.Add (addBase);
return ctor;
}
private CodeMemberMethod CreateDestructorlessDispose ()
{
var dispose = new CodeMemberMethod {

Просмотреть файл

@ -24,8 +24,24 @@ namespace Mono.VisualC.Code.Atoms {
}
internal protected override object InsideCodeNamespace (CodeNamespace ns)
{
ns.Types.Add (CreateEnumType ());
return null;
}
internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl)
{
if (!decl.IsClass)
return null;
decl.Members.Add (CreateEnumType ());
return null;
}
public CodeTypeDeclaration CreateEnumType ()
{
var type = new CodeTypeDeclaration (Name) {
Attributes = MemberAttributes.Public,
TypeAttributes = TypeAttributes.Public,
IsEnum = true
};
@ -33,7 +49,6 @@ namespace Mono.VisualC.Code.Atoms {
foreach (Item i in Items)
type.Members.Add (new CodeMemberField (typeof (int), i.Name) { InitExpression = new CodePrimitiveExpression (i.Value) });
ns.Types.Add (type);
return type;
}

Просмотреть файл

@ -47,7 +47,7 @@ namespace Mono.VisualC.Code.Atoms {
}
if (Type.ElementType == CppTypes.Enum || Type.ElementType == CppTypes.Union)
return new CodeTypeReference (Type.ElementTypeName);
return Type.TypeReference ();
if (Type.ElementType == CppTypes.Typename)
return new CodeTypeReference (Type.ElementTypeName, CodeTypeReferenceOptions.GenericTypeParameter);

Просмотреть файл

@ -7,12 +7,12 @@ using System.CodeDom;
using Mono.VisualC.Interop;
using Mono.VisualC.Interop.ABI;
using Mono.VisualC.Interop.Util;
namespace Mono.VisualC.Code.Atoms {
public class Method : CodeContainer {
public string Name { get; set; }
public Access Access { get; set; }
public bool IsVirtual { get; set; }
public bool IsStatic { get; set; }
@ -21,8 +21,11 @@ namespace Mono.VisualC.Code.Atoms {
public bool IsDestructor { get; set; }
public CppType RetType { get; set; }
public IList<NameTypePair<CppType>> Parameters { get; set; }
private string formatted_name;
// 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 {
@ -49,24 +52,30 @@ namespace Mono.VisualC.Code.Atoms {
internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl)
{
if (decl.IsClass) {
// FIXME: add commented methods to wrappers too? for now, I say let's reduce code clutter.
if (CommentedOut)
var method = CreateWrapperMethod (decl.BaseTypes [0].BaseType != typeof (ICppObject).Name);
if (method == null || CommentedOut)
return null;
var method = CreateWrapperMethod ();
if (Comment != null)
method.Comments.Add (new CodeCommentStatement (Comment));
decl.Members.Add (method);
return method;
} else if (decl.IsInterface) {
CodeTypeMember method = CreateInterfaceMethod ();
CodeTypeMember member;
if (CommentedOut) {
member = new CodeSnippetTypeMember ();
member.Comments.Add (new CodeCommentStatement (Comment));
member.Comments.Add (new CodeCommentStatement (CreateInterfaceMethod ().CommentOut (current_code_provider)));
member.Comments.Add (new CodeCommentStatement (method.CommentOut (current_code_provider)));
} else
member = CreateInterfaceMethod ();
member = method;
if (Comment != null)
member.Comments.Insert (0, new CodeCommentStatement (Comment));
decl.Members.Add (member);
}
@ -77,8 +86,11 @@ namespace Mono.VisualC.Code.Atoms {
internal protected override object InsideCodeStatementCollection (CodeStatementCollection stmts)
{
List<CodeExpression> arguments = new List<CodeExpression> ();
var native = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "Native");
var native_ptr = new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "native_ptr");
if (!IsStatic)
arguments.Add (new CodeFieldReferenceExpression (new CodeThisReferenceExpression (), "Native"));
arguments.Add (native);
foreach (var param in Parameters) {
// FIXME: handle typenames better
@ -92,9 +104,15 @@ namespace Mono.VisualC.Code.Atoms {
arguments.Add (new CodeArgumentReferenceExpression (param.Name));
}
// FIXME: Does just specifying the field name work for all code generators?
var impl = new CodeFieldReferenceExpression { FieldName = "impl" };
if (IsConstructor) {
var alloc = new CodeMethodInvokeExpression (impl, "Alloc", new CodeThisReferenceExpression ());
stmts.Add (new CodeAssignStatement (native_ptr, alloc));
}
var invoke = new CodeMethodInvokeExpression (impl, Name, arguments.ToArray ());
if (RetType.Equals (CppTypes.Void))
@ -102,16 +120,26 @@ namespace Mono.VisualC.Code.Atoms {
else
stmts.Add (new CodeMethodReturnStatement (invoke));
if (IsDestructor)
stmts.Add (new CodeMethodInvokeExpression (native, "Dispose"));
return null;
}
private CodeMemberMethod CreateInterfaceMethod ()
{
var returnType = ReturnTypeReference;
var method = new CodeMemberMethod {
Name = this.Name,
ReturnType = this.ReturnTypeReference
ReturnType = returnType
};
if (returnType == null) {
Comment = "FIXME: Unknown return type \"" + RetType.ToString () + "\" for method \"" + Name + "\"";;
CommentedOut = true;
method.ReturnType = new CodeTypeReference (typeof (void));
}
if (IsVirtual) method.CustomAttributes.Add (new CodeAttributeDeclaration ("Virtual"));
if (IsConstructor) method.CustomAttributes.Add (new CodeAttributeDeclaration ("Constructor"));
if (IsDestructor) method.CustomAttributes.Add (new CodeAttributeDeclaration ("Destructor"));
@ -123,25 +151,44 @@ namespace Mono.VisualC.Code.Atoms {
method.Parameters.Add (new CodeParameterDeclarationExpression (typeof (CppInstancePtr).Name, "this"));
if (hasMangledInfo)
method.CustomAttributes.Add (new CodeAttributeDeclaration ("ValidateBindings",
method.CustomAttributes.Add (new CodeAttributeDeclaration (typeof (AbiTestAttribute).Name,
new CodeAttributeArgument (new CodePrimitiveExpression (Mangled.Name)),
new CodeAttributeArgument ("Abi", new CodeTypeOfExpression (Mangled.Type))));
foreach (var param in GetParameterDeclarations (true))
for (int i = 0; i < Parameters.Count; i++) {
var param = GenerateParameterDeclaration (Parameters [i]);
string paramStr = Parameters [i].Type.ToString ();
if (param == null) {
Comment = "FIXME: Unknown parameter type \"" + paramStr + "\" to method \"" + Name + "\"";
CommentedOut = true;
method.Parameters.Add (new CodeParameterDeclarationExpression (paramStr, Parameters [i].Name));
continue;
}
// FIXME: Only add MangleAs attribute if the managed type chosen would mangle differently by default
if (!IsVirtual && !paramStr.Equals (string.Empty))
param.CustomAttributes.Add (new CodeAttributeDeclaration ("MangleAs", new CodeAttributeArgument (new CodePrimitiveExpression (paramStr))));
method.Parameters.Add (param);
}
return method;
}
private CodeMemberMethod CreateWrapperMethod ()
private CodeMemberMethod CreateWrapperMethod (bool hasBase)
{
CodeMemberMethod method;
if (IsConstructor)
method = new CodeConstructor {
if (IsConstructor) {
var ctor = new CodeConstructor {
Name = FormattedName,
Attributes = MemberAttributes.Public
};
else if (IsDestructor)
if (hasBase)
ctor.BaseConstructorArgs.Add (new CodeFieldReferenceExpression (new CodeFieldReferenceExpression { FieldName = "impl" }, "TypeInfo"));
method = ctor;
} else if (IsDestructor)
method = new CodeMemberMethod {
Name = "Dispose",
Attributes = MemberAttributes.Public
@ -163,8 +210,13 @@ namespace Mono.VisualC.Code.Atoms {
else if (IsVirtual && !IsDestructor)
method.CustomAttributes.Add (new CodeAttributeDeclaration ("OverrideNative"));
foreach (var param in GetParameterDeclarations (false))
for (int i = 0; i < Parameters.Count; i++) {
var param = GenerateParameterDeclaration (Parameters [i]);
if (param == null)
return null;
method.Parameters.Add (param);
}
return method;
}
@ -189,51 +241,54 @@ namespace Mono.VisualC.Code.Atoms {
}
}
public IEnumerable<CodeParameterDeclarationExpression> GetParameterDeclarations ()
private CodeParameterDeclarationExpression GenerateParameterDeclaration (NameTypePair<CppType> param)
{
return GetParameterDeclarations (false);
}
CodeParameterDeclarationExpression paramDecl;
private IEnumerable<CodeParameterDeclarationExpression> GetParameterDeclarations (bool includeMangleAttribute)
{
foreach (var param in Parameters) {
CodeParameterDeclarationExpression paramDecl;
if (param.Type.ElementType == CppTypes.Typename)
paramDecl = new CodeParameterDeclarationExpression (param.Type.ElementTypeName, param.Name);
if (param.Type.ElementType == CppTypes.Typename)
paramDecl = new CodeParameterDeclarationExpression (param.Type.ElementTypeName, param.Name);
else {
Type managedType = param.Type.ToManagedType ();
if (managedType != null && managedType.IsByRef)
paramDecl = new CodeParameterDeclarationExpression (managedType.GetElementType (), param.Name) { Direction = FieldDirection.Ref };
else if (managedType != null && managedType != typeof (ICppObject))
paramDecl = new CodeParameterDeclarationExpression (managedType, param.Name);
else
paramDecl = new CodeParameterDeclarationExpression (param.Type.TypeReference (), param.Name);
}
else {
Type managedType = param.Type.ToManagedType ();
CodeTypeReference typeRef = param.Type.TypeReference ();
// FIXME: Only add MangleAs attribute if the managed type chosen would mangle differently by default
string paramStr = param.Type.ToString ();
if (includeMangleAttribute && !IsVirtual && !paramStr.Equals (string.Empty))
paramDecl.CustomAttributes.Add (new CodeAttributeDeclaration ("MangleAs", new CodeAttributeArgument (new CodePrimitiveExpression (paramStr))));
if (managedType != null && managedType.IsByRef)
paramDecl = new CodeParameterDeclarationExpression (managedType.GetElementType (), param.Name) { Direction = FieldDirection.Ref };
else if (managedType != null && managedType != typeof (ICppObject))
paramDecl = new CodeParameterDeclarationExpression (managedType, param.Name);
else if (typeRef != null)
paramDecl = new CodeParameterDeclarationExpression (typeRef, param.Name);
else
return null;
yield return paramDecl;
}
return paramDecl;
}
public string FormattedName {
get {
string upper = Name.ToUpper ();
StringBuilder sb = new StringBuilder (Name.Length);
for (int i = 0; i < Name.Length; i++) {
if (i == 0)
sb.Append (upper [0]);
else if (Name [i] == '_')
sb.Append (upper [++i]);
else
sb.Append (Name [i]);
if (formatted_name == null) {
string upper = Name.ToUpper ();
StringBuilder sb = new StringBuilder (Name.Length);
for (int i = 0; i < Name.Length; i++) {
if (i == 0)
sb.Append (upper [0]);
else if (Name [i] == '_')
sb.Append (upper [++i]);
else
sb.Append (Name [i]);
}
formatted_name = sb.ToString ();
}
return sb.ToString ();
return formatted_name;
}
set {
formatted_name = value;
}
}

Просмотреть файл

@ -0,0 +1,50 @@
using System;
using System.IO;
using System.CodeDom;
namespace Mono.VisualC.Code.Atoms {
public class Namespace : CodeContainer {
public Namespace (string name)
{
Name = name;
}
internal protected override object InsideCodeCompileUnit (CodeCompileUnit ccu)
{
CreateNamespace (ccu, Name);
return null;
}
internal protected override object InsideCodeNamespace (CodeNamespace ns)
{
CodeCompileUnit ccu = ns.UserData ["CodeCompileUnit"] as CodeCompileUnit;
if (ccu == null)
throw new NotSupportedException ("Invalid CodeNamespace");
CreateNamespace (ccu, ns.Name + "." + Name);
return null;
}
private void CreateNamespace (CodeCompileUnit ccu, string name)
{
CodeNamespace ns = new CodeNamespace (name);
ns.Imports.Add (new CodeNamespaceImport ("Mono.VisualC.Interop"));
ns.UserData ["CodeCompileUnit"] = ccu;
foreach (var atom in Atoms)
atom.Visit (ns);
ccu.Namespaces.Add (ns);
}
public override void Write (TextWriter writer)
{
writer.WriteLine ("namespace {0} {{", Name);
base.Write (writer);
writer.WriteLine ("}");
}
}
}

Просмотреть файл

@ -66,7 +66,6 @@ namespace Mono.VisualC.Code.Atoms {
NotDefined
}
public Condition IfCondition {get; set;}
public string Name {get; set;}
public PoundIfDef (Condition condition, string name) {
IfCondition = condition;
Name = name;

Просмотреть файл

@ -8,8 +8,6 @@ namespace Mono.VisualC.Code.Atoms {
public class Union : CodeContainer {
public string Name { get; set; }
public Union (string name)
{
Name = name;
@ -19,9 +17,11 @@ namespace Mono.VisualC.Code.Atoms {
{
var union = new CodeTypeDeclaration (Name) {
Attributes = MemberAttributes.Public,
TypeAttributes = TypeAttributes.Public | TypeAttributes.ExplicitLayout,
TypeAttributes = TypeAttributes.Public,
IsStruct = true
};
var explicitLayout = new CodeAttributeArgument (new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeof (LayoutKind)), "Explicit"));
union.CustomAttributes.Add (new CodeAttributeDeclaration (new CodeTypeReference (typeof (StructLayoutAttribute)), explicitLayout));
foreach (var atom in Atoms) {
Field field = atom as Field;
@ -44,6 +44,9 @@ namespace Mono.VisualC.Code.Atoms {
internal protected override object InsideCodeTypeDeclaration (CodeTypeDeclaration decl)
{
if (!decl.IsClass)
return null;
decl.Members.Add (CreateUnionType ());
return null;
}

Просмотреть файл

@ -13,16 +13,11 @@ namespace Mono.VisualC.Code {
public string Comment { get; set; }
public bool CommentedOut { get; set; }
protected CodeDomProvider current_code_provider;
[ThreadStatic]
protected static CodeDomProvider current_code_provider;
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;
while (result != null) {
@ -33,8 +28,6 @@ namespace Mono.VisualC.Code {
if (result is CodeStatementCollection) { result = InsideCodeStatementCollection (result as CodeStatementCollection); continue; }
break;
}
current_code_provider = null;
}
internal protected virtual object InsideCodeCompileUnit (CodeCompileUnit ccu)

Просмотреть файл

@ -1,16 +1,20 @@
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.CodeDom;
using System.CodeDom.Compiler;
using Mono.VisualC.Code.Atoms;
namespace Mono.VisualC.Code {
public abstract class CodeContainer : CodeAtom {
private LinkedList<CodeAtom> containedAtoms;
public string IndentString {get; set;}
public string Name { get; set; }
public CodeContainer (string indentString)
{
@ -26,6 +30,19 @@ namespace Mono.VisualC.Code {
get { return containedAtoms; }
}
// Convenience method
public virtual void AddToNamespace (string name, CodeAtom atom)
{
Namespace ns = Atoms.OfType<Namespace> ().Where (n => n.Name == name).SingleOrDefault ();
if (ns == null) {
ns = new Namespace (name);
Atoms.AddLast (ns);
}
ns.Atoms.AddLast (atom);
}
public override void Write (TextWriter writer)
{
IndentedTextWriter itw = new IndentedTextWriter (writer, IndentString);

Просмотреть файл

@ -14,8 +14,11 @@ namespace Mono.VisualC.Code {
public static CodeComment CommentOut (this CodeTypeMember code, CodeDomProvider provider)
{
// FIXME: Not implemented ini mono
//return CommentOut (provider.GenerateCodeFromMember, code);
// FIXME: Not implemented in mono
try {
return CommentOut (provider.GenerateCodeFromMember, code);
} catch (NotImplementedException) {}
return new CodeComment ();
}
public static CodeComment CommentOut (this CodeStatement code, CodeDomProvider provider)
@ -53,11 +56,15 @@ namespace Mono.VisualC.Code {
from p in m.Types
select p.TypeReference (true);
Type managedType = useManagedType? t.ToManagedType () : null;
if (managedType == typeof (ICppObject))
managedType = null;
Type managedType = useManagedType && (t.ElementType != CppTypes.Typename)? t.ToManagedType () : null;
return new CodeTypeReference (managedType != null ? managedType.FullName : t.ElementTypeName, tempParm.ToArray ());
if ((managedType == null || managedType == typeof (ICppObject)) && t.ElementTypeName != null) {
string qualifiedName = t.Namespaces != null? string.Join (".", t.Namespaces) + "." + t.ElementTypeName : t.ElementTypeName;
return new CodeTypeReference (qualifiedName, tempParm.ToArray ());
} else if (managedType != null)
return new CodeTypeReference (managedType.FullName, tempParm.ToArray ());
return null;
}
public static CodeTypeReference [] TypeParameterReferences (this CodeTypeDeclaration ctd)

Просмотреть файл

@ -18,7 +18,11 @@ namespace Mono.VisualC.Code {
public virtual CodeCompileUnit WrapperToCodeDom (CodeDomProvider provider)
{
CodeCompileUnit ccu = new CodeCompileUnit ();
Visit (ccu, provider);
ccu.UserData ["CodeAtom"] = this;
current_code_provider = provider;
Visit (ccu);
current_code_provider = null;
return ccu;
}
@ -27,8 +31,10 @@ namespace Mono.VisualC.Code {
{
CodeNamespace ns = new CodeNamespace (ManagedNamespace);
ns.Imports.Add (new CodeNamespaceImport ("Mono.VisualC.Interop"));
ccu.Namespaces.Add (ns);
ns.UserData ["CodeCompileUnit"] = ccu;
ccu.Namespaces.Add (ns);
return ns;
}

Просмотреть файл

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

Просмотреть файл

@ -57,8 +57,13 @@ namespace Mono.VisualC.Interop.ABI {
// The members below must be implemented for a given C++ ABI:
public abstract string GetMangledMethodName (MethodInfo methodInfo);
public abstract CallingConvention? GetCallingConvention (MethodInfo methodInfo);
protected abstract string GetMangledMethodName (MethodInfo methodInfo);
public string GetMangledMethodName (string className, MethodInfo methodInfo)
{
class_name = className;
return GetMangledMethodName (methodInfo);
}
// The ImplementClass overrides are the main entry point to the Abi API:

Просмотреть файл

@ -33,7 +33,7 @@ namespace Mono.VisualC.Interop.ABI {
return CallingConvention.Cdecl;
}
public override string GetMangledMethodName (MethodInfo methodInfo)
protected override string GetMangledMethodName (MethodInfo methodInfo)
{
string methodName = methodInfo.Name;
MethodType methodType = GetMethodType (methodInfo);

Просмотреть файл

@ -40,7 +40,7 @@ namespace Mono.VisualC.Interop.ABI {
return CallingConvention.ThisCall;
}
public override string GetMangledMethodName (MethodInfo methodInfo)
protected override string GetMangledMethodName (MethodInfo methodInfo)
{
string methodName = methodInfo.Name;
MethodType methodType = GetMethodType (methodInfo);

Просмотреть файл

@ -30,7 +30,7 @@ namespace Mono.VisualC.Interop.ABI {
return defaultType;
}
public override string GetMangledMethodName (MethodInfo methodInfo)
protected override string GetMangledMethodName (MethodInfo methodInfo)
{
throw new NotSupportedException ("Name mangling is not supported by this class. All C++ interface methods must be declared virtual.");
}

Просмотреть файл

@ -10,7 +10,6 @@
using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
namespace Mono.VisualC.Interop {
@ -61,11 +60,11 @@ namespace Mono.VisualC.Interop {
// for testing:
[AttributeUsage (AttributeTargets.Method)]
public class ValidateBindingsAttribute : Attribute {
public class AbiTestAttribute : Attribute {
public string MangledName { get; set; }
public Type Abi { get; set; }
public ValidateBindingsAttribute (string mangledName)
public AbiTestAttribute (string mangledName)
{
MangledName = mangledName;
}
@ -115,7 +114,7 @@ using Mono.VisualC.Interop;
// this means that either no MangleAsAttribute was defined, or
// only CppModifiers were applied .. apply CppType from managed parameter type
if (mangleType.ElementType == CppTypes.Unknown && mangleType.ElementTypeName == null)
mangleType.ApplyTo (CppType.ForManagedType (managedType));
mangleType.CopyTypeFrom (CppType.ForManagedType (managedType));
else if (mangleType.ElementType == CppTypes.Unknown)
// FIXME: otherwise, we just assume it's CppTypes.Class for now.
mangleType.ElementType = CppTypes.Class;
@ -123,11 +122,6 @@ using Mono.VisualC.Interop;
return mangleType;
}
[Conditional ("VALIDATE")]
public virtual void ValidateBindings (MemberInfo member)
{
throw new NotImplementedException ();
}
}
}

Просмотреть файл

@ -12,31 +12,56 @@ using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Mono.VisualC.Interop.Util;
namespace Mono.VisualC.Interop {
public abstract class CppModifiers {
// This can be added to at runtime to support other modifiers
public static readonly Dictionary<string,Func<Match,CppModifiers>> Tokenize = new Dictionary<string,Func<Match,CppModifiers>> () {
{ "\\bconst\\b", m => CppModifiers.Const },
{ "\\*", m => CppModifiers.Pointer },
{ "\\[([^\\]]*)\\]", m => m.Groups [1].Success && m.Groups [1].Value.Trim () != ""? new ArrayModifier (int.Parse (m.Groups [1].Value)) : CppModifiers.Array },
{ "\\&", m => CppModifiers.Reference },
{ "\\bvolatile\\b", m => CppModifiers.Volatile },
{ "\\bsigned\\b", m => CppModifiers.Signed },
{ "\\bunsigned\\b", m => CppModifiers.Unsigned },
{ "\\bshort\\b", m => CppModifiers.Short },
{ "\\blong\\b", m => CppModifiers.Long },
{ "\\<(.*)\\>", m => m.Groups [1].Success && m.Groups [1].Value.Trim () != ""? new TemplateModifier (m.Groups [1].Value) : CppModifiers.Template }
// The list should be prioritized, in that the first items should be modifiers that can potentially contain other modifiers
public static readonly Dictionary<string,Action<Match,List<CppModifiers>>> Tokenize = new Dictionary<string,Action<Match,List<CppModifiers>>> () {
{ "\\<(.*)\\>", (m,l) => l.AddFirst (m.Groups [1].Success && m.Groups [1].Value.Trim () != ""? new TemplateModifier (m.Groups [1].Value) : CppModifiers.Template) },
{ "\\[([^\\]]*)\\]", (m,l) => l.Add (m.Groups [1].Success && m.Groups [1].Value.Trim () != ""? new ArrayModifier (int.Parse (m.Groups [1].Value)) : CppModifiers.Array) },
{ "\\bconst\\b", (m,l) => l.Add (CppModifiers.Const) },
{ "\\*", (m,l) => l.Add (CppModifiers.Pointer) },
{ "\\&", (m,l) => l.Add (CppModifiers.Reference) },
{ "\\bvolatile\\b", (m,l) => l.Add (CppModifiers.Volatile) },
{ "\\bunsigned\\b", (m,l) => l.Add (CppModifiers.Unsigned) },
{ "\\bsigned\\b", (m,l) => l.Add (CppModifiers.Signed) },
{ "\\bshort\\b", (m,l) => l.Add (CppModifiers.Short) },
{ "\\blong\\b", (m,l) => l.Add (CppModifiers.Long) }
};
public static IEnumerable<CppModifiers> Parse (string input)
{
foreach (var token in Tokenize) {
foreach (Match match in Regex.Matches (input, token.Key))
yield return token.Value (match);
}
private struct Token {
public Action<Match, List<CppModifiers>> action;
public Match match;
}
private static IEnumerable<Token> Tokenizer (string input) {
foreach (var token in Tokenize) {
Match match;
while ((match = Regex.Match (input, token.Key)) != null && match.Success) {
yield return new Token { match = match, action = token.Value };
input = input.Remove (match.Index, match.Length);
}
}
}
public static List<CppModifiers> Parse (string input)
{
List<CppModifiers> cpm = new List<CppModifiers> ();
var tokenizer = Tokenizer (input);
foreach (var token in tokenizer.OrderBy (t => t.match.Index))
token.action (token.match, cpm);
return cpm;
}
// removes any modifiers from the passed input
public static string Remove (string input)
{
foreach (var token in Tokenize)
@ -45,6 +70,23 @@ namespace Mono.VisualC.Interop {
return input;
}
// normalizes the order of order-agnostic modifiers
public static IEnumerable<CppModifiers> NormalizeOrder (IEnumerable<CppModifiers> modifiers)
{
var parts = modifiers.Transform (
For.AllInputsIn (CppModifiers.Unsigned, CppModifiers.Long).InAnyOrder ().Emit (new CppModifiers [] { CppModifiers.Unsigned, CppModifiers.Long }),
For.AllInputsIn (CppModifiers.Signed, CppModifiers.Long).InAnyOrder ().Emit (new CppModifiers [] { CppModifiers.Signed, CppModifiers.Long }),
For.AllInputsIn (CppModifiers.Unsigned, CppModifiers.Short).InAnyOrder ().Emit (new CppModifiers [] { CppModifiers.Unsigned, CppModifiers.Short }),
For.AllInputsIn (CppModifiers.Signed, CppModifiers.Short).InAnyOrder ().Emit (new CppModifiers [] { CppModifiers.Signed, CppModifiers.Short }),
For.UnmatchedInput<CppModifiers> ().Emit (cppmod => new CppModifiers [] { cppmod })
);
foreach (var array in parts)
foreach (var item in array)
yield return item;
}
public override bool Equals (object obj)
{
return this == obj as CppModifiers;

Просмотреть файл

@ -58,14 +58,14 @@ namespace Mono.VisualC.Interop {
(t) => t.ElementType == CppTypes.Char && t.Modifiers.Count (m => m == CppModifiers.Pointer) == 2? typeof (string).MakeArrayType () : null,
// arrays
(t) => t.Modifiers.Contains (CppModifiers.Array)? t.Subtract (CppModifiers.Array).ToManagedType ().MakeArrayType () : null,
(t) => t.Modifiers.Contains (CppModifiers.Array) && (t.Subtract (CppModifiers.Array).ToManagedType () != null)? t.Subtract (CppModifiers.Array).ToManagedType ().MakeArrayType () : null,
// convert single pointers to primatives to managed byref
(t) => t.Modifiers.Count (m => m == CppModifiers.Pointer) == 1? t.Subtract (CppModifiers.Pointer).ToManagedType ().MakeByRefType () : null,
(t) => t.Modifiers.Count (m => m == CppModifiers.Pointer) == 1 && (t.Subtract (CppModifiers.Pointer).ToManagedType () != null)? t.Subtract (CppModifiers.Pointer).ToManagedType ().MakeByRefType () : null,
// more than one level of indirection gets IntPtr type
(t) => t.Modifiers.Contains (CppModifiers.Pointer)? typeof (IntPtr) : null,
(t) => t.Modifiers.Contains (CppModifiers.Reference)? t.Subtract (CppModifiers.Reference).ToManagedType ().MakeByRefType () : null,
(t) => t.Modifiers.Contains (CppModifiers.Reference) && (t.Subtract (CppModifiers.Reference).ToManagedType () != null)? t.Subtract (CppModifiers.Reference).ToManagedType ().MakeByRefType () : null,
(t) => t.ElementType == CppTypes.Int && t.Modifiers.Contains (CppModifiers.Short) && t.Modifiers.Contains (CppModifiers.Unsigned)? typeof (ushort) : null,
(t) => t.ElementType == CppTypes.Int && t.Modifiers.Contains (CppModifiers.Long) && t.Modifiers.Contains (CppModifiers.Unsigned)? typeof (ulong) : null,
@ -127,6 +127,9 @@ namespace Mono.VisualC.Interop {
// this will contain the name of said type
public string ElementTypeName { get; set; }
// may be null, and will certainly be null if ElementTypeName is null
public string [] Namespaces { get; set; }
// this is initialized lazily to avoid unnecessary heap
// allocations if possible
private List<CppModifiers> internalModifiers;
@ -150,10 +153,13 @@ namespace Mono.VisualC.Interop {
{
ElementType = CppTypes.Unknown;
ElementTypeName = null;
Namespaces = null;
internalModifiers = null;
Parse (cppTypeSpec);
}
// FIXME: This makes no attempt to actually verify that the parts compose a valid C++ type.
private void Parse (object [] parts)
{
foreach (object part in parts) {
@ -176,15 +182,19 @@ namespace Mono.VisualC.Interop {
Type managedType = part as Type;
if (managedType != null) {
CppType mapped = CppType.ForManagedType (managedType);
ApplyTo (mapped);
CopyTypeFrom (mapped);
continue;
}
string strPart = part as string;
if (strPart != null) {
var parsed = CppModifiers.Parse (strPart);
if (parsed.Any ()) {
Modifiers.AddRange (parsed);
if (parsed.Count > 0) {
if (internalModifiers == null)
internalModifiers = parsed;
else
internalModifiers.AddRange (parsed);
strPart = CppModifiers.Remove (strPart);
}
@ -192,6 +202,15 @@ namespace Mono.VisualC.Interop {
strPart = strPart.Trim ();
if (strPart != "") {
string [] qualifiedName = strPart.Split (new string [] { "::" }, StringSplitOptions.RemoveEmptyEntries);
int numNamespaces = qualifiedName.Length - 1;
if (numNamespaces > 0) {
Namespaces = new string [numNamespaces];
for (int i = 0; i < numNamespaces; i++)
Namespaces [i] = qualifiedName [i];
}
strPart = qualifiedName [numNamespaces];
// FIXME: Use Enum.TryParse here if we decide to make this NET_4_0 only
try {
CppTypes cppTypes = (CppTypes)Enum.Parse (typeof (CppTypes), strPart, true);
@ -210,10 +229,11 @@ namespace Mono.VisualC.Interop {
// and combines its modifiers into this instance.
// Use when THIS instance may have attributes you want,
// but want the element type of the passed instance.
public CppType ApplyTo (CppType type)
public CppType CopyTypeFrom (CppType type)
{
ElementType = type.ElementType;
ElementTypeName = type.ElementTypeName;
Namespaces = type.Namespaces;
List<CppModifiers> oldModifiers = internalModifiers;
internalModifiers = type.internalModifiers;
@ -267,11 +287,16 @@ namespace Mono.VisualC.Interop {
return false;
CppType other = (CppType)obj;
// FIXME: the order of some modifiers is not significant
return (((internalModifiers == null || !internalModifiers.Any ()) &&
(other.internalModifiers == null || !other.internalModifiers.Any ())) ||
(internalModifiers != null && other.internalModifiers != null &&
internalModifiers.SequenceEqual (other.internalModifiers))) &&
CppModifiers.NormalizeOrder (internalModifiers).SequenceEqual (CppModifiers.NormalizeOrder (other.internalModifiers)))) &&
(((Namespaces == null || !Namespaces.Any ()) &&
(other.Namespaces == null || !other.Namespaces.Any ())) ||
(Namespaces != null && other.Namespaces != null &&
Namespaces.SequenceEqual (other.Namespaces))) &&
ElementType == other.ElementType &&
ElementTypeName == other.ElementTypeName;
}
@ -282,6 +307,7 @@ namespace Mono.VisualC.Interop {
unchecked {
return (internalModifiers != null? internalModifiers.SequenceHashCode () : 0) ^
ElementType.GetHashCode () ^
(Namespaces != null? Namespaces.SequenceHashCode () : 0) ^
(ElementTypeName != null? ElementTypeName.GetHashCode () : 0);
}
}
@ -291,25 +317,22 @@ namespace Mono.VisualC.Interop {
StringBuilder cppTypeString = new StringBuilder ();
if (ElementType != CppTypes.Unknown && ElementType != CppTypes.Typename)
cppTypeString.Append (Enum.GetName (typeof (CppTypes), ElementType).ToLower ());
cppTypeString.Append (Enum.GetName (typeof (CppTypes), ElementType).ToLower ()).Append (' ');
if (ElementTypeName != null && ElementType != CppTypes.Typename) {
if (cppTypeString.Length > 0)
cppTypeString.Append (' ');
cppTypeString.Append (ElementTypeName);
if (Namespaces != null) {
foreach (var ns in Namespaces)
cppTypeString.Append (ns).Append ("::");
}
if (ElementTypeName != null && ElementType != CppTypes.Typename)
cppTypeString.Append (ElementTypeName);
if (internalModifiers != null) {
foreach (var modifier in internalModifiers) {
string stringified = modifier.ToString ();
if (cppTypeString.Length > 0)
cppTypeString.Append (' ');
cppTypeString.Append (stringified);
}
foreach (var modifier in internalModifiers)
cppTypeString.Append (' ').Append (modifier.ToString ());
}
return cppTypeString.ToString ();
return cppTypeString.ToString ().Trim ();
}
public Type ToManagedType ()

Просмотреть файл

@ -155,6 +155,10 @@ namespace Mono.VisualC.Interop.Util {
list.AddRange (items);
list.AddRange (temp);
}
public static void AddFirst<T> (this List<T> list, T item)
{
list.Insert (0, item);
}
}

Просмотреть файл

@ -21,13 +21,12 @@ using System.CodeDom.Compiler;
using Microsoft.CSharp;
namespace CPPInterop {
class Generator {
public static readonly Regex TemplateRegEx = new Regex ("([^\\<]+)\\<(.+)\\>$");
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 ShouldValidate { get; set; }
public bool AbiTest { get; set; }
public string Library {get; set;}
private string nspace;
@ -35,24 +34,27 @@ namespace CPPInterop {
get { return nspace; }
set {
nspace = value;
enumerations.ManagedNamespace = value;
unions.ManagedNamespace = value;
Tree.ManagedNamespace = value;
//enumerations.ManagedNamespace = value;
//unions.ManagedNamespace = value;
}
}
public Dictionary<string, string> Classes;
public HashSet<string> Enumerations;
public HashSet<string> Unions;
public Dictionary<string,CodeTypeDeclaration> UnknownTypes;
public HashSet<string> UnknownTypes;
public CodeDomProvider Provider { get; set; }
public CodeGeneratorOptions Options { get; set; }
private CodeUnit currentUnit;
private CodeUnit enumerations;
private CodeUnit unions;
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)
@ -61,7 +63,7 @@ namespace CPPInterop {
Generator gen = new Generator ();
var p = new OptionSet () {
{ "h|?|help", v => help = v != null },
{ "validate", v => gen.ShouldValidate = v != null },
{ "testabi", v => gen.AbiTest = v != null },
{ "f=", v => gen.Source = v },
{ "o=", v => gen.Dir = v },
{ "ns=", v => gen.Namespace = v },
@ -112,13 +114,11 @@ namespace CPPInterop {
public Generator ()
{
Classes = new Dictionary<string, string>();
UnknownTypes = new Dictionary<string,CodeTypeDeclaration> ();
UnknownTypes = new HashSet<string> ();
Enumerations = new HashSet<string> ();
Unions = new HashSet<string> ();
enumerations = new CodeUnit { ManagedNamespace = Namespace };
unions = new CodeUnit { ManagedNamespace = Namespace };
Tree = new CodeUnit { ManagedNamespace = Namespace };
//enumerations = new CodeUnit { ManagedNamespace = Namespace };
//unions = new CodeUnit { ManagedNamespace = Namespace };
properties = new Dictionary<string, Property> ();
fileList = new HashSet<string> ();
@ -127,77 +127,101 @@ namespace CPPInterop {
public void Run ()
{
Console.WriteLine ("Generating bindings...");
XmlDocument xmldoc = new XmlDocument ();
xmldoc.Load (Source);
// FIXME: Support namespaces!!!
//XmlNodeList namespaces = xmldoc.SelectNodes ("/GCC_XML/Namespace[@name != '::' and @name != '' and @name != 'std']");
ProcessClasses (xmldoc);
// save files
Save ();
GenerateStaticLibField ();
if (enumerations.Atoms.Any ())
SaveFile (enumerations.WrapperToCodeDom (Provider), "Enums");
if (unions.Atoms.Any ())
SaveFile (unions.WrapperToCodeDom (Provider), "Unions");
Console.WriteLine ("Validating bindings...");
GenerateUnknownTypeStubs ();
Assembly asm = Validate ();
if (ShouldValidate) {
GenerateUnknownTypeStubs ();
Validate ();
//File.Delete (fileList.Where (f => f.StartsWith ("UnknownTypes")).Single ());
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");
}
void ProcessClasses (XmlDocument xmldoc)
{
XmlNodeList classes = xmldoc.SelectNodes ("/GCC_XML/Class[not(@incomplete)]");
// FIXME: Figure out how to eliminate struct noise
XmlNodeList classes = xmldoc.SelectNodes ("/GCC_XML/Class[not(@incomplete)]" /* |/GCC_XML/Struct[not(@incomplete)]" */);
foreach (XmlNode clas in classes) {
var f = xmldoc.SelectSingleNode ("/GCC_XML/File[@id='" + clas.Attributes["file"].Value + "']/@name");
if (f != null && f.Value.StartsWith ("/"))
continue;
string name = clas.Attributes["name"].Value;
if (Classes.ContainsKey (name))
if (clas.Attributes ["name"] == null || clas.Attributes ["name"].Value == "")
continue;
// FIXME: better way to do this
//currentUnit = new CodeUnit { ManagedNamespace = Namespace };
string name = clas.Attributes ["name"].Value;
string ns = GetNamespace (xmldoc.DocumentElement, clas);
CppType currentType = new CppType (clas.Name.ToLower (), ns != null? ns + "::" + name : name);
IEnumerable<CodeAtom> nested = null;
// FIXME: better way to do this (GCC-XML output doesn't seem to leave much choice)
CppType [] replaceArgs = null;
Match m = TemplateRegEx.Match (name);
if (m.Success) {
string baseName = m.Groups [1].Value;
if (Classes.ContainsKey (baseName))
var templated = currentType.Modifiers.OfType<CppModifiers.TemplateModifier> ().SingleOrDefault ();
if (templated != null) {
string baseName = currentType.ElementTypeName;
if (CheckType (currentType, true, out nested))
continue;
replaceArgs = m.Groups [2].Value.Split (',').Select (s => new CppType (s)).ToArray ();
replaceArgs = templated.Types;
string [] ras = new string [replaceArgs.Length];
string [] letters = new string [replaceArgs.Length];
for (int i = 0; i < replaceArgs.Length; i++) {
letters [i] = genericTypeArgs [i];
ras [i] = string.Format ("{0} with {1}", replaceArgs [i].ToString (), letters [i]);
}
Console.Error.WriteLine ("Warning: Creating generic type {0}<{1}> from the instantiated template {2} by replacing {3} (very buggy!!!)", baseName, string.Join (",", letters), name, string.Join (", ", ras));
name = baseName;
}
currentUnit = new CodeUnit { ManagedNamespace = Namespace };
name = baseName;
} else if (CheckType (currentType, true, out nested))
continue;
var classAtom = new Class (name) {
StaticCppLibrary = string.Format ("{0}.Libs.{1}", Namespace, Library)
};
GetContainer (xmldoc.DocumentElement, clas, Tree).Atoms.AddLast (classAtom);
//GetContainer (xmldoc.DocumentElement, clas, currentUnit).Atoms.AddLast (classAtom);
if (nested != null) {
foreach (var orphan in nested)
classAtom.Atoms.AddLast (orphan);
}
// add tempate type args
if (replaceArgs != null) {
for (int i = 0; i < replaceArgs.Length; i++)
classAtom.TemplateArguments.Add (genericTypeArgs [i]);
}
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);
// FIXME: Handle when base class name is fully qualified
foreach (XmlNode baseNode in clas.SelectNodes ("Base")) {
classAtom.Bases.Add (new Class.BaseClass {
Name = find (xmldoc.DocumentElement, baseNode.Attributes ["type"]).Attributes ["name"].Value,
@ -264,7 +288,7 @@ namespace CPPInterop {
};
if (ShouldValidate)
if (AbiTest)
methodAtom.Mangled = new NameTypePair<Type> { Name = n.Attributes ["mangled"].Value, Type = typeof (ItaniumAbi) };
XmlNodeList argNodes = n.SelectNodes ("Argument");
@ -291,7 +315,7 @@ namespace CPPInterop {
}
// Try to filter out duplicate methods
MethodSignature sig = new MethodSignature { Name = methodAtom.FormattedName, Arguments = argTypes };
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
@ -337,9 +361,15 @@ namespace CPPInterop {
string pname = methodAtom.FormattedName.Substring (3);
Property propertyAtom = null;
// ...AND there is a corresponding getter method, then assume it's a property setter
if (properties.TryGetValue (pname, out propertyAtom) || findMethod (xmldoc.DocumentElement, members, getterName) != 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.RetType.Equals (methodAtom.Parameters [0].Type);
} else {
XmlNode getter = findMethod (xmldoc.DocumentElement, members, getterName);
doIt = (getter != null && findType (xmldoc.DocumentElement, getter.Attributes ["returns"]).Equals (methodAtom.Parameters [0].Type));
}
if (doIt) {
if (propertyAtom != null) {
propertyAtom.Setter = methodAtom;
} else {
@ -361,72 +391,205 @@ namespace CPPInterop {
}
SaveFile (currentUnit.WrapperToCodeDom (Provider), name);
//SaveFile (currentUnit.WrapperToCodeDom (Provider), name);
}
}
void Validate ()
Assembly Validate ()
{
Console.WriteLine ("Validating bindings...");
var compileParams = new CompilerParameters {
GenerateInMemory = true,
IncludeDebugInformation = true,
WarningLevel = 0,
TreatWarningsAsErrors = false
};
compileParams.ReferencedAssemblies.Add (typeof (CppLibrary).Assembly.CodeBase);
CompilerResults results = Provider.CompileAssemblyFromFile (compileParams, fileList.ToArray ());
var errors = results.Errors.Cast<CompilerError> ().Where (e => !e.IsWarning);
if (results.Errors.Count > 0) {
foreach (CompilerError error in results.Errors)
Console.Error.WriteLine ("{0}({1},{2}): error {3}: {4}", error.FileName, error.Line, error.Column, error.ErrorNumber, error.ErrorText);
int count = 0;
foreach (var error in errors) {
Console.Error.WriteLine ("Validation failed with {0} compilation errors.", results.Errors.Count);
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.");
//Type [] types = validationAssembly.GetExportedTypes ();
// do stuff...
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 + "\"");
}
}
CppType ProcessEnum (XmlNode enm)
string GetNamespace (XmlNode root, XmlNode n)
{
XmlNode ns = find (root, n.Attributes ["context"]);
if (ns == null)
return null;
string nsname;
if (ns.Name == "Namespace")
nsname = ns.Attributes ["name"].Value;
else if (ns.Name == "Class" || ns.Name == "Struct")
nsname = new CppType (ns.Attributes ["name"].Value).ElementTypeName;
else
throw new NotSupportedException ("Unknown context: " + ns.Name);
if (nsname == "::")
return null;
string parent = GetNamespace (root, ns);
if (parent != null)
return parent + "::" + nsname;
return nsname;
}
CodeContainer GetContainer (XmlNode root, XmlNode n, CodeContainer def)
{
XmlNode ns = find (root, n.Attributes ["context"]);
if (ns == null)
return def;
string nsname;
if (ns.Name == "Namespace")
nsname = ns.Attributes ["name"].Value;
else if (ns.Name == "Class" || ns.Name == "Struct")
nsname = new CppType (ns.Attributes ["name"].Value).ElementTypeName;
else
throw new NotSupportedException ("Unknown context: " + ns.Name);
if (nsname == "::")
return def;
CodeContainer parent = GetContainer (root, 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 (XmlNode root, XmlNode enm)
{
bool hasName = false;
string ename = "Enum" + Enumerations.Count;
string ename = "Enum" + enumCount++;
if (enm.Attributes ["name"] != null && enm.Attributes ["name"].Value != "") {
hasName = true;
ename = enm.Attributes ["name"].Value;
}
if (!hasName || !Enumerations.Contains (ename)) {
CppType enumType = new CppType (CppTypes.Enum, ename);
Enumeration enumAtom = new Enumeration (enm.Attributes ["name"].Value);
foreach (XmlNode v in enm.SelectNodes ("EnumValue"))
enumAtom.Items.Add (new Enumeration.Item { Name = v.Attributes ["name"].Value, Value = Convert.ToInt32 (v.Attributes ["init"].Value) });
if (hasName) // assume it might be shared between classes
enumerations.Atoms.AddLast (enumAtom);
else
currentUnit.Atoms.AddLast (enumAtom);
Enumerations.Add (ename);
if (hasName) {
string ns = GetNamespace (root, enm);
if (ns != null)
enumType = new CppType (CppTypes.Enum, ns + "::" + ename);
}
return new CppType (CppTypes.Enum, ename);
IEnumerable<CodeAtom> dontCare;
if (!hasName || !CheckType (enumType, true, out dontCare)) {
Enumeration enumAtom = new Enumeration (ename);
foreach (XmlNode v in enm.SelectNodes ("EnumValue"))
enumAtom.Items.Add (new Enumeration.Item { Name = v.Attributes ["name"].Value, Value = Convert.ToInt32 (v.Attributes ["init"].Value) });
//GetContainer (root, enm, flatUnit).Atoms.AddLast (enumAtom);
CodeContainer nested = GetContainer (root, enm, Tree);//GetContainer (root, enm, currentUnit);
//if (hasName && !(nested is Class)) // assume it might be used by other classes
// GetContainer (root, enm, enumerations).Atoms.AddLast (enumAtom);
//else
nested.Atoms.AddLast (enumAtom);
}
return enumType;
}
CppType ProcessUnion (XmlNode root, XmlNode union)
{
bool hasName = false;
string uname = "Union" + Unions.Count;
string uname = "Union" + unionCount++;
if (union.Attributes ["name"] != null && union.Attributes ["name"].Value != "") {
hasName = true;
uname = union.Attributes ["name"].Value;
}
if (!hasName || !Unions.Contains (uname)) {
CppType unionType = new CppType (CppTypes.Union, uname);
if (hasName) {
string ns = GetNamespace (root, union);
if (ns != null)
unionType = new CppType (CppTypes.Union, ns + "::" + uname);
}
IEnumerable<CodeAtom> orphans = null;
if (!hasName || !CheckType (unionType, true, out orphans)) {
Union unionAtom = new Union (uname);
foreach (string id in union.Attributes["members"].Value.Split (' ').Where (id => !id.Equals (string.Empty))) {
@ -439,16 +602,22 @@ namespace CPPInterop {
Field field = new Field (n.Attributes ["name"].Value, findType (root, n.Attributes ["type"]));
unionAtom.Atoms.AddLast (field);
}
if (hasName) // assume it might be shared between classes
unions.Atoms.AddLast (unionAtom);
else
currentUnit.Atoms.AddLast (unionAtom);
Unions.Add (uname);
//GetContainer (root, union, flatUnit).Atoms.AddLast (unionAtom);
CodeContainer nested = GetContainer (root, union, Tree);//GetContainer (root, union, currentUnit);
//if (hasName && !(nested is Class)) // assume it might be used by other classes
// GetContainer (root, union, unions).Atoms.AddLast (unionAtom);
//else
nested.Atoms.AddLast (unionAtom);
if (orphans != null) {
foreach (var orphan in orphans)
unionAtom.Atoms.AddLast (orphan);
}
}
return new CppType (CppTypes.Union, uname);
return unionType;
}
void GenerateStaticLibField ()
@ -476,22 +645,61 @@ namespace CPPInterop {
}
void GenerateUnknownTypeStubs ()
{
{/*
var ccu = new CodeCompileUnit ();
var ns = new CodeNamespace (Namespace);
foreach (var type in UnknownTypes)
ns.Types.Add (type.Value);
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 (CheckType (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))
return;
File.Delete (name);
if (File.Exists (name) && !fileList.Contains (name)) {
int i = 1;
while (File.Exists (Path.Combine (Dir, fname (baseName + i))))
@ -513,6 +721,8 @@ namespace CPPInterop {
{
// 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 ());
@ -524,31 +734,80 @@ namespace CPPInterop {
return inType;
}
// FIXME: Do something trickier than just throw ElementTypeName in here?
void CheckType (CppType cpptype)
// returns true if the type was already created
bool CheckType (CppType type)
{
string type = cpptype.ElementTypeName;
if (type == null) return;
IEnumerable<CodeAtom> dontCare;
return CheckType (type, false, out dontCare);
}
bool CheckType (CppType type, bool toBeCreated, out IEnumerable<CodeAtom> orphanedAtoms)
{
orphanedAtoms = null;
if (type.ElementTypeName == null || type.ElementTypeName == "" || type.ElementType == CppTypes.Typename)
return true;
bool typeFound = Classes.ContainsKey (type) || Enumerations.Contains (type) || Unions.Contains (type);
bool alreadyUnknown = UnknownTypes.ContainsKey (type);
// check template parameters recursively
foreach (var paramType in type.Modifiers.OfType<CppModifiers.TemplateModifier> ().Where (t => t.Types != null).SelectMany (t => t.Types))
CheckType (paramType);
if (!typeFound && !alreadyUnknown) {
var ctd = new CodeTypeDeclaration (type) {
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,
IsPartial = true
IsClass = true
};
if (cpptype.Modifiers.Contains (CppModifiers.Template)) {
var template = cpptype.Modifiers.OfType<CppModifiers.TemplateModifier> ().Single ();
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);
UnknownTypes.Add (type, ctd);
} else if (typeFound && alreadyUnknown)
UnknownTypes.Remove (type);
return typeFound;
}
static XmlNode find (XmlNode root, XmlAttribute att)
@ -602,14 +861,14 @@ namespace CPPInterop {
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));
case "FundamentalType": return modifiers.CopyTypeFrom (new CppType (name));
case "Class": return modifiers.CopyTypeFrom (new CppType (CppTypes.Class, name));
case "Struct": return modifiers.CopyTypeFrom (new CppType (CppTypes.Struct, name));
case "Union": return modifiers.CopyTypeFrom (ProcessUnion (root, n));
case "Enumeration": return modifiers.CopyTypeFrom (ProcessEnum (root, n));
// FIXME: support function pointers betters
case "FunctionType": return modifiers.ApplyTo (CppTypes.Void);
case "FunctionType": return modifiers.CopyTypeFrom (CppTypes.Void);
}
throw new NotImplementedException ("Unknown type node: " + n.Name);

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Mono.VisualC.Interop;
using Mono.VisualC.Interop.Util;
@ -9,7 +11,20 @@ namespace CPPInterop {
// signatures. The problem is, most of the types don't exist yet.
public struct MethodSignature {
public string Name;
public CppType [] Arguments;
public IEnumerable<CppType> Arguments;
public MethodSignature (string name, IEnumerable<CppType> args)
{
Name = name;
// This is kinda hacky, but it was the best I could come up with at the time.
// Remove any modifiers that will affect the managed type signature.
// FIXME: Subtract more?
Arguments = args.Select (a => a.Subtract (CppModifiers.Const))
.Select (a => a.Subtract (CppModifiers.Volatile))
.Select (a => a.Modifiers.Count (m => m == CppModifiers.Long) > 1? a.Subtract (CppModifiers.Long) : a)
.Select (a => a.ElementType == CppTypes.Char? a.Subtract (CppModifiers.Unsigned).Subtract (CppModifiers.Signed) : a);
}
public override bool Equals (object obj)
{

70
generator/Postfixes.cs Normal file
Просмотреть файл

@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Mono.VisualC.Code;
using Mono.VisualC.Code.Atoms;
namespace CPPInterop {
public static class Postfixes {
public static readonly List<IPostfix> List = new List<IPostfix> () {
new DuplicateMemberFix ()
};
}
public interface IPostfix {
bool TryFix (string errorMessage, Generator gen);
}
public class DuplicateMemberFix : IPostfix {
private static readonly Regex ID = new Regex("The type `(.+)' already contains a definition for `(.+)'");
public DuplicateMemberFix ()
{
}
public bool TryFix (string errorMessage, Generator gen)
{
Match m = ID.Match (errorMessage);
if (!m.Success)
return false;
string typeName = m.Groups [1].Value;
string oldMember = m.Groups [2].Value;
string newMember = oldMember + "2";
bool memberFound = false;
CodeContainer type = gen.GetPath (typeName.Split ('.').Skip (1).ToArray ());
foreach (var method in type.Atoms.OfType<Method> ()) {
if (method.FormattedName == oldMember) {
method.FormattedName = newMember;
memberFound = true;
break;
}
}
if (!memberFound) {
foreach (var prop in type.Atoms.OfType<Property> ()) {
if (prop.Name == oldMember) {
prop.Name = newMember;
memberFound = true;
break;
}
}
}
if (memberFound) {
//Console.WriteLine ("Renaming \"{0}\" to \"{1}\" to prevent name collision in class \"{2}\" ...", oldMember, newMember, typeName);
gen.Save ();
}
return memberFound;
}
}
}

Просмотреть файл

@ -18,7 +18,7 @@
<DefineConstants>DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Commandlineparameters>-f=/Users/Alex/OpenSource/gccxml/gccxml-build/bin/qapplication.xml -o=.</Commandlineparameters>
<Commandlineparameters>--testabi -f=/Users/Alex/OpenSource/gccxml/gccxml-build/bin/qapplication.xml -o=. --ns=Foo --lib=LibFoo</Commandlineparameters>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@ -31,6 +31,7 @@
<Compile Include="Main.cs" />
<Compile Include="Options.cs" />
<Compile Include="MethodSignature.cs" />
<Compile Include="Postfixes.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System">