Example #1
0
        public static Primitives.Delegate ToDelegate(this TypeInfo type, string nativeName, Cursor cursor, Generator generator)
        {
            if (type.Kind != TypeKind.FunctionProto && type.Kind != TypeKind.Unexposed)
            {
                throw new InvalidOperationException();
            }

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

            var delegateType = new Primitives.Delegate()
            {
                Name       = clrName,
                ReturnType = type.GetResultType().ToClrType()
            };

            var cursorVisitor = new DelegatingCursorVisitor(
                delegate(Cursor c, Cursor parent1)
            {
                if (c.Kind == CursorKind.ParmDecl)
                {
                    delegateType.Arguments.Add(new Primitives.Argument()
                    {
                        Name       = NameConversions.ToClrName(c.GetDisplayName(), NameConversion.Parameter),
                        NativeName = c.GetDisplayName(),
                        Type       = c.GetTypeInfo().ToClrType()
                    });
                }

                return(ChildVisitResult.Continue);
            });

            cursorVisitor.VisitChildren(cursor);

            return(delegateType);
        }
        public ChildVisitResult Visit(Cursor cursor, Cursor parent)
        {
            if (!cursor.GetLocation().IsFromMainFile())
            {
                return(ChildVisitResult.Continue);
            }

            CursorKind curKind = cursor.Kind;

            if (curKind == CursorKind.TypedefDecl)
            {
                var nativeName = cursor.GetSpelling();
                var clrName    = NameConversions.ToClrName(nativeName, NameConversion.Type);

                TypeInfo type = cursor.GetTypedefDeclUnderlyingType().GetCanonicalType();

                // we handle enums and records in struct and enum visitors with forward declarations also
                switch (type.Kind)
                {
                case TypeKind.Record:
                    this.generator.AddType(
                        nativeName,
                        new Primitives.SafeHandle()
                    {
                        NativeName = nativeName,
                        Name       = NameConversions.ToClrName(nativeName, NameConversion.Type)
                    });
                    return(ChildVisitResult.Continue);

                case TypeKind.Pointer:
                    var pointee = type.GetPointeeType();

                    if (pointee.Kind == TypeKind.FunctionProto)
                    {
                        var functionType = cursor.GetTypedefDeclUnderlyingType();
                        var pt           = functionType.GetPointeeType();

                        var delegateType = pt.ToDelegate(nativeName, cursor, this.generator);
                        this.generator.AddType(nativeName, delegateType);

                        return(ChildVisitResult.Continue);
                    }
                    else
                    {
                        return(ChildVisitResult.Continue);
                    }

                default:
                    return(ChildVisitResult.Continue);
                }
            }

            return(ChildVisitResult.Recurse);
        }
        public static Argument GenerateArgument(this Generator generator, FunctionKind functionKind, Cursor functionCursor, uint argumentIndex)
        {
            var functionType = functionCursor.GetTypeInfo();
            var paramCursor  = functionCursor.GetArgument(argumentIndex);
            var type         = functionType.GetArgType(argumentIndex);

            var nativeName = paramCursor.GetSpelling();

            if (string.IsNullOrEmpty(nativeName))
            {
                nativeName = "param" + argumentIndex;
            }

            var name = NameConversions.ToClrName(nativeName, NameConversion.Parameter);

            Argument argument = new Argument();

            argument.Name       = name;
            argument.NativeName = nativeName;
            argument.Type       = type.ToClrType(functionKind);
            return(argument);
        }
Example #4
0
        public ChildVisitResult Visit(Cursor cursor, Cursor parent)
        {
            if (!cursor.GetLocation().IsFromMainFile())
            {
                return(ChildVisitResult.Continue);
            }

            if (parent.Kind == CursorKind.UnionDecl)
            {
                return(ChildVisitResult.Continue);
            }

            CursorKind curKind = cursor.Kind;

            if (curKind == CursorKind.StructDecl)
            {
                this.fieldPosition = 0;
                var nativeName = cursor.GetSpelling();

                // 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);
                    forwardDeclaringVisitor.VisitChildren(cursor.GetSemanticParent());
                    nativeName = forwardDeclaringVisitor.ForwardDeclarationCursor.GetSpelling();

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

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

                this.current = new Struct()
                {
                    Name        = clrName,
                    Description = GetComment(cursor)
                };

                // This is a lazy attempt to handle forward declarations. A better way would be as described here:
                // https://joshpeterson.github.io/identifying-a-forward-declaration-with-libclang
                // but libclang doesn't expose clang_getNullCursor (yet)
                current = this.generator.AddType(nativeName, current) as Struct;

                // If the struct is in use as a 'handle', this AddType would have returned an Handle and the 'as Struct'
                // statement would have cast it to null. In that case, we don't create an explicit struct.
                if (current != null)
                {
                    var visitor = new DelegatingCursorVisitor(this.Visit);
                    visitor.VisitChildren(cursor);
                }

                return(ChildVisitResult.Continue);
            }

            if (curKind == CursorKind.FieldDecl)
            {
                var fieldName = cursor.GetSpelling();
                if (string.IsNullOrEmpty(fieldName))
                {
                    fieldName = "field" + this.fieldPosition; // what if they have fields called field*? :)
                }
                else
                {
                    fieldName = NameConversions.ToClrName(fieldName, NameConversion.Field);
                }

                this.fieldPosition++;

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

                return(ChildVisitResult.Continue);
            }

            return(ChildVisitResult.Recurse);
        }
Example #5
0
        public static IEnumerable <Field> GetFields(Cursor cursor, string cursorSpelling, Generator generator)
        {
            var canonical = cursor.GetTypeInfo().GetCanonicalType();
            var comment   = GetComment(cursor);

            switch (canonical.Kind)
            {
            case TypeKind.ConstantArray:
                var size = canonical.GetArraySize();

                if (size == 0)
                {
                    // This happens in e.g., libusb_bos_dev_capability_descriptor
                    // #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
                    // [] /* valid C99 code */
                    // #else
                    // [0] /* non-standard, but usually working code */
                    // #endif
                    Field emptyArrayField = new Field();
                    emptyArrayField.Name        = cursorSpelling;
                    emptyArrayField.Type        = "IntPtr";
                    emptyArrayField.Description = comment;
                    yield return(emptyArrayField);
                }
                else if (canonical.GetArrayElementType().GetCanonicalType().Kind == TypeKind.UChar)
                {
                    Field fixedLengthField = new Field();
                    fixedLengthField.Name = cursorSpelling;
                    fixedLengthField.Type = "string";
                    fixedLengthField.FixedLengthString = (int)size;
                    fixedLengthField.Description       = comment;
                    yield return(fixedLengthField);
                }
                else if (canonical.GetArrayElementType().GetCanonicalType().Kind == TypeKind.Pointer)
                {
                    for (int i = 0; i < size; i++)
                    {
                        Field fixedLengthField = new Field();
                        fixedLengthField.Name        = $"{cursorSpelling}_{i}";
                        fixedLengthField.Type        = "void*";
                        fixedLengthField.Description = comment;
                        yield return(fixedLengthField);
                    }
                }
                else if (canonical.GetArrayElementType().GetCanonicalType().Kind == TypeKind.Record)
                {
                    for (int i = 0; i < size; i++)
                    {
                        Field fixedLengthField = new Field();
                        fixedLengthField.Name        = $"{cursorSpelling}_{i}";
                        fixedLengthField.Type        = "void*";
                        fixedLengthField.Description = comment;
                        yield return(fixedLengthField);
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
                break;

            case TypeKind.Pointer:
                var targetSpelling = cursor.GetTypeInfo().GetPointeeType().GetSpelling();
                var targetType     = generator.Types.ContainsKey(targetSpelling) ? generator.Types[targetSpelling] : null;

                if (targetType is SafeHandle)
                {
                    // Adding a SafeHandle to a struct would generate CS0208, so let's
                    // use an IntPtr instead.
                    yield return
                        (new Field()
                    {
                        Name = cursorSpelling,
                        Type = "IntPtr",
                        Description = comment,
                        Unsafe = true
                    });
                }
                else
                {
                    yield return
                        (new Field()
                    {
                        Name = cursorSpelling,
                        Type = cursor.GetTypeInfo().ToClrType(),
                        Description = comment,
                        Unsafe = true
                    });
                }

                break;

            case TypeKind.Enum:
                var enumField = new Field();
                enumField.Name        = cursorSpelling;
                enumField.Type        = NameConversions.ToClrName(canonical.GetSpelling(), NameConversion.Type);
                enumField.Description = comment;
                yield return(enumField);

                break;

            case TypeKind.Record:
                var recordField = new Field();
                recordField.Name        = cursorSpelling;
                recordField.Type        = NameConversions.ToClrName(canonical.GetSpelling(), NameConversion.Type);
                recordField.Description = comment;
                yield return(recordField);

                break;

            default:
                var field = new Field();
                field.Name        = cursorSpelling;
                field.Type        = canonical.ToClrType();
                field.Description = comment;
                yield return(field);

                break;
            }
        }
Example #6
0
        public ChildVisitResult Visit(Cursor cursor, Cursor parent)
        {
            if (!cursor.GetLocation().IsFromMainFile())
            {
                return(ChildVisitResult.Continue);
            }

            CursorKind curKind = cursor.Kind;

            if (curKind == CursorKind.EnumDecl)
            {
                var nativeName  = cursor.GetSpelling();
                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);
                    forwardDeclaringVisitor.VisitChildren(cursor.GetLexicalParent());
                    nativeName = forwardDeclaringVisitor.ForwardDeclarationCursor.GetSpelling();

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

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

                Enum enumDeclaration = new Enum()
                {
                    Name        = clrName,
                    Description = enumComment,
                    BaseType    = "byte"
                };

                // Most enums maps to bytes, with these exceptiosn
                if (nativeName == "libusb_error" || nativeName == "libusb_capability")
                {
                    enumDeclaration.BaseType = "int";
                }

                // Normally the enum values are prefixed with the enum name, e.g.
                // enum log_level => LOG_LEVEL_DEBUG = 1
                //
                // However, this is not always consistent, e.g.
                // enum libusb_capability => LIBUSB_CAP_HAS_CAPABILITY = 1
                //
                // Patch the prefix where required.
                var prefix = nativeName;

                var mappings = new Dictionary <string, string>();
                mappings.Add("libusb_capability", "libusb_cap");
                mappings.Add("libusb_class_code", "libusb_class");
                mappings.Add("libusb_descriptor_type", "libusb_dt");
                mappings.Add("libusb_endpoint_direction", "libusb_endpoint");
                mappings.Add("libusb_hotplug_flag", "libusb_hotplug");
                mappings.Add("libusb_request_recipient", "libusb_recipient");
                mappings.Add("libusb_standard_request", "libusb_request");
                mappings.Add("libusb_transfer_flags", "libusb_transfer");
                mappings.Add("libusb_transfer_status", "libusb_transfer");
                mappings.Add("libusb_bos_type", "libusb_bt");

                if (mappings.ContainsKey(prefix))
                {
                    prefix = mappings[prefix];
                }

                // visit all the enum values
                DelegatingCursorVisitor visitor = new DelegatingCursorVisitor(
                    (c, vistor) =>
                {
                    var value = c.GetEnumConstantDeclValue();

                    var field =
                        new EnumValue()
                    {
                        Name  = NameConversions.ToClrName(c.GetSpelling(), prefix, NameConversion.Enum),
                        Value = value > 0 ? $"0x{(int)c.GetEnumConstantDeclValue():X}" : $"{value}"
                    };

                    field.Description = this.GetComment(c, forType: true);

                    enumDeclaration.Values.Add(field);
                    return(ChildVisitResult.Continue);
                });
                visitor.VisitChildren(cursor);

                // Add a missing 'None' value
                if (nativeName == "libusb_transfer_flags")
                {
                    enumDeclaration.Values.Add(new EnumValue()
                    {
                        Name  = "None",
                        Value = "0x0"
                    });
                }

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

            return(ChildVisitResult.Recurse);
        }
Example #7
0
        private Method ExtractFunction(Cursor cursor)
        {
            var functionType = cursor.GetTypeInfo();
            var nativeName   = cursor.GetSpelling();
            var resultType   = cursor.GetResultType();

            Method method = new Method();

            method.ReturnType = resultType.ToClrType();

            // Most methods return ints to indicate whether an operation completed successfully.
            // The int is value which is defined in the libusb_error enum. Thus, cast the result
            // to Error.
            // There are a couple of exceptions, for which we default to returning an int and
            // let the caller decide what to do.

            Collection <string> methodsThatReturnInt = new Collection <string>()
            {
                "libusb_has_capability",       // => true/false
                "libusb_kernel_driver_active", // => true/false
                "libusb_control_transfer",     // => > 0: # of bytes, < 0: error
                "libusb_try_lock_events",      // => true/false
                "libusb_event_handling_ok",    // => true/false
                "libusb_event_handler_active", // => true/false
                "libusb_wait_for_event",       // => true/false
                "libusb_get_device_speed",     // => libusb_speed
                "libusb_get_max_packet_size",
                "libusb_get_max_iso_packet_size"
            };

            // Methods that return ints should return Error. Exceptions:
            if (method.ReturnType == "int" && !methodsThatReturnInt.Contains(nativeName))
            {
                method.ReturnType = "Error";
            }

            method.NativeName = nativeName;

            Dictionary <string, string> nameMappings = new Dictionary <string, string>();

            nameMappings.Add("libusb_strerror", "StrError");
            nameMappings.Add("libusb_setlocale", "SetLocale");

            if (nameMappings.ContainsKey(nativeName))
            {
                method.Name = nameMappings[nativeName];
            }
            else
            {
                method.Name = NameConversions.ToClrName(nativeName, NameConversion.Function);
            }

            var pinvokeMethods = new Collection <string>()
            {
                "libusb_exit",
                "libusb_unref_device",
                "libusb_close"
            };

            var functionKind = FunctionKind.Default;

            if (pinvokeMethods.Contains(nativeName))
            {
                functionKind = FunctionKind.Close;
            }

            int numArgTypes = functionType.GetNumArgTypes();

            for (uint i = 0; i < numArgTypes; ++i)
            {
                var argument = ArgumentGenerator.GenerateArgument(this.generator, functionKind, cursor, i);
                method.Arguments.Add(argument);
            }

            this.GetComment(cursor, method);

            return(method);
        }
Example #8
0
        public static string ToClrType(this TypeInfo type, FunctionKind functionKind = FunctionKind.Default)
        {
            if (type.Kind == TypeKind.Typedef && type.GetTypedefName() == "size_t")
            {
                return("UIntPtr");
            }

            if (type.Kind == TypeKind.Typedef && type.GetTypedefName() == "ssize_t")
            {
                return("IntPtr");
            }

            var canonical = type.GetCanonicalType();

            switch (canonical.Kind)
            {
            case TypeKind.Bool:
                return("bool");

            case TypeKind.UChar:
            case TypeKind.Char_U:
                return("byte");

            case TypeKind.SChar:
            case TypeKind.Char_S:
                return("sbyte");

            case TypeKind.UShort:
                return("ushort");

            case TypeKind.Short:
                return("short");

            case TypeKind.Float:
                return("float");

            case TypeKind.Double:
                return("double");

            case TypeKind.Int:
                return("int");

            case TypeKind.Enum:
                var enumName = type.GetTypeDeclaration().GetDisplayName();
                return(NameConversions.ToClrName(enumName, NameConversion.Type));

            case TypeKind.UInt:
                return("uint");

            case TypeKind.IncompleteArray:
                return("IntPtr");

            case TypeKind.Long:
                return("int");

            case TypeKind.ULong:
                return("uint");

            case TypeKind.LongLong:
                return("long");

            case TypeKind.ULongLong:
                return("ulong");

            case TypeKind.Void:
                return("void");

            case TypeKind.Pointer:
                // int LIBUSB_CALL libusb_init(libusb_context **ctx);
                // would map to Init(ref IntPtr),
                // whereas
                // void LIBUSB_CALL libusb_exit(libusb_context * ctx);
                // would map to Exit(IntPtr);
                var pointee = type.GetPointeeType();
                switch (pointee.Kind)
                {
                case TypeKind.Pointer:
                    // Double pointers are usually linked lists
                    var listType = pointee.GetPointeeType();

                    if (listType.Kind == TypeKind.Elaborated)
                    {
                        var pointee2Spelling = listType.GetNamedType().GetSpelling();
                        return($"{NameConversions.ToClrName(pointee2Spelling, NameConversion.Type)}**");
                    }
                    else
                    {
                        return("ref IntPtr");
                    }

                case TypeKind.Int:
                    return("ref int");

                case TypeKind.UChar:
                    return("byte*");

                case TypeKind.Typedef:
                    var typeDefName = pointee.GetTypedefName();

                    if (pointee.GetCanonicalType().Kind == TypeKind.Int)
                    {
                        return("ref int");
                    }
                    else if (typeDefName == "uint8_t")
                    {
                        return("byte*");
                    }
                    else if (functionKind == FunctionKind.Default)
                    {
                        return(NameConversions.ToClrName(pointee.GetTypedefName(), NameConversion.Type));
                    }
                    else
                    {
                        return("IntPtr");
                    }

                case TypeKind.Void:
                    return("IntPtr");

                case TypeKind.Elaborated:
                    var spelling = pointee.GetNamedType().GetSpelling();

                    if (spelling == "timeval")
                    {
                        return("ref UnixNativeTimeval");
                    }
                    else if (spelling == "libusb_context")
                    {
                        return("ref Context");
                    }
                    else
                    {
                        return($"{NameConversions.ToClrName(spelling, NameConversion.Type)}*");
                    }

                default:
                    return("IntPtr");
                }

            default:
                throw new NotSupportedException();
            }
        }