[ffigen] Objective C interfaces (#287)
diff --git a/pkgs/ffigen/lib/src/code_generator.dart b/pkgs/ffigen/lib/src/code_generator.dart index 0e175e6..6944957 100644 --- a/pkgs/ffigen/lib/src/code_generator.dart +++ b/pkgs/ffigen/lib/src/code_generator.dart
@@ -13,6 +13,7 @@ export 'code_generator/global.dart'; export 'code_generator/imports.dart'; export 'code_generator/library.dart'; +export 'code_generator/objc_interface.dart'; export 'code_generator/struc.dart'; export 'code_generator/type.dart'; export 'code_generator/typealias.dart';
diff --git a/pkgs/ffigen/lib/src/code_generator/binding_string.dart b/pkgs/ffigen/lib/src/code_generator/binding_string.dart index 2929ee8..031bf5a 100644 --- a/pkgs/ffigen/lib/src/code_generator/binding_string.dart +++ b/pkgs/ffigen/lib/src/code_generator/binding_string.dart
@@ -23,4 +23,5 @@ global, enumClass, typeDef, + objcInterface, }
diff --git a/pkgs/ffigen/lib/src/code_generator/dart_keywords.dart b/pkgs/ffigen/lib/src/code_generator/dart_keywords.dart index 8e0de3c..93d5877 100644 --- a/pkgs/ffigen/lib/src/code_generator/dart_keywords.dart +++ b/pkgs/ffigen/lib/src/code_generator/dart_keywords.dart
@@ -7,65 +7,66 @@ /// Source: https://dart.dev/guides/language/language-tour#keywords. const keywords = { 'abstract', - 'else', - 'import', - 'super', 'as', - 'enum', - 'in', - 'switch', 'assert', - 'export', - 'interface', - 'sync', 'async', - 'extends', - 'is', - 'this', 'await', - 'extension', - 'library', - 'throw', 'break', - 'external', - 'mixin', - 'true', 'case', - 'factory', - 'new', - 'try', 'catch', - 'false', - 'null', - 'typedef', 'class', - 'final', - 'on', - 'var', 'const', - 'finally', - 'operator', - 'void', 'continue', - 'for', - 'part', - 'while', 'covariant', - 'Function', - 'rethrow', - 'with', 'default', - 'get', - 'return', - 'yield', 'deferred', - 'hide', - 'set', 'do', - 'if', - 'show', 'dynamic', + 'else', + 'enum', + 'export', + 'extends', + 'extension', + 'external', + 'factory', + 'false', + 'final', + 'finally', + 'for', + 'Function', + 'get', + 'hide', + 'if', 'implements', - 'static', + 'import', + 'in', + 'interface', + 'is', 'late', + 'library', + 'mixin', + 'new', + 'null', + 'on', + 'operator', + 'part', + 'required', + 'rethrow', + 'return', + 'set', + 'show', + 'static', + 'super', + 'switch', + 'sync', + 'this', + 'throw', + 'true', + 'try', + 'typedef', + 'var', + 'void', + 'while', + 'with', + 'yield', };
diff --git a/pkgs/ffigen/lib/src/code_generator/objc_interface.dart b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart new file mode 100644 index 0000000..de76111 --- /dev/null +++ b/pkgs/ffigen/lib/src/code_generator/objc_interface.dart
@@ -0,0 +1,380 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:ffigen/src/code_generator.dart'; + +import 'binding_string.dart'; +import 'utils.dart'; +import 'writer.dart'; + +// Class methods defined on NSObject that we don't want to copy to child objects +// by default. +const _excludedNSObjectClassMethods = { + 'allocWithZone:', + 'copyWithZone:', + 'mutableCopyWithZone:', + 'instancesRespondToSelector:', + 'conformsToProtocol:', + 'instanceMethodForSelector:', + 'instanceMethodSignatureForSelector:', + 'isSubclassOfClass:', + 'resolveClassMethod:', + 'resolveInstanceMethod:', + 'hash', + 'superclass', + 'class', + 'description', + 'debugDescription', +}; + +class _ObjCBuiltInFunctions { + late final _registerNameFunc = Func( + name: '_sel_registerName', + originalName: 'sel_registerName', + returnType: Type.pointer(Type.struct(objCSelType)), + parameters: [ + Parameter(name: 'str', type: Type.pointer(Type.importedType(charType))) + ], + ); + late final String registerName; + + late final _getClassFunc = Func( + name: '_objc_getClass', + originalName: 'objc_getClass', + returnType: Type.pointer(Type.struct(objCObjectType)), + parameters: [ + Parameter(name: 'str', type: Type.pointer(Type.importedType(charType))) + ], + ); + late final String getClass; + + // We need to load a separate instance of objc_msgSend for each signature. + final _msgSendFuncs = <String, Func>{}; + Func getMsgSendFunc(Type returnType, List<ObjCMethodParam> params) { + // TODO(#279): These keys don't dedupe sufficiently. + var key = returnType.hashCode.toRadixString(36); + for (final p in params) { + key += ' ' + p.type.hashCode.toRadixString(36); + } + _msgSendFuncs[key] ??= Func( + name: '_objc_msgSend_${_msgSendFuncs.length}', + originalName: 'objc_msgSend', + returnType: returnType, + parameters: [ + Parameter(name: 'obj', type: Type.pointer(Type.struct(objCObjectType))), + Parameter(name: 'sel', type: Type.pointer(Type.struct(objCSelType))), + for (final p in params) Parameter(name: p.name, type: p.type.type), + ], + ); + return _msgSendFuncs[key]!; + } + + bool utilsExist = false; + void ensureUtilsExist(Writer w, StringBuffer s) { + if (utilsExist) return; + utilsExist = true; + + registerName = w.topLevelUniqueNamer.makeUnique('_registerName'); + final selType = _registerNameFunc.functionType.returnType.getCType(w); + s.write('\n$selType $registerName(${w.className} _lib, String name) {\n'); + s.write(' final cstr = name.toNativeUtf8();\n'); + s.write(' final sel = _lib.${_registerNameFunc.name}(cstr.cast());\n'); + s.write(' ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n'); + s.write(' return sel;\n'); + s.write('}\n'); + + getClass = w.topLevelUniqueNamer.makeUnique('_getClass'); + final objType = _getClassFunc.functionType.returnType.getCType(w); + s.write('\n$objType $getClass(${w.className} _lib, String name) {\n'); + s.write(' final cstr = name.toNativeUtf8();\n'); + s.write(' final clazz = _lib.${_getClassFunc.name}(cstr.cast());\n'); + s.write(' ${w.ffiPkgLibraryPrefix}.calloc.free(cstr);\n'); + s.write(' return clazz;\n'); + s.write('}\n'); + + s.write('\nclass _ObjCWrapper {\n'); + s.write(' final $objType _id;\n'); + s.write(' final ${w.className} _lib;\n'); + s.write(' _ObjCWrapper._(this._id, this._lib);\n'); + s.write('}\n'); + } + + void addDependencies(Set<Binding> dependencies) { + _registerNameFunc.addDependencies(dependencies); + _getClassFunc.addDependencies(dependencies); + for (final func in _msgSendFuncs.values) { + func.addDependencies(dependencies); + } + } +} + +final _builtInFunctions = _ObjCBuiltInFunctions(); + +class ObjCInterface extends NoLookUpBinding { + ObjCInterface? superType; + final methods = <ObjCMethod>[]; + bool filled = false; + + // Objective C supports overriding class methods, but Dart doesn't support + // overriding static methods. So in our generated Dart code, child classes + // must explicitly implement all the class methods of their super type. To + // help with this, we store the class methods in this map, as well as in the + // methods list. + final classMethods = <String, ObjCMethod>{}; + + ObjCInterface({ + String? usr, + required String originalName, + required String name, + String? dartDoc, + }) : super( + usr: usr, + originalName: originalName, + name: name, + dartDoc: dartDoc, + ); + + @override + BindingString toBindingString(Writer w) { + final s = StringBuffer(); + if (dartDoc != null) { + s.write(makeDartDoc(dartDoc!)); + } + + final uniqueNamer = UniqueNamer({name}); + final natLib = w.className; + + _builtInFunctions.ensureUtilsExist(w, s); + final objType = Type.pointer(Type.struct(objCObjectType)).getCType(w); + final selType = Type.pointer(Type.struct(objCSelType)).getCType(w); + + // Class declaration. + s.write('class $name '); + uniqueNamer.markUsed('_id'); + s.write('extends ${superType?.name ?? '_ObjCWrapper'} {\n'); + s.write(' $name._($objType id, $natLib lib) : super._(id, lib);\n\n'); + + // Class object, used to call static methods. + final classObject = uniqueNamer.makeUnique('_class'); + s.write(' static $objType? $classObject;\n\n'); + + // Cast method. + s.write(' static $name castFrom<T extends _ObjCWrapper>(T other) {\n'); + s.write(' return $name._(other._id, other._lib);\n'); + s.write(' }\n\n'); + + // Methods. + for (final m in methods) { + final methodName = m._getDartMethodName(uniqueNamer); + final selName = uniqueNamer.makeUnique('_sel_$methodName'); + final isStatic = m.kind == ObjCMethodKind.classMethod; + + // SEL object for the method. + s.write(' static $selType? $selName;'); + + // The method declaration. + if (m.dartDoc != null) { + s.write(makeDartDoc(m.dartDoc!)); + } + s.write(' '); + if (isStatic) s.write('static '); + s.write('${m.returnType!.getConvertedType(w, name)} '); + if (m.kind == ObjCMethodKind.propertyGetter) s.write('get '); + if (m.kind == ObjCMethodKind.propertySetter) s.write('set '); + s.write(methodName); + if (m.kind != ObjCMethodKind.propertyGetter) { + s.write('('); + var first = true; + if (isStatic) { + first = false; + s.write('$natLib _lib'); + } + for (final p in m.params) { + if (first) { + first = false; + } else { + s.write(', '); + } + s.write('${p.type.getConvertedType(w, name)} ${p.name}'); + } + s.write(')'); + } + s.write(' {\n'); + + // Implementation. + if (isStatic) { + s.write(' $classObject ??= ' + '${_builtInFunctions.getClass}(_lib, "$originalName");\n'); + } + s.write(' $selName ??= ' + '${_builtInFunctions.registerName}(_lib, "${m.originalName}");\n'); + final convertReturn = m.returnType!.needsConverting; + s.write(' ${convertReturn ? 'final _ret = ' : 'return '}'); + s.write('_lib.${m.msgSend!.name}('); + s.write(isStatic ? '_class!' : '_id'); + s.write(', $selName!'); + for (final p in m.params) { + s.write(', ${p.type.doArgConversion(p.name)}'); + } + s.write(');\n'); + if (convertReturn) { + final result = m.returnType!.doReturnConversion('_ret', name, '_lib'); + s.write(' return $result;'); + } + + s.write(' }\n\n'); + } + + s.write('}\n\n'); + + return BindingString( + type: BindingStringType.objcInterface, string: s.toString()); + } + + @override + void addDependencies(Set<Binding> dependencies) { + if (dependencies.contains(this)) return; + dependencies.add(this); + + if (superType != null) { + superType!.addDependencies(dependencies); + // Copy class methods from the super type, because Dart classes don't + // inherit static methods. + for (final m in superType!.classMethods.values) { + if (!_excludedNSObjectClassMethods.contains(m.originalName)) { + addMethod(m); + } + } + } + + for (final m in methods) { + m.addDependencies(dependencies); + } + + _builtInFunctions.addDependencies(dependencies); + } + + void addMethod(ObjCMethod method) { + methods.add(method); + if (method.kind == ObjCMethodKind.classMethod) { + classMethods[method.originalName] ??= method; + } + } +} + +enum ObjCMethodKind { + instanceMethod, + classMethod, + propertyGetter, + propertySetter, +} + +class ObjCProperty { + final String originalName; + String? dartName; + ObjCProperty(this.originalName); +} + +class ObjCMethod { + final String? dartDoc; + final String originalName; + final ObjCProperty? property; + ObjCMethodType? returnType; + final params = <ObjCMethodParam>[]; + final ObjCMethodKind kind; + Func? msgSend; + + ObjCMethod({ + required this.originalName, + this.property, + this.dartDoc, + required this.kind, + }); + + void addDependencies(Set<Binding> dependencies) { + returnType!.type.addDependencies(dependencies); + for (final p in params) { + p.type.type.addDependencies(dependencies); + } + msgSend = _builtInFunctions.getMsgSendFunc(returnType!.type, params); + } + + String _getDartMethodName(UniqueNamer uniqueNamer) { + if (property != null) { + // A getter and a setter are allowed to have the same name, so we can't + // just run the name through uniqueNamer. Instead they need to share + // the dartName, which is run through uniqueNamer. + if (property!.dartName == null) { + property!.dartName = uniqueNamer.makeUnique(property!.originalName); + } + return property!.dartName!; + } + // Objective C methods can look like: + // foo + // foo: + // foo:someArgName: + // If there is a trailing ':', omit it. Replace all other ':' with '_'. + var name = originalName; + final index = name.indexOf(':'); + if (index != -1) name = name.substring(0, index); + return uniqueNamer.makeUnique(name.replaceAll(':', '_')); + } +} + +class ObjCMethodParam { + final ObjCMethodType type; + final String name; + ObjCMethodParam(Type t, this.name) : type = ObjCMethodType(t); +} + +// Wrapper around Type with helper methods for converting between the internal +// types passed to native code, and the external types visible to the user. For +// example, ObjCInterfaces are passed to native as Pointer<ObjCObject>, but the +// user sees the Dart wrapper class. +class ObjCMethodType { + final Type type; + ObjCMethodType(this.type); + + bool get isObject { + if (type.broadType != BroadType.Pointer) return false; + final child = type.child!; + if (child.broadType != BroadType.Compound) return false; + return child.compound == objCObjectType; + } + + bool get isInstanceType { + if (type.broadType != BroadType.Typealias) return false; + final alias = type.typealias!; + if (alias.name != 'instancetype') return false; + return ObjCMethodType(alias.type).isObject; + } + + bool get isInterface => type.broadType == BroadType.ObjCInterface; + bool get isBool => type.broadType == BroadType.Boolean; + bool get needsConverting => + isInterface || isBool || isObject || isInstanceType; + + String getConvertedType(Writer w, String enclosingClass) { + if (isBool) return 'bool'; + if (isInterface) return type.objCInterface!.name; + if (isObject) return 'NSObject'; + if (isInstanceType) return enclosingClass; + return type.getDartType(w); + } + + String doArgConversion(String value) { + if (isBool) return '$value ? 1 : 0'; + if (isInterface || isObject || isInstanceType) return '$value._id'; + return value; + } + + String doReturnConversion( + String value, String enclosingClass, String library) { + if (isBool) return '$value != 0'; + if (isInterface) return '${type.objCInterface!.name}._($value, $library)'; + if (isObject) return 'NSObject._($value, $library)'; + if (isInstanceType) return '$enclosingClass._($value, $library)'; + return value; + } +}
diff --git a/pkgs/ffigen/lib/src/code_generator/type.dart b/pkgs/ffigen/lib/src/code_generator/type.dart index b70b4ec..a708167 100644 --- a/pkgs/ffigen/lib/src/code_generator/type.dart +++ b/pkgs/ffigen/lib/src/code_generator/type.dart
@@ -55,6 +55,9 @@ ConstantArray, IncompleteArray, + /// Represents an Objective C interface. + ObjCInterface, + /// Used as a marker, so that declarations having these can exclude them. Unimplemented, } @@ -98,6 +101,9 @@ /// Reference to the [ImportedType] this type refers to. ImportedType? importedType; + /// Reference to the [ObjCInterface] this type refers to. + ObjCInterface? objCInterface; + /// For providing [SupportedNativeType] only. final SupportedNativeType? nativeType; @@ -123,6 +129,7 @@ this.typealias, this.functionType, this.importedType, + this.objCInterface, this.length, this.unimplementedReason, }); @@ -184,6 +191,10 @@ factory Type.handle() { return Type._(broadType: BroadType.Handle); } + factory Type.objCInterface(ObjCInterface objCInterface) { + return Type._( + broadType: BroadType.ObjCInterface, objCInterface: objCInterface); + } /// Get all dependencies of this type and save them in [dependencies]. void addDependencies(Set<Binding> dependencies) { @@ -198,6 +209,8 @@ return typealias!.addDependencies(dependencies); case BroadType.Enum: return enumClass!.addDependencies(dependencies); + case BroadType.ObjCInterface: + return objCInterface!.addDependencies(dependencies); default: if (child != null) { return child!.addDependencies(dependencies); @@ -269,13 +282,14 @@ return '${w.ffiLibraryPrefix}.${_primitives[enumNativeType]!.c}'; case BroadType.NativeFunction: return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getCType(w)}>'; - case BroadType - .IncompleteArray: // Array parameters are treated as Pointers in C. + case BroadType.IncompleteArray: + // Array parameters are treated as Pointers in C. return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>'; - case BroadType - .ConstantArray: // Array parameters are treated as Pointers in C. + case BroadType.ConstantArray: + // Array parameters are treated as Pointers in C. return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>'; - case BroadType.Boolean: // Booleans are treated as uint8. + case BroadType.Boolean: + // Booleans are treated as uint8. return '${w.ffiLibraryPrefix}.${_primitives[SupportedNativeType.Uint8]!.c}'; case BroadType.Handle: return '${w.ffiLibraryPrefix}.Handle'; @@ -283,6 +297,8 @@ return functionType!.getCType(w); case BroadType.ImportedType: return '${importedType!.libraryImport.prefix}.${importedType!.cType}'; + case BroadType.ObjCInterface: + return Type.pointer(Type.struct(objCObjectType)).getCType(w); case BroadType.Typealias: return typealias!.name; case BroadType.Unimplemented: @@ -302,13 +318,14 @@ return _primitives[enumNativeType]!.dart; case BroadType.NativeFunction: return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getDartType(w)}>'; - case BroadType - .IncompleteArray: // Array parameters are treated as Pointers in C. + case BroadType.IncompleteArray: + // Array parameters are treated as Pointers in C. return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>'; - case BroadType - .ConstantArray: // Array parameters are treated as Pointers in C. + case BroadType.ConstantArray: + // Array parameters are treated as Pointers in C. return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>'; - case BroadType.Boolean: // Booleans are treated as uint8. + case BroadType.Boolean: + // Booleans are treated as uint8. return _primitives[SupportedNativeType.Uint8]!.dart; case BroadType.Handle: return 'Object'; @@ -320,6 +337,8 @@ } else { return importedType!.dartType; } + case BroadType.ObjCInterface: + return getCType(w); case BroadType.Typealias: // Typealias cannot be used by name in Dart types unless both the C and // Dart type of the underlying types are same.
diff --git a/pkgs/ffigen/lib/src/code_generator/writer.dart b/pkgs/ffigen/lib/src/code_generator/writer.dart index 0fd7515..d3ff495 100644 --- a/pkgs/ffigen/lib/src/code_generator/writer.dart +++ b/pkgs/ffigen/lib/src/code_generator/writer.dart
@@ -21,6 +21,8 @@ final symbolAddressWriter = SymbolAddressWriter(); late String _className; + String get className => _className; + final String? classDocComment; String? _ffiLibraryPrefix; @@ -31,6 +33,14 @@ return _ffiLibraryPrefix!; } + String? _ffiPkgLibraryPrefix; + String get ffiPkgLibraryPrefix { + _ffiPkgLibraryPrefix ??= libraryImports + .firstWhere((element) => element.name == ffiPkgImport.name) + .prefix; + return _ffiPkgLibraryPrefix!; + } + final Set<LibraryImport> libraryImports; late String _lookupFuncIdentifier;
diff --git a/pkgs/ffigen/lib/src/config_provider/config_types.dart b/pkgs/ffigen/lib/src/config_provider/config_types.dart index 1f0c7a6..6ba4f4e 100644 --- a/pkgs/ffigen/lib/src/config_provider/config_types.dart +++ b/pkgs/ffigen/lib/src/config_provider/config_types.dart
@@ -29,6 +29,7 @@ } enum CommentStyle { doxygen, any } + enum CommentLength { none, brief, full } enum CompoundDependencies { full, opaque }
diff --git a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart index 2c0c33f..0b76fcb 100644 --- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart +++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -759,6 +759,24 @@ late final _clang_getArgType = _clang_getArgTypePtr.asFunction<CXType Function(CXType, int)>(); + /// Retrieves the base type of the ObjCObjectType. + /// + /// If the type is not an ObjC object, an invalid type is returned. + CXType clang_Type_getObjCObjectBaseType( + CXType T, + ) { + return _clang_Type_getObjCObjectBaseType( + T, + ); + } + + late final _clang_Type_getObjCObjectBaseTypePtr = + _lookup<ffi.NativeFunction<CXType Function(CXType)>>( + 'clang_Type_getObjCObjectBaseType'); + late final _clang_Type_getObjCObjectBaseType = + _clang_Type_getObjCObjectBaseTypePtr + .asFunction<CXType Function(CXType)>(); + /// Return the number of elements of an array or vector type. /// /// If a type is passed in that is not an array or vector type, @@ -983,6 +1001,40 @@ late final _clang_getCursorDefinition = _clang_getCursorDefinitionPtr.asFunction<CXCursor Function(CXCursor)>(); + /// Given a cursor that represents a property declaration, return the + /// name of the method that implements the getter. + CXString clang_Cursor_getObjCPropertyGetterName( + CXCursor C, + ) { + return _clang_Cursor_getObjCPropertyGetterName( + C, + ); + } + + late final _clang_Cursor_getObjCPropertyGetterNamePtr = + _lookup<ffi.NativeFunction<CXString Function(CXCursor)>>( + 'clang_Cursor_getObjCPropertyGetterName'); + late final _clang_Cursor_getObjCPropertyGetterName = + _clang_Cursor_getObjCPropertyGetterNamePtr + .asFunction<CXString Function(CXCursor)>(); + + /// Given a cursor that represents a property declaration, return the + /// name of the method that implements the setter, if any. + CXString clang_Cursor_getObjCPropertySetterName( + CXCursor C, + ) { + return _clang_Cursor_getObjCPropertySetterName( + C, + ); + } + + late final _clang_Cursor_getObjCPropertySetterNamePtr = + _lookup<ffi.NativeFunction<CXString Function(CXCursor)>>( + 'clang_Cursor_getObjCPropertySetterName'); + late final _clang_Cursor_getObjCPropertySetterName = + _clang_Cursor_getObjCPropertySetterNamePtr + .asFunction<CXString Function(CXCursor)>(); + /// Given a cursor that represents a declaration, return the associated /// comment's source range. The range may include multiple consecutive comments /// with whitespace in between.
diff --git a/pkgs/ffigen/lib/src/header_parser/includer.dart b/pkgs/ffigen/lib/src/header_parser/includer.dart index 4f3bbbf..b2791f8 100644 --- a/pkgs/ffigen/lib/src/header_parser/includer.dart +++ b/pkgs/ffigen/lib/src/header_parser/includer.dart
@@ -35,6 +35,11 @@ usr, name, bindingsIndex.isSeenType, config.functionDecl.shouldInclude); } +bool shouldIncludeInterface(String usr, String name) { + // TODO(#279): Check config YAML. + return true; +} + bool shouldIncludeEnumClass(String usr, String name) { return _shouldIncludeDecl( usr, name, bindingsIndex.isSeenType, config.enumClassDecl.shouldInclude);
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart new file mode 100644 index 0000000..1905c53 --- /dev/null +++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/objcinterfacedecl_parser.dart
@@ -0,0 +1,190 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:ffi'; + +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/header_parser/data.dart'; +import 'package:logging/logging.dart'; + +import '../clang_bindings/clang_bindings.dart' as clang_types; +import '../includer.dart'; +import '../utils.dart'; + +final _logger = Logger('ffigen.header_parser.objcinterfacedecl_parser'); + +class _ParsedObjCInterface { + ObjCInterface interface; + _ParsedObjCInterface(this.interface); +} + +class _ParsedObjCMethod { + ObjCMethod method; + bool hasError = false; + _ParsedObjCMethod(this.method); +} + +final _interfaceStack = Stack<_ParsedObjCInterface>(); +final _methodStack = Stack<_ParsedObjCMethod>(); + +Type? parseObjCInterfaceDeclaration(clang_types.CXCursor cursor) { + final itfUsr = cursor.usr(); + final itfName = cursor.spelling(); + if (!shouldIncludeInterface(itfUsr, itfName)) { + return null; + } + + final t = cursor.type(); + final name = t.spelling(); + + _logger.fine('++++ Adding ObjC interface: ' + 'Name: $name, ${cursor.completeStringRepr()}'); + + return Type.objCInterface(ObjCInterface( + usr: itfUsr, originalName: name, + name: name, // TODO(#279): config.interfaceDecl.renameUsingConfig(name), + dartDoc: getCursorDocComment(cursor), + )); +} + +void fillObjCInterfaceMethodsIfNeeded( + ObjCInterface itf, clang_types.CXCursor cursor) { + if (itf.filled) return; + itf.filled = true; // Break cycles. + + _logger.fine('++++ Filling ObjC interface: ' + 'Name: ${itf.originalName}, ${cursor.completeStringRepr()}'); + + _interfaceStack.push(_ParsedObjCInterface(itf)); + clang.clang_visitChildren( + cursor, + Pointer.fromFunction(_parseInterfaceVisitor, exceptional_visitor_return), + nullptr); + _interfaceStack.pop(); + + _logger.fine('++++ Finished ObjC interface: ' + 'Name: ${itf.originalName}, ${cursor.completeStringRepr()}'); +} + +int _parseInterfaceVisitor(clang_types.CXCursor cursor, + clang_types.CXCursor parent, Pointer<Void> clientData) { + switch (cursor.kind) { + case clang_types.CXCursorKind.CXCursor_ObjCSuperClassRef: + _parseSuperType(cursor); + break; + case clang_types.CXCursorKind.CXCursor_ObjCPropertyDecl: + _parseProperty(cursor); + break; + case clang_types.CXCursorKind.CXCursor_ObjCInstanceMethodDecl: + case clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl: + _parseMethod(cursor); + break; + } + return clang_types.CXChildVisitResult.CXChildVisit_Continue; +} + +void _parseSuperType(clang_types.CXCursor cursor) { + final superType = cursor.type().toCodeGenType(); + _logger.fine(' > Super type: ' + '$superType ${cursor.completeStringRepr()}'); + _interfaceStack.top.interface.superType = superType.objCInterface; +} + +void _parseProperty(clang_types.CXCursor cursor) { + final itf = _interfaceStack.top.interface; + final fieldName = cursor.spelling(); + final fieldType = cursor.type().toCodeGenType(); + final dartDoc = getCursorDocComment(cursor); + final property = ObjCProperty(fieldName); + _logger.fine(' > Property: ' + '$fieldType $fieldName ${cursor.completeStringRepr()}'); + + final getter = ObjCMethod( + originalName: clang + .clang_Cursor_getObjCPropertyGetterName(cursor) + .toStringAndDispose(), + property: property, + dartDoc: dartDoc, + kind: ObjCMethodKind.propertyGetter, + ); + getter.returnType = ObjCMethodType(fieldType); + itf.addMethod(getter); + + final setter = ObjCMethod( + originalName: clang + .clang_Cursor_getObjCPropertySetterName(cursor) + .toStringAndDispose(), + property: property, + dartDoc: dartDoc, + kind: ObjCMethodKind.propertySetter, + ); + setter.returnType = ObjCMethodType(Type.nativeType(SupportedNativeType.Void)); + setter.params.add(ObjCMethodParam(fieldType, 'value')); + itf.addMethod(setter); +} + +void _parseMethod(clang_types.CXCursor cursor) { + final isClassMethod = + cursor.kind == clang_types.CXCursorKind.CXCursor_ObjCClassMethodDecl; + final method = ObjCMethod( + originalName: cursor.spelling(), + dartDoc: getCursorDocComment(cursor), + kind: isClassMethod + ? ObjCMethodKind.classMethod + : ObjCMethodKind.instanceMethod, + ); + final parsed = _ParsedObjCMethod(method); + _logger.fine(' > ${isClassMethod ? 'Class' : 'Instance'} method: ' + '${method.originalName} ${cursor.completeStringRepr()}'); + _methodStack.push(parsed); + clang.clang_visitChildren( + cursor, + Pointer.fromFunction(_parseMethodVisitor, exceptional_visitor_return), + nullptr); + _methodStack.pop(); + if (parsed.hasError || method.returnType == null) { + // Discard it. + return; + } + _interfaceStack.top.interface.addMethod(method); +} + +int _parseMethodVisitor(clang_types.CXCursor cursor, + clang_types.CXCursor parent, Pointer<Void> clientData) { + switch (cursor.kind) { + case clang_types.CXCursorKind.CXCursor_TypeRef: + case clang_types.CXCursorKind.CXCursor_ObjCClassRef: + _parseMethodReturnType(cursor); + break; + case clang_types.CXCursorKind.CXCursor_ParmDecl: + _parseMethodParam(cursor); + break; + default: + } + return clang_types.CXChildVisitResult.CXChildVisit_Continue; +} + +void _parseMethodReturnType(clang_types.CXCursor cursor) { + final parsed = _methodStack.top; + if (parsed.method.returnType != null) { + parsed.hasError = true; + _logger.fine( + ' >> Extra return type: ${cursor.completeStringRepr()}'); + _logger.warning('Method "${parsed.method.originalName}" in instance ' + '"${_interfaceStack.top.interface.originalName}" has multiple return ' + 'types.'); + } else { + parsed.method.returnType = ObjCMethodType(cursor.type().toCodeGenType()); + _logger.fine(' >> Return type: ' + '${parsed.method.returnType} ${cursor.completeStringRepr()}'); + } +} + +void _parseMethodParam(clang_types.CXCursor cursor) { + final name = cursor.spelling(); + final type = cursor.type().toCodeGenType(); + _logger.fine( + ' >> Parameter: $type $name ${cursor.completeStringRepr()}'); + _methodStack.top.method.params.add(ObjCMethodParam(type, name)); +}
diff --git a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart index 60c623e..7778ddf 100644 --- a/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart +++ b/pkgs/ffigen/lib/src/header_parser/translation_unit_parser.dart
@@ -57,6 +57,9 @@ case clang_types.CXCursorKind.CXCursor_VarDecl: addToBindings(parseVarDeclaration(cursor)); break; + case clang_types.CXCursorKind.CXCursor_ObjCInterfaceDecl: + addToBindings(_getCodeGenTypeFromCursor(cursor)?.objCInterface); + break; default: _logger.finer('rootCursorVisitor: CursorKind not implemented'); }
diff --git a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart index 3c2f2dc..0f487a9 100644 --- a/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart +++ b/pkgs/ffigen/lib/src/header_parser/type_extractor/extractor.dart
@@ -13,6 +13,7 @@ import '../data.dart'; import '../sub_parsers/compounddecl_parser.dart'; import '../sub_parsers/enumdecl_parser.dart'; +import '../sub_parsers/objcinterfacedecl_parser.dart'; import '../type_extractor/cxtypekindmap.dart'; import '../utils.dart'; @@ -38,17 +39,18 @@ ignoreFilter: ignoreFilter, pointerReference: pointerReference); } - // Objective C types skip the cache, and are conditional on the language flag. + // These basic Objective C types skip the cache, and are conditional on the + // language flag. if (config.language == Language.objc) { switch (cxtype.kind) { case clang_types.CXTypeKind.CXType_ObjCObjectPointer: case clang_types.CXTypeKind.CXType_BlockPointer: case clang_types.CXTypeKind.CXType_ObjCId: + case clang_types.CXTypeKind.CXType_ObjCClass: + case clang_types.CXTypeKind.CXType_ObjCTypeParam: return Type.pointer(Type.struct(objCObjectType)); case clang_types.CXTypeKind.CXType_ObjCSel: return Type.pointer(Type.struct(objCSelType)); - case clang_types.CXTypeKind.CXType_ObjCClass: - return Type.struct(objCObjectType); } } @@ -194,9 +196,10 @@ } else { return _CreateTypeFromCursorResult(Type.enumClass(enumClass)); } + case clang_types.CXTypeKind.CXType_ObjCInterface: + return _CreateTypeFromCursorResult(parseObjCInterfaceDeclaration(cursor)); default: - throw UnimplementedError( - 'Unknown cursor kind: ${cursor.completeStringRepr()}'); + throw UnimplementedError('Unknown type: ${cxtype.completeStringRepr()}'); } } @@ -206,6 +209,8 @@ if (type.compound != null) { fillCompoundMembersIfNeeded(type.compound!, cursor, ignoreFilter: ignoreFilter, pointerReference: pointerReference); + } else if (type.objCInterface != null) { + fillObjCInterfaceMethodsIfNeeded(type.objCInterface!, cursor); } }
diff --git a/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_objc_interface_bindings.dart b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_objc_interface_bindings.dart new file mode 100644 index 0000000..e584704 --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/expected_bindings/_expected_objc_interface_bindings.dart
@@ -0,0 +1,852 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +import 'dart:ffi' as ffi; +import 'package:ffi/ffi.dart' as pkg_ffi; + +/// ObjC Interface Test +class NativeLibrary { + /// Holds the symbol lookup function. + final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeLibrary(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeLibrary.fromLookup( + ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) + lookup) + : _lookup = lookup; + + ffi.Pointer<ObjCSel> _sel_registerName( + ffi.Pointer<pkg_ffi.Char> str, + ) { + return __sel_registerName( + str, + ); + } + + late final __sel_registerNamePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCSel> Function( + ffi.Pointer<pkg_ffi.Char>)>>('sel_registerName'); + late final __sel_registerName = __sel_registerNamePtr + .asFunction<ffi.Pointer<ObjCSel> Function(ffi.Pointer<pkg_ffi.Char>)>(); + + ffi.Pointer<ObjCObject> _objc_getClass( + ffi.Pointer<pkg_ffi.Char> str, + ) { + return __objc_getClass( + str, + ); + } + + late final __objc_getClassPtr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<pkg_ffi.Char>)>>('objc_getClass'); + late final __objc_getClass = __objc_getClassPtr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<pkg_ffi.Char>)>(); + + instancetype _objc_msgSend_0( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_0( + obj, + sel, + ); + } + + late final __objc_msgSend_0Ptr = _lookup< + ffi.NativeFunction< + instancetype Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_0 = __objc_msgSend_0Ptr.asFunction< + instancetype Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + instancetype _objc_msgSend_1( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<_NSZone> zone, + ) { + return __objc_msgSend_1( + obj, + sel, + zone, + ); + } + + late final __objc_msgSend_1Ptr = _lookup< + ffi.NativeFunction< + instancetype Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<_NSZone>)>>('objc_msgSend'); + late final __objc_msgSend_1 = __objc_msgSend_1Ptr.asFunction< + instancetype Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<_NSZone>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_2( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_2( + obj, + sel, + ); + } + + late final __objc_msgSend_2Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_2 = __objc_msgSend_2Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_3( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_3( + obj, + sel, + ); + } + + late final __objc_msgSend_3Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_3 = __objc_msgSend_3Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_4( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<_NSZone> zone, + ) { + return __objc_msgSend_4( + obj, + sel, + zone, + ); + } + + late final __objc_msgSend_4Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<_NSZone>)>>('objc_msgSend'); + late final __objc_msgSend_4 = __objc_msgSend_4Ptr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<_NSZone>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_5( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<_NSZone> zone, + ) { + return __objc_msgSend_5( + obj, + sel, + zone, + ); + } + + late final __objc_msgSend_5Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<_NSZone>)>>('objc_msgSend'); + late final __objc_msgSend_5 = __objc_msgSend_5Ptr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<_NSZone>)>(); + + int _objc_msgSend_6( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_6( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_6Ptr = _lookup< + ffi.NativeFunction< + BOOL Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_6 = __objc_msgSend_6Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_7( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCObject> protocol, + ) { + return __objc_msgSend_7( + obj, + sel, + protocol, + ); + } + + late final __objc_msgSend_7Ptr = _lookup< + ffi.NativeFunction< + BOOL Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>)>>('objc_msgSend'); + late final __objc_msgSend_7 = __objc_msgSend_7Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>)>(); + + IMP _objc_msgSend_8( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_8( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_8Ptr = _lookup< + ffi.NativeFunction< + IMP Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_8 = __objc_msgSend_8Ptr.asFunction< + IMP Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>(); + + IMP _objc_msgSend_9( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_9( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_9Ptr = _lookup< + ffi.NativeFunction< + IMP Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_9 = __objc_msgSend_9Ptr.asFunction< + IMP Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_10( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_10( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_10Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_10 = __objc_msgSend_10Ptr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_11( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_11( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_11Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_11 = __objc_msgSend_11Ptr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_12( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> aSelector, + ) { + return __objc_msgSend_12( + obj, + sel, + aSelector, + ); + } + + late final __objc_msgSend_12Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_12 = __objc_msgSend_12Ptr.asFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_13( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_13( + obj, + sel, + ); + } + + late final __objc_msgSend_13Ptr = _lookup< + ffi.NativeFunction< + BOOL Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_13 = __objc_msgSend_13Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_14( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCObject> aClass, + ) { + return __objc_msgSend_14( + obj, + sel, + aClass, + ); + } + + late final __objc_msgSend_14Ptr = _lookup< + ffi.NativeFunction< + BOOL Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>)>>('objc_msgSend'); + late final __objc_msgSend_14 = __objc_msgSend_14Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>)>(); + + int _objc_msgSend_15( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> sel1, + ) { + return __objc_msgSend_15( + obj, + sel, + sel1, + ); + } + + late final __objc_msgSend_15Ptr = _lookup< + ffi.NativeFunction< + BOOL Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_15 = __objc_msgSend_15Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_16( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCSel> sel1, + ) { + return __objc_msgSend_16( + obj, + sel, + sel1, + ); + } + + late final __objc_msgSend_16Ptr = _lookup< + ffi.NativeFunction< + BOOL Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_16 = __objc_msgSend_16Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_17( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_17( + obj, + sel, + ); + } + + late final __objc_msgSend_17Ptr = _lookup< + ffi.NativeFunction< + NSUInteger Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_17 = __objc_msgSend_17Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_18( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_18( + obj, + sel, + ); + } + + late final __objc_msgSend_18Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_18 = __objc_msgSend_18Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_19( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_19( + obj, + sel, + ); + } + + late final __objc_msgSend_19Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_19 = __objc_msgSend_19Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_20( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_20( + obj, + sel, + ); + } + + late final __objc_msgSend_20Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_20 = __objc_msgSend_20Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + int _objc_msgSend_21( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ) { + return __objc_msgSend_21( + obj, + sel, + ); + } + + late final __objc_msgSend_21Ptr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>>('objc_msgSend'); + late final __objc_msgSend_21 = __objc_msgSend_21Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>)>(); + + void _objc_msgSend_22( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + int value, + ) { + return __objc_msgSend_22( + obj, + sel, + value, + ); + } + + late final __objc_msgSend_22Ptr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Int32)>>('objc_msgSend'); + late final __objc_msgSend_22 = __objc_msgSend_22Ptr.asFunction< + void Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, int)>(); + + ffi.Pointer<ObjCObject> _objc_msgSend_23( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + double someArg, + ) { + return __objc_msgSend_23( + obj, + sel, + someArg, + ); + } + + late final __objc_msgSend_23Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer<ObjCObject> Function(ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, ffi.Double)>>('objc_msgSend'); + late final __objc_msgSend_23 = __objc_msgSend_23Ptr.asFunction< + ffi.Pointer<ObjCObject> Function( + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, double)>(); + + int _objc_msgSend_24( + ffi.Pointer<ObjCObject> obj, + ffi.Pointer<ObjCSel> sel, + ffi.Pointer<ObjCObject> someArg, + ffi.Pointer<ObjCObject> otherArg, + ) { + return __objc_msgSend_24( + obj, + sel, + someArg, + otherArg, + ); + } + + late final __objc_msgSend_24Ptr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>, + ffi.Pointer<ObjCObject>)>>('objc_msgSend'); + late final __objc_msgSend_24 = __objc_msgSend_24Ptr.asFunction< + int Function(ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCSel>, + ffi.Pointer<ObjCObject>, ffi.Pointer<ObjCObject>)>(); +} + +ffi.Pointer<ObjCSel> _registerName(NativeLibrary _lib, String name) { + final cstr = name.toNativeUtf8(); + final sel = _lib._sel_registerName(cstr.cast()); + pkg_ffi.calloc.free(cstr); + return sel; +} + +ffi.Pointer<ObjCObject> _getClass(NativeLibrary _lib, String name) { + final cstr = name.toNativeUtf8(); + final clazz = _lib._objc_getClass(cstr.cast()); + pkg_ffi.calloc.free(cstr); + return clazz; +} + +class _ObjCWrapper { + final ffi.Pointer<ObjCObject> _id; + final NativeLibrary _lib; + _ObjCWrapper._(this._id, this._lib); +} + +class Foo extends NSObject { + Foo._(ffi.Pointer<ObjCObject> id, NativeLibrary lib) : super._(id, lib); + + static ffi.Pointer<ObjCObject>? _class; + + static Foo castFrom<T extends _ObjCWrapper>(T other) { + return Foo._(other._id, other._lib); + } + + static ffi.Pointer<ObjCSel>? _sel_someProperty; + int get someProperty { + _sel_someProperty ??= _registerName(_lib, "someProperty"); + return _lib._objc_msgSend_21(_id, _sel_someProperty!); + } + + static ffi.Pointer<ObjCSel>? _sel_someProperty1; + void set someProperty(int value) { + _sel_someProperty1 ??= _registerName(_lib, "setSomeProperty:"); + return _lib._objc_msgSend_22(_id, _sel_someProperty1!, value); + } + + static ffi.Pointer<ObjCSel>? _sel_aClassMethod; + static Foo aClassMethod(NativeLibrary _lib, double someArg) { + _class ??= _getClass(_lib, "Foo"); + _sel_aClassMethod ??= _registerName(_lib, "aClassMethod:"); + final _ret = _lib._objc_msgSend_23(_class!, _sel_aClassMethod!, someArg); + return Foo._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_anInstanceMethod; + int anInstanceMethod(NSObject someArg, NSObject otherArg) { + _sel_anInstanceMethod ??= + _registerName(_lib, "anInstanceMethod:withOtherArg:"); + return _lib._objc_msgSend_24( + _id, _sel_anInstanceMethod!, someArg._id, otherArg._id); + } + + static ffi.Pointer<ObjCSel>? _sel_new1; + static Foo new1(NativeLibrary _lib) { + _class ??= _getClass(_lib, "Foo"); + _sel_new1 ??= _registerName(_lib, "new"); + final _ret = _lib._objc_msgSend_0(_class!, _sel_new1!); + return Foo._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_alloc; + static Foo alloc(NativeLibrary _lib) { + _class ??= _getClass(_lib, "Foo"); + _sel_alloc ??= _registerName(_lib, "alloc"); + final _ret = _lib._objc_msgSend_0(_class!, _sel_alloc!); + return Foo._(_ret, _lib); + } +} + +class NSObject extends _ObjCWrapper { + NSObject._(ffi.Pointer<ObjCObject> id, NativeLibrary lib) : super._(id, lib); + + static ffi.Pointer<ObjCObject>? _class; + + static NSObject castFrom<T extends _ObjCWrapper>(T other) { + return NSObject._(other._id, other._lib); + } + + static ffi.Pointer<ObjCSel>? _sel_init; + NSObject init() { + _sel_init ??= _registerName(_lib, "init"); + final _ret = _lib._objc_msgSend_0(_id, _sel_init!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_new1; + static NSObject new1(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_new1 ??= _registerName(_lib, "new"); + final _ret = _lib._objc_msgSend_0(_class!, _sel_new1!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_allocWithZone; + static NSObject allocWithZone(NativeLibrary _lib, ffi.Pointer<_NSZone> zone) { + _class ??= _getClass(_lib, "NSObject"); + _sel_allocWithZone ??= _registerName(_lib, "allocWithZone:"); + final _ret = _lib._objc_msgSend_1(_class!, _sel_allocWithZone!, zone); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_alloc; + static NSObject alloc(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_alloc ??= _registerName(_lib, "alloc"); + final _ret = _lib._objc_msgSend_0(_class!, _sel_alloc!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_copy; + NSObject copy() { + _sel_copy ??= _registerName(_lib, "copy"); + final _ret = _lib._objc_msgSend_2(_id, _sel_copy!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_mutableCopy; + NSObject mutableCopy() { + _sel_mutableCopy ??= _registerName(_lib, "mutableCopy"); + final _ret = _lib._objc_msgSend_3(_id, _sel_mutableCopy!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_copyWithZone; + static NSObject copyWithZone(NativeLibrary _lib, ffi.Pointer<_NSZone> zone) { + _class ??= _getClass(_lib, "NSObject"); + _sel_copyWithZone ??= _registerName(_lib, "copyWithZone:"); + final _ret = _lib._objc_msgSend_4(_class!, _sel_copyWithZone!, zone); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_mutableCopyWithZone; + static NSObject mutableCopyWithZone( + NativeLibrary _lib, ffi.Pointer<_NSZone> zone) { + _class ??= _getClass(_lib, "NSObject"); + _sel_mutableCopyWithZone ??= _registerName(_lib, "mutableCopyWithZone:"); + final _ret = _lib._objc_msgSend_5(_class!, _sel_mutableCopyWithZone!, zone); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_instancesRespondToSelector; + static int instancesRespondToSelector( + NativeLibrary _lib, ffi.Pointer<ObjCSel> aSelector) { + _class ??= _getClass(_lib, "NSObject"); + _sel_instancesRespondToSelector ??= + _registerName(_lib, "instancesRespondToSelector:"); + return _lib._objc_msgSend_6( + _class!, _sel_instancesRespondToSelector!, aSelector); + } + + static ffi.Pointer<ObjCSel>? _sel_conformsToProtocol; + static int conformsToProtocol(NativeLibrary _lib, NSObject protocol) { + _class ??= _getClass(_lib, "NSObject"); + _sel_conformsToProtocol ??= _registerName(_lib, "conformsToProtocol:"); + return _lib._objc_msgSend_7( + _class!, _sel_conformsToProtocol!, protocol._id); + } + + static ffi.Pointer<ObjCSel>? _sel_methodForSelector; + IMP methodForSelector(ffi.Pointer<ObjCSel> aSelector) { + _sel_methodForSelector ??= _registerName(_lib, "methodForSelector:"); + return _lib._objc_msgSend_8(_id, _sel_methodForSelector!, aSelector); + } + + static ffi.Pointer<ObjCSel>? _sel_instanceMethodForSelector; + static IMP instanceMethodForSelector( + NativeLibrary _lib, ffi.Pointer<ObjCSel> aSelector) { + _class ??= _getClass(_lib, "NSObject"); + _sel_instanceMethodForSelector ??= + _registerName(_lib, "instanceMethodForSelector:"); + return _lib._objc_msgSend_9( + _class!, _sel_instanceMethodForSelector!, aSelector); + } + + static ffi.Pointer<ObjCSel>? _sel_forwardingTargetForSelector; + NSObject forwardingTargetForSelector(ffi.Pointer<ObjCSel> aSelector) { + _sel_forwardingTargetForSelector ??= + _registerName(_lib, "forwardingTargetForSelector:"); + final _ret = _lib._objc_msgSend_10( + _id, _sel_forwardingTargetForSelector!, aSelector); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_methodSignatureForSelector; + NSMethodSignature methodSignatureForSelector(ffi.Pointer<ObjCSel> aSelector) { + _sel_methodSignatureForSelector ??= + _registerName(_lib, "methodSignatureForSelector:"); + final _ret = + _lib._objc_msgSend_11(_id, _sel_methodSignatureForSelector!, aSelector); + return NSMethodSignature._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_instanceMethodSignatureForSelector; + static NSMethodSignature instanceMethodSignatureForSelector( + NativeLibrary _lib, ffi.Pointer<ObjCSel> aSelector) { + _class ??= _getClass(_lib, "NSObject"); + _sel_instanceMethodSignatureForSelector ??= + _registerName(_lib, "instanceMethodSignatureForSelector:"); + final _ret = _lib._objc_msgSend_12( + _class!, _sel_instanceMethodSignatureForSelector!, aSelector); + return NSMethodSignature._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_allowsWeakReference; + int allowsWeakReference() { + _sel_allowsWeakReference ??= _registerName(_lib, "allowsWeakReference"); + return _lib._objc_msgSend_13(_id, _sel_allowsWeakReference!); + } + + static ffi.Pointer<ObjCSel>? _sel_retainWeakReference; + int retainWeakReference() { + _sel_retainWeakReference ??= _registerName(_lib, "retainWeakReference"); + return _lib._objc_msgSend_13(_id, _sel_retainWeakReference!); + } + + static ffi.Pointer<ObjCSel>? _sel_isSubclassOfClass; + static int isSubclassOfClass(NativeLibrary _lib, NSObject aClass) { + _class ??= _getClass(_lib, "NSObject"); + _sel_isSubclassOfClass ??= _registerName(_lib, "isSubclassOfClass:"); + return _lib._objc_msgSend_14(_class!, _sel_isSubclassOfClass!, aClass._id); + } + + static ffi.Pointer<ObjCSel>? _sel_resolveClassMethod; + static int resolveClassMethod(NativeLibrary _lib, ffi.Pointer<ObjCSel> sel) { + _class ??= _getClass(_lib, "NSObject"); + _sel_resolveClassMethod ??= _registerName(_lib, "resolveClassMethod:"); + return _lib._objc_msgSend_15(_class!, _sel_resolveClassMethod!, sel); + } + + static ffi.Pointer<ObjCSel>? _sel_resolveInstanceMethod; + static int resolveInstanceMethod( + NativeLibrary _lib, ffi.Pointer<ObjCSel> sel) { + _class ??= _getClass(_lib, "NSObject"); + _sel_resolveInstanceMethod ??= + _registerName(_lib, "resolveInstanceMethod:"); + return _lib._objc_msgSend_16(_class!, _sel_resolveInstanceMethod!, sel); + } + + static ffi.Pointer<ObjCSel>? _sel_hash; + static int hash(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_hash ??= _registerName(_lib, "hash"); + return _lib._objc_msgSend_17(_class!, _sel_hash!); + } + + static ffi.Pointer<ObjCSel>? _sel_superclass; + static NSObject superclass(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_superclass ??= _registerName(_lib, "superclass"); + final _ret = _lib._objc_msgSend_18(_class!, _sel_superclass!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_class1; + static NSObject class1(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_class1 ??= _registerName(_lib, "class"); + final _ret = _lib._objc_msgSend_19(_class!, _sel_class1!); + return NSObject._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_description; + static NSString description(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_description ??= _registerName(_lib, "description"); + final _ret = _lib._objc_msgSend_20(_class!, _sel_description!); + return NSString._(_ret, _lib); + } + + static ffi.Pointer<ObjCSel>? _sel_debugDescription; + static NSString debugDescription(NativeLibrary _lib) { + _class ??= _getClass(_lib, "NSObject"); + _sel_debugDescription ??= _registerName(_lib, "debugDescription"); + final _ret = _lib._objc_msgSend_20(_class!, _sel_debugDescription!); + return NSString._(_ret, _lib); + } +} + +typedef instancetype = ffi.Pointer<ObjCObject>; + +class ObjCObject extends ffi.Opaque {} + +class _NSZone extends ffi.Opaque {} + +typedef BOOL = pkg_ffi.SignedChar; + +class ObjCSel extends ffi.Opaque {} + +typedef IMP = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>; + +class NSMethodSignature extends _ObjCWrapper { + NSMethodSignature._(ffi.Pointer<ObjCObject> id, NativeLibrary lib) + : super._(id, lib); + + static ffi.Pointer<ObjCObject>? _class; + + static NSMethodSignature castFrom<T extends _ObjCWrapper>(T other) { + return NSMethodSignature._(other._id, other._lib); + } +} + +typedef NSUInteger = pkg_ffi.UnsignedLong; + +class NSString extends _ObjCWrapper { + NSString._(ffi.Pointer<ObjCObject> id, NativeLibrary lib) : super._(id, lib); + + static ffi.Pointer<ObjCObject>? _class; + + static NSString castFrom<T extends _ObjCWrapper>(T other) { + return NSString._(other._id, other._lib); + } +}
diff --git a/pkgs/ffigen/test/header_parser_tests/objc_basic_types.h b/pkgs/ffigen/test/header_parser_tests/objc_basic_types.h index a2f7fc9..c8d4f31 100644 --- a/pkgs/ffigen/test/header_parser_tests/objc_basic_types.h +++ b/pkgs/ffigen/test/header_parser_tests/objc_basic_types.h
@@ -3,6 +3,6 @@ id anId; SEL selector; NSObject* object; - Class* clazz; + Class clazz; int32_t (^blockThatReturnsAnInt)(void); };
diff --git a/pkgs/ffigen/test/header_parser_tests/objc_basic_types_test.dart b/pkgs/ffigen/test/header_parser_tests/objc_basic_types_test.dart index 8ee93f4..252683e 100644 --- a/pkgs/ffigen/test/header_parser_tests/objc_basic_types_test.dart +++ b/pkgs/ffigen/test/header_parser_tests/objc_basic_types_test.dart
@@ -23,7 +23,7 @@ actual = parser.parse( Config.fromYaml(yaml.loadYaml(''' ${strings.name}: 'NativeLibrary' -${strings.description}: 'Opaque Dependencies Test' +${strings.description}: 'ObjC Basic Types Test' ${strings.output}: 'unused' ${strings.language}: '${strings.langObjC}' ${strings.headers}:
diff --git a/pkgs/ffigen/test/header_parser_tests/objc_interface.h b/pkgs/ffigen/test/header_parser_tests/objc_interface.h new file mode 100644 index 0000000..ee299bf --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/objc_interface.h
@@ -0,0 +1,16 @@ +// This is the Foo interface. +@interface Foo : NSObject { + // This is an instance variable. They are private, so are ignored. + double instVar; +} + +// This is a property. We generate getters and setters for them. +@property int32_t someProperty; + +// This is a class method, so becomes a static function. ++ (Foo*)aClassMethod:(double)someArg; + +// This is an instance method, so becomes a regular method. +- (int32_t)anInstanceMethod:(NSString*)someArg withOtherArg:(Foo*)otherArg; + +@end
diff --git a/pkgs/ffigen/test/header_parser_tests/objc_interface_test.dart b/pkgs/ffigen/test/header_parser_tests/objc_interface_test.dart new file mode 100644 index 0000000..b614f3d --- /dev/null +++ b/pkgs/ffigen/test/header_parser_tests/objc_interface_test.dart
@@ -0,0 +1,48 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// Objective C support is only available on mac. +@TestOn('mac-os') + +import 'package:ffigen/src/code_generator.dart'; +import 'package:ffigen/src/config_provider.dart'; +import 'package:ffigen/src/header_parser.dart' as parser; +import 'package:ffigen/src/strings.dart' as strings; +import 'package:logging/logging.dart'; +import 'package:test/test.dart'; +import 'package:yaml/yaml.dart' as yaml; + +import '../test_utils.dart'; + +late Library actual; +void main() { + group('objc_interface_test', () { + setUpAll(() { + logWarnings(Level.SEVERE); + actual = parser.parse( + Config.fromYaml(yaml.loadYaml(''' +${strings.name}: 'NativeLibrary' +${strings.description}: 'ObjC Interface Test' +${strings.output}: 'unused' +${strings.language}: '${strings.langObjC}' +${strings.headers}: + ${strings.entryPoints}: + - 'test/header_parser_tests/objc_interface.h' +''') as yaml.YamlMap), + ); + }); + test('Expected bindings', () { + matchLibraryWithExpected(actual, [ + 'test', + 'debug_generated', + 'header_parser_objc_interface_test_output.dart' + ], [ + 'test', + 'header_parser_tests', + 'expected_bindings', + '_expected_objc_interface_bindings.dart' + ]); + }); + }); +}
diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml index c6940ac..37d96f9 100644 --- a/pkgs/ffigen/tool/libclang_config.yaml +++ b/pkgs/ffigen/tool/libclang_config.yaml
@@ -109,3 +109,6 @@ - clang_getCursorDefinition - clang_Cursor_isNull - clang_Cursor_hasAttrs + - clang_Type_getObjCObjectBaseType + - clang_Cursor_getObjCPropertyGetterName + - clang_Cursor_getObjCPropertySetterName