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); }
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); }
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; } }
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); }
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); }
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(); } }