Beispiel #1
0
        public unsafe static CodeTypeDelegate ToDelegate(this CXType type, string nativeName, CXCursor cursor, ModuleGenerator generator)
        {
            if (type.kind != CXTypeKind.CXType_FunctionProto &&
                type.kind != CXTypeKind.CXType_Unexposed)
            {
                throw new InvalidOperationException();
            }

            var clrName = NameConversions.ToClrName(nativeName, NameConversion.Type);

            CodeTypeDelegate delegateType = new CodeTypeDelegate();

            delegateType.CustomAttributes.Add(
                new CodeAttributeDeclaration(
                    new CodeTypeReference(typeof(UnmanagedFunctionPointerAttribute)),
                    new CodeAttributeArgument(
                        new CodePropertyReferenceExpression(
                            new CodeTypeReferenceExpression(typeof(CallingConvention)),
                            type.GetCallingConvention().ToString()))));

            delegateType.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            delegateType.Name       = clrName;
            delegateType.ReturnType = new CodeTypeReference(type.ResultType.ToClrType());

            uint argumentCounter = 0;

            var cursorVisitor = new DelegatingCXCursorVisitor(
                delegate(CXCursor c, CXCursor parent1)
            {
                if (c.Kind == CXCursorKind.CXCursor_ParmDecl)
                {
                    delegateType.Parameters.Add(Argument.GenerateArgument(generator, type, c, argumentCounter++, FunctionType.Delegate));
                }

                return(CXChildVisitResult.CXChildVisit_Continue);
            });

            cursor.VisitChildren(cursorVisitor.Visit, new CXClientData());

            return(delegateType);
        }
        public unsafe void GenerateTypes(string libraryName = "imobiledevice")
        {
            string[] arguments =
            {
                // Use the C++ backend
                "-x",
                "c++",

                // Parse the doxygen comments
                "-Wdocumentation",

                // Target 32-bit OS
                "-m32"
            };

            arguments = arguments.Concat(this.IncludeDirectories.Select(x => "-I" + x)).ToArray();

            FunctionVisitor functionVisitor;

            using (var createIndex = ClangSharp.Index.Create(false, true))
                using (var translationUnit = CXTranslationUnit.Parse(createIndex.Handle, this.InputFile, arguments, null, CXTranslationUnit_Flags.CXTranslationUnit_None))
                {
                    StringWriter errorWriter    = new StringWriter();
                    var          set            = translationUnit.DiagnosticSet;
                    var          numDiagnostics = set.NumDiagnostics;

                    bool hasError   = false;
                    bool hasWarning = false;

                    for (uint i = 0; i < numDiagnostics; ++i)
                    {
                        CXDiagnostic diagnostic = set.GetDiagnostic(i);
                        var          severity   = diagnostic.Severity;

                        switch (severity)
                        {
                        case CXDiagnosticSeverity.CXDiagnostic_Error:
                        case CXDiagnosticSeverity.CXDiagnostic_Fatal:
                            hasError = true;
                            break;

                        case CXDiagnosticSeverity.CXDiagnostic_Warning:
                            hasWarning = true;
                            break;
                        }

                        var location = diagnostic.Location;

                        location.GetFileLocation(out CXFile file, out uint line, out _, out _);
                        var fileName = file.Name.CString;

                        var message = diagnostic.Spelling.CString;
                        errorWriter.WriteLine($"{severity}: {fileName}:{line} {message}");
                    }

                    if (hasError)
                    {
                        throw new Exception(errorWriter.ToString());
                    }

                    if (hasWarning)
                    {
                        // Dump the warnings to the console output.
                        Console.WriteLine(errorWriter.ToString());
                    }

                    // Generate the marhaler types for string arrays (char **)
                    var arrayMarshalerGenerator = new ArrayMarshalerGenerator(this);
                    arrayMarshalerGenerator.Generate();

                    // Creates enums
                    var enumVisitor     = new EnumVisitor(this);
                    var realEnumVisitor = new DelegatingCXCursorVisitor(enumVisitor.Visit);
                    translationUnit.Cursor.VisitChildren(realEnumVisitor.Visit, new CXClientData());

                    // Creates structs
                    var structVisitor     = new StructVisitor(this);
                    var realStructVisitor = new DelegatingCXCursorVisitor(structVisitor.Visit);
                    translationUnit.Cursor.VisitChildren(realStructVisitor.Visit, new CXClientData());

                    // Creates safe handles & delegates
                    var typeDefVisitor     = new TypeDefVisitor(this);
                    var realTypeDefVisitor = new DelegatingCXCursorVisitor(typeDefVisitor.Visit);
                    translationUnit.Cursor.VisitChildren(realTypeDefVisitor.Visit, new CXClientData());

                    // Creates functions in a NativeMethods class
                    functionVisitor = new FunctionVisitor(this, libraryName);
                    var realFunctionVisitor = new DelegatingCXCursorVisitor(functionVisitor.Visit);
                    translationUnit.Cursor.VisitChildren(realFunctionVisitor.Visit, new CXClientData());

                    createIndex.Dispose();
                }

            // Update the SafeHandle to call the _free method
            var handles = this.Types.Where(t => t.Name.EndsWith("Handle"));

            foreach (var handle in handles)
            {
                var freeMethod = functionVisitor.NativeMethods.Members
                                 .OfType <CodeMemberMethod>()
                                 .Where(m =>
                                        (m.Name.EndsWith("_free") || m.Name.EndsWith("_disconnect")) &&
                                        m.Parameters.Count == 1 &&
                                        m.Parameters[0].Type.BaseType == handle.Name)
                                 .SingleOrDefault();

                if (freeMethod == null)
                {
                    continue;
                }

                var type = (HandleType)((NustacheGeneratedType)handle).Type;
                type.ReleaseMethodName         = freeMethod.Name;
                type.ReleaseMethodReturnsValue = freeMethod.ReturnType.BaseType != "System.Void";

                // Directly pass the IntPtr, becuase the handle itself will already be in the 'closed' state
                // when this method is called.
                freeMethod.Parameters[0].Type      = new CodeTypeReference(typeof(IntPtr));
                freeMethod.Parameters[0].Direction = FieldDirection.In;
            }

            // Extract the API interface and class, as well as the Exception class. Used for DI.
            ApiExtractor extractor = new ApiExtractor(this, functionVisitor);

            extractor.Generate();

            // Add the 'Error' extension IsError and ThrowOnError extension methods
            var extensionsExtractor = new ErrorExtensionExtractor(this, functionVisitor);

            extensionsExtractor.Generate();

            // Patch the native methods to be compatible with .NET Core - basically,
            // do the marshalling ourselves
            NativeMethodOverloadGenerator.Generate(this);
        }
