From 1a27eb3119b6380b756f31fe630624297a39ac68 Mon Sep 17 00:00:00 2001 From: "alexander.corrado" Date: Mon, 16 Aug 2010 20:17:30 +0000 Subject: [PATCH] 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 --- Mono.VisualC.Code/Atoms/Class.cs | 55 +- Mono.VisualC.Code/Atoms/Enumeration.cs | 17 +- Mono.VisualC.Code/Atoms/Field.cs | 2 +- Mono.VisualC.Code/Atoms/Method.cs | 157 ++++-- Mono.VisualC.Code/Atoms/Namespace.cs | 50 ++ Mono.VisualC.Code/Atoms/Preprocessor.cs | 1 - Mono.VisualC.Code/Atoms/Union.cs | 9 +- Mono.VisualC.Code/CodeAtom.cs | 11 +- Mono.VisualC.Code/CodeContainer.cs | 17 + Mono.VisualC.Code/CodeDomExtensions.cs | 19 +- Mono.VisualC.Code/CodeUnit.cs | 10 +- Mono.VisualC.Code/Mono.VisualC.Code.csproj | 1 + Mono.VisualC.Interop/ABI/CppAbi.cs | 7 +- Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs | 2 +- Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs | 2 +- .../ABI/Impl/VirtualOnlyAbi.cs | 2 +- Mono.VisualC.Interop/Attributes.cs | 12 +- Mono.VisualC.Interop/CppModifiers.cs | 76 ++- Mono.VisualC.Interop/CppType.cs | 67 ++- .../Util/IEnumerableTransform.cs | 4 + generator/Main.cs | 487 ++++++++++++++---- generator/MethodSignature.cs | 17 +- generator/Postfixes.cs | 70 +++ generator/generator.csproj | 3 +- 24 files changed, 848 insertions(+), 250 deletions(-) create mode 100644 Mono.VisualC.Code/Atoms/Namespace.cs create mode 100644 generator/Postfixes.cs diff --git a/Mono.VisualC.Code/Atoms/Class.cs b/Mono.VisualC.Code/Atoms/Class.cs index 7c141c4..62d33b3 100644 --- a/Mono.VisualC.Code/Atoms/Class.cs +++ b/Mono.VisualC.Code/Atoms/Class.cs @@ -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 { diff --git a/Mono.VisualC.Code/Atoms/Enumeration.cs b/Mono.VisualC.Code/Atoms/Enumeration.cs index 3b01fed..e88887b 100644 --- a/Mono.VisualC.Code/Atoms/Enumeration.cs +++ b/Mono.VisualC.Code/Atoms/Enumeration.cs @@ -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; } diff --git a/Mono.VisualC.Code/Atoms/Field.cs b/Mono.VisualC.Code/Atoms/Field.cs index 32dc649..f0fa36c 100644 --- a/Mono.VisualC.Code/Atoms/Field.cs +++ b/Mono.VisualC.Code/Atoms/Field.cs @@ -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); diff --git a/Mono.VisualC.Code/Atoms/Method.cs b/Mono.VisualC.Code/Atoms/Method.cs index 9512d1d..fd96ab0 100644 --- a/Mono.VisualC.Code/Atoms/Method.cs +++ b/Mono.VisualC.Code/Atoms/Method.cs @@ -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> 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 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 arguments = new List (); + 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 GetParameterDeclarations () + private CodeParameterDeclarationExpression GenerateParameterDeclaration (NameTypePair param) { - return GetParameterDeclarations (false); - } + CodeParameterDeclarationExpression paramDecl; - private IEnumerable 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; } } diff --git a/Mono.VisualC.Code/Atoms/Namespace.cs b/Mono.VisualC.Code/Atoms/Namespace.cs new file mode 100644 index 0000000..b65d5a4 --- /dev/null +++ b/Mono.VisualC.Code/Atoms/Namespace.cs @@ -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 ("}"); + } + } +} + diff --git a/Mono.VisualC.Code/Atoms/Preprocessor.cs b/Mono.VisualC.Code/Atoms/Preprocessor.cs index 9af876e..62585d2 100644 --- a/Mono.VisualC.Code/Atoms/Preprocessor.cs +++ b/Mono.VisualC.Code/Atoms/Preprocessor.cs @@ -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; diff --git a/Mono.VisualC.Code/Atoms/Union.cs b/Mono.VisualC.Code/Atoms/Union.cs index 4e26c40..62d5aed 100644 --- a/Mono.VisualC.Code/Atoms/Union.cs +++ b/Mono.VisualC.Code/Atoms/Union.cs @@ -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; } diff --git a/Mono.VisualC.Code/CodeAtom.cs b/Mono.VisualC.Code/CodeAtom.cs index dc3ff6d..b004db6 100644 --- a/Mono.VisualC.Code/CodeAtom.cs +++ b/Mono.VisualC.Code/CodeAtom.cs @@ -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) diff --git a/Mono.VisualC.Code/CodeContainer.cs b/Mono.VisualC.Code/CodeContainer.cs index fdf0be4..8729fff 100644 --- a/Mono.VisualC.Code/CodeContainer.cs +++ b/Mono.VisualC.Code/CodeContainer.cs @@ -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 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 ().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); diff --git a/Mono.VisualC.Code/CodeDomExtensions.cs b/Mono.VisualC.Code/CodeDomExtensions.cs index b3ac564..a30f95d 100644 --- a/Mono.VisualC.Code/CodeDomExtensions.cs +++ b/Mono.VisualC.Code/CodeDomExtensions.cs @@ -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) diff --git a/Mono.VisualC.Code/CodeUnit.cs b/Mono.VisualC.Code/CodeUnit.cs index aa00844..0240fda 100644 --- a/Mono.VisualC.Code/CodeUnit.cs +++ b/Mono.VisualC.Code/CodeUnit.cs @@ -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; } diff --git a/Mono.VisualC.Code/Mono.VisualC.Code.csproj b/Mono.VisualC.Code/Mono.VisualC.Code.csproj index 9f2fc88..c452f60 100644 --- a/Mono.VisualC.Code/Mono.VisualC.Code.csproj +++ b/Mono.VisualC.Code/Mono.VisualC.Code.csproj @@ -47,6 +47,7 @@ + diff --git a/Mono.VisualC.Interop/ABI/CppAbi.cs b/Mono.VisualC.Interop/ABI/CppAbi.cs index 9b361a8..27127e1 100644 --- a/Mono.VisualC.Interop/ABI/CppAbi.cs +++ b/Mono.VisualC.Interop/ABI/CppAbi.cs @@ -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: diff --git a/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs b/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs index fc00fe6..e3c272f 100644 --- a/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs +++ b/Mono.VisualC.Interop/ABI/Impl/ItaniumAbi.cs @@ -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); diff --git a/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs b/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs index 84c92df..c6d26d7 100644 --- a/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs +++ b/Mono.VisualC.Interop/ABI/Impl/MsvcAbi.cs @@ -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); diff --git a/Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs b/Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs index f0270b4..c8e9eaf 100644 --- a/Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs +++ b/Mono.VisualC.Interop/ABI/Impl/VirtualOnlyAbi.cs @@ -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."); } diff --git a/Mono.VisualC.Interop/Attributes.cs b/Mono.VisualC.Interop/Attributes.cs index c099c72..5c49c40 100644 --- a/Mono.VisualC.Interop/Attributes.cs +++ b/Mono.VisualC.Interop/Attributes.cs @@ -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 (); - } } } diff --git a/Mono.VisualC.Interop/CppModifiers.cs b/Mono.VisualC.Interop/CppModifiers.cs index 03f5954..0a86c15 100644 --- a/Mono.VisualC.Interop/CppModifiers.cs +++ b/Mono.VisualC.Interop/CppModifiers.cs @@ -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> Tokenize = new Dictionary> () { - { "\\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>> Tokenize = new Dictionary>> () { + { "\\<(.*)\\>", (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 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> action; + public Match match; } + private static IEnumerable 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 Parse (string input) + { + List cpm = new List (); + 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 NormalizeOrder (IEnumerable 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 ().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; diff --git a/Mono.VisualC.Interop/CppType.cs b/Mono.VisualC.Interop/CppType.cs index 6f04fc6..39fb869 100644 --- a/Mono.VisualC.Interop/CppType.cs +++ b/Mono.VisualC.Interop/CppType.cs @@ -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 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 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 () diff --git a/Mono.VisualC.Interop/Util/IEnumerableTransform.cs b/Mono.VisualC.Interop/Util/IEnumerableTransform.cs index 04d5583..a5f5ea0 100644 --- a/Mono.VisualC.Interop/Util/IEnumerableTransform.cs +++ b/Mono.VisualC.Interop/Util/IEnumerableTransform.cs @@ -155,6 +155,10 @@ namespace Mono.VisualC.Interop.Util { list.AddRange (items); list.AddRange (temp); } + public static void AddFirst (this List list, T item) + { + list.Insert (0, item); + } } diff --git a/generator/Main.cs b/generator/Main.cs index 8c3cfe0..e6190c7 100644 --- a/generator/Main.cs +++ b/generator/Main.cs @@ -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 Classes; - public HashSet Enumerations; - public HashSet Unions; - public Dictionary UnknownTypes; + public HashSet 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 properties; + private int enumCount = 0; + private int unionCount = 0; + private HashSet 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(); - UnknownTypes = new Dictionary (); + UnknownTypes = new HashSet (); - Enumerations = new HashSet (); - Unions = new HashSet (); - - 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 (); fileList = new HashSet (); @@ -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 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 ().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 { 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 ().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 ().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 ().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 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 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 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 ()) { + 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 dontCare; + return CheckType (type, false, out dontCare); + } + bool CheckType (CppType type, bool toBeCreated, out IEnumerable 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 ().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 ().Where (c => c.Name == type.ElementTypeName).Any () || + place.Atoms.OfType ().Where (e => e.Name == type.ElementTypeName).Any () || + place.Atoms.OfType ().Where (u => u.Name == type.ElementTypeName).Any (); + + var sameNamedNamespace = place.Atoms.OfType ().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 ().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 ().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); diff --git a/generator/MethodSignature.cs b/generator/MethodSignature.cs index 7fa05ae..884ee6f 100644 --- a/generator/MethodSignature.cs +++ b/generator/MethodSignature.cs @@ -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 Arguments; + + public MethodSignature (string name, IEnumerable 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) { diff --git a/generator/Postfixes.cs b/generator/Postfixes.cs new file mode 100644 index 0000000..331ddfa --- /dev/null +++ b/generator/Postfixes.cs @@ -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 List = new List () { + 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 ()) { + if (method.FormattedName == oldMember) { + method.FormattedName = newMember; + memberFound = true; + break; + } + } + + if (!memberFound) { + foreach (var prop in type.Atoms.OfType ()) { + 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; + } + + } +} + diff --git a/generator/generator.csproj b/generator/generator.csproj index 0b5a090..dbb4ac8 100644 --- a/generator/generator.csproj +++ b/generator/generator.csproj @@ -18,7 +18,7 @@ DEBUG prompt 4 - -f=/Users/Alex/OpenSource/gccxml/gccxml-build/bin/qapplication.xml -o=. + --testabi -f=/Users/Alex/OpenSource/gccxml/gccxml-build/bin/qapplication.xml -o=. --ns=Foo --lib=LibFoo none @@ -31,6 +31,7 @@ +