Beispiel #3
0
        public unsafe CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
        {
            if (!cursor.Location.IsFromMainFile)
            {
                return(CXChildVisitResult.CXChildVisit_Continue);
            }

            CXCursorKind curKind = cursor.Kind;

            if (curKind == CXCursorKind.CXCursor_EnumDecl)
            {
                var nativeName  = cursor.Spelling.CString;
                var type        = cursor.EnumDecl_IntegerType.ToClrType();
                var enumComment = this.GetComment(cursor, forType: true);

                // enumName can be empty because of typedef enum { .. } enumName;
                // so we have to find the sibling, and this is the only way I've found
                // to do with libclang, maybe there is a better way?
                if (string.IsNullOrEmpty(nativeName))
                {
                    var forwardDeclaringVisitor = new ForwardDeclarationVisitor(cursor, skipSystemHeaderCheck: true);
                    cursor.LexicalParent.VisitChildren(forwardDeclaringVisitor.Visit, new CXClientData());
                    nativeName = forwardDeclaringVisitor.ForwardDeclarationCXCursor.Spelling.CString;

                    if (string.IsNullOrEmpty(nativeName))
                    {
                        nativeName = "_";
                    }
                }

                var clrName = NameConversions.ToClrName(nativeName, NameConversion.Type);

                // if we've printed these previously, skip them
                if (this.generator.NameMapping.ContainsKey(nativeName))
                {
                    return(CXChildVisitResult.CXChildVisit_Continue);
                }

                CodeTypeDeclaration enumDeclaration = new CodeTypeDeclaration();
                enumDeclaration.Attributes = MemberAttributes.Public;
                enumDeclaration.IsEnum     = true;
                enumDeclaration.Name       = clrName;
                enumDeclaration.BaseTypes.Add(type);

                if (enumComment != null)
                {
                    enumDeclaration.Comments.Add(enumComment);
                }

                // visit all the enum values
                DelegatingCXCursorVisitor visitor = new DelegatingCXCursorVisitor(
                    (c, vistor) =>
                {
                    var field =
                        new CodeMemberField()
                    {
                        Name           = NameConversions.ToClrName(c.Spelling.CString, NameConversion.Field),
                        InitExpression = new CodePrimitiveExpression(c.EnumConstantDeclValue)
                    };

                    var fieldComment = this.GetComment(c, forType: true);
                    if (fieldComment != null)
                    {
                        field.Comments.Add(fieldComment);
                    }

                    enumDeclaration.Members.Add(field);
                    return(CXChildVisitResult.CXChildVisit_Continue);
                });
                cursor.VisitChildren(visitor.Visit, new CXClientData());

                this.generator.AddType(nativeName, new CodeDomGeneratedType(enumDeclaration));
            }

            return(CXChildVisitResult.CXChildVisit_Recurse);
        }
Beispiel #4
0
        public unsafe CXChildVisitResult Visit(CXCursor cursor, CXCursor parent)
        {
            if (!cursor.Location.IsFromMainFile)
            {
                return(CXChildVisitResult.CXChildVisit_Continue);
            }

            CXCursorKind curKind = cursor.Kind;

            if (curKind == CXCursorKind.CXCursor_StructDecl)
            {
                this.fieldPosition = 0;
                var nativeName = cursor.Spelling.CString;

                // struct names can be empty, and so we visit its sibling to find the name
                if (string.IsNullOrEmpty(nativeName))
                {
                    var forwardDeclaringVisitor = new ForwardDeclarationVisitor(cursor, skipSystemHeaderCheck: true);
                    cursor.SemanticParent.VisitChildren(forwardDeclaringVisitor.Visit, new CXClientData());
                    nativeName = forwardDeclaringVisitor.ForwardDeclarationCXCursor.Spelling.CString;

                    if (string.IsNullOrEmpty(nativeName))
                    {
                        nativeName = "_";
                    }
                }

                var clrName = NameConversions.ToClrName(nativeName, NameConversion.Type);

                if (!this.generator.NameMapping.ContainsKey(nativeName))
                {
                    this.current            = new CodeTypeDeclaration(clrName);
                    this.current.IsStruct   = true;
                    this.current.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                    this.generator.AddType(nativeName, new CodeDomGeneratedType(this.current));

                    var layoutAttribute =
                        new CodeAttributeDeclaration(
                            new CodeTypeReference(typeof(StructLayoutAttribute)),
                            new CodeAttributeArgument(
                                new CodePropertyReferenceExpression(
                                    new CodeTypeReferenceExpression(typeof(LayoutKind)),
                                    nameof(LayoutKind.Sequential))));

                    this.current.CustomAttributes.Add(layoutAttribute);

                    var visitor = new DelegatingCXCursorVisitor(this.Visit);
                    cursor.VisitChildren(visitor.Visit, new CXClientData());
                }

                return(CXChildVisitResult.CXChildVisit_Continue);
            }

            if (curKind == CXCursorKind.CXCursor_FieldDecl)
            {
                var fieldName = cursor.Spelling.CString;
                if (string.IsNullOrEmpty(fieldName))
                {
                    fieldName = "field" + this.fieldPosition; // what if they have fields called field*? :)
                }

                this.fieldPosition++;

                foreach (var member in cursor.ToCodeTypeMember(fieldName, this.generator))
                {
                    this.current.Members.Add(member);
                }

                return(CXChildVisitResult.CXChildVisit_Continue);
            }

            return(CXChildVisitResult.CXChildVisit_Recurse);
        }