diff --git a/lib/src/code_generator/compound.dart b/lib/src/code_generator/compound.dart index db755826..c3eb55e9 100644 --- a/lib/src/code_generator/compound.dart +++ b/lib/src/code_generator/compound.dart @@ -120,7 +120,7 @@ abstract class Compound extends BindingType { /// Marking type names because dart doesn't allow class member to have the /// same name as a type name used internally. for (final m in members) { - localUniqueNamer.markUsed(m.type.getDartType(w)); + localUniqueNamer.markUsed(m.type.getFfiDartType(w)); } /// Write @Packed(X) annotation if struct is packed. @@ -148,7 +148,7 @@ abstract class Compound extends BindingType { if (!sameDartAndCType(m.type, w)) { s.write('$depth@${m.type.getCType(w)}()\n'); } - s.write('${depth}external ${m.type.getDartType(w)} ${m.name};\n\n'); + s.write('${depth}external ${m.type.getFfiDartType(w)} ${m.name};\n\n'); } } s.write('}\n\n'); diff --git a/lib/src/code_generator/enum_class.dart b/lib/src/code_generator/enum_class.dart index 081c864f..70d142b6 100644 --- a/lib/src/code_generator/enum_class.dart +++ b/lib/src/code_generator/enum_class.dart @@ -84,7 +84,7 @@ class EnumClass extends BindingType { String getCType(Writer w) => nativeType.getCType(w); @override - String getDartType(Writer w) => nativeType.getDartType(w); + String getFfiDartType(Writer w) => nativeType.getFfiDartType(w); @override String? getDefaultValue(Writer w, String nativeLib) => '0'; diff --git a/lib/src/code_generator/func.dart b/lib/src/code_generator/func.dart index f8d32e24..f8a81773 100644 --- a/lib/src/code_generator/func.dart +++ b/lib/src/code_generator/func.dart @@ -120,7 +120,7 @@ class Func extends LookUpBinding { : functionType.getCType(w, writeArgumentNames: false); final dartType = exposeFunctionTypedefs ? _exposedDartFunctionTypealias!.name - : functionType.getDartType(w, writeArgumentNames: false); + : functionType.getFfiDartType(w, writeArgumentNames: false); if (ffiNativeConfig.enabled) { final assetString = ffiNativeConfig.asset != null @@ -131,17 +131,17 @@ class Func extends LookUpBinding { "@${w.ffiLibraryPrefix}.Native<$cType>(symbol: '$originalName'$assetString$isLeafString)\n"); s.write( - 'external ${functionType.returnType.getDartType(w)} $enclosingFuncName(\n'); + 'external ${functionType.returnType.getFfiDartType(w)} $enclosingFuncName(\n'); for (final p in functionType.dartTypeParameters) { - s.write(' ${p.type.getDartType(w)} ${p.name},\n'); + s.write(' ${p.type.getFfiDartType(w)} ${p.name},\n'); } s.write(');\n\n'); } else { // Write enclosing function. s.write( - '${functionType.returnType.getDartType(w)} $enclosingFuncName(\n'); + '${functionType.returnType.getFfiDartType(w)} $enclosingFuncName(\n'); for (final p in functionType.dartTypeParameters) { - s.write(' ${p.type.getDartType(w)} ${p.name},\n'); + s.write(' ${p.type.getFfiDartType(w)} ${p.name},\n'); } s.write(') {\n'); s.write('return $funcVarName'); diff --git a/lib/src/code_generator/func_type.dart b/lib/src/code_generator/func_type.dart index 675c85b1..3ee17d59 100644 --- a/lib/src/code_generator/func_type.dart +++ b/lib/src/code_generator/func_type.dart @@ -65,16 +65,16 @@ class FunctionType extends Type { } @override - String getDartType(Writer w, {bool writeArgumentNames = true}) { + String getFfiDartType(Writer w, {bool writeArgumentNames = true}) { final sb = StringBuffer(); // Write return Type. - sb.write(returnType.getDartType(w)); + sb.write(returnType.getFfiDartType(w)); // Write Function. sb.write(' Function('); sb.write(dartTypeParameters.map((p) { - return '${p.type.getDartType(w)} ${writeArgumentNames ? p.name : ""}'; + return '${p.type.getFfiDartType(w)} ${writeArgumentNames ? p.name : ""}'; }).join(', ')); sb.write(')'); @@ -137,7 +137,7 @@ class NativeFunc extends Type { '${w.ffiLibraryPrefix}.NativeFunction<${_type.getCType(w)}>'; @override - String getDartType(Writer w) => getCType(w); + String getFfiDartType(Writer w) => getCType(w); @override String toString() => 'NativeFunction<${_type.toString()}>'; diff --git a/lib/src/code_generator/global.dart b/lib/src/code_generator/global.dart index 579d8dda..5f2e6397 100644 --- a/lib/src/code_generator/global.dart +++ b/lib/src/code_generator/global.dart @@ -45,7 +45,7 @@ class Global extends LookUpBinding { s.write(makeDartDoc(dartDoc!)); } final pointerName = w.wrapperLevelUniqueNamer.makeUnique('_$globalVarName'); - final dartType = type.getDartType(w); + final dartType = type.getFfiDartType(w); final cType = type.getCType(w); s.write( diff --git a/lib/src/code_generator/handle.dart b/lib/src/code_generator/handle.dart index a1b2d47e..c3817f23 100644 --- a/lib/src/code_generator/handle.dart +++ b/lib/src/code_generator/handle.dart @@ -16,7 +16,7 @@ class HandleType extends Type { String getCType(Writer w) => '${w.ffiLibraryPrefix}.Handle'; @override - String getDartType(Writer w) => 'Object'; + String getFfiDartType(Writer w) => 'Object'; @override String toString() => 'Handle'; diff --git a/lib/src/code_generator/imports.dart b/lib/src/code_generator/imports.dart index 93af5ad1..f75a2677 100644 --- a/lib/src/code_generator/imports.dart +++ b/lib/src/code_generator/imports.dart @@ -40,7 +40,7 @@ class ImportedType extends Type { } @override - String getDartType(Writer w) => cType == dartType ? getCType(w) : dartType; + String getFfiDartType(Writer w) => cType == dartType ? getCType(w) : dartType; @override String toString() => '${libraryImport.name}.$cType'; @@ -62,7 +62,7 @@ class SelfImportedType extends Type { String getCType(Writer w) => cType; @override - String getDartType(Writer w) => dartType; + String getFfiDartType(Writer w) => dartType; @override String toString() => cType; diff --git a/lib/src/code_generator/native_type.dart b/lib/src/code_generator/native_type.dart index dfb43a57..19425bab 100644 --- a/lib/src/code_generator/native_type.dart +++ b/lib/src/code_generator/native_type.dart @@ -54,7 +54,7 @@ class NativeType extends Type { String getCType(Writer w) => '${w.ffiLibraryPrefix}.$_cType'; @override - String getDartType(Writer w) => _dartType; + String getFfiDartType(Writer w) => _dartType; @override String toString() => _cType; @@ -67,7 +67,6 @@ class NativeType extends Type { } class BooleanType extends NativeType { - // Booleans are treated as uint8. const BooleanType._() : super._('Bool', 'bool', 'false'); static const _boolean = BooleanType._(); factory BooleanType() => _boolean; diff --git a/lib/src/code_generator/objc_block.dart b/lib/src/code_generator/objc_block.dart index 7bbb4c0e..4c82a913 100644 --- a/lib/src/code_generator/objc_block.dart +++ b/lib/src/code_generator/objc_block.dart @@ -82,15 +82,15 @@ class ObjCBlock extends BindingType { '${w.ffiLibraryPrefix}.NativeCallable<${trampFuncType.getCType(w)}>'; // Write the function pointer based trampoline function. - s.write(returnType.getDartType(w)); + s.write(returnType.getFfiDartType(w)); s.write(' $funcPtrTrampoline(${blockPtr.getCType(w)} block'); for (int i = 0; i < params.length; ++i) { - s.write(', ${params[i].type.getDartType(w)} ${params[i].name}'); + s.write(', ${params[i].type.getFfiDartType(w)} ${params[i].name}'); } s.write(') {\n'); s.write(' ${isVoid ? '' : 'return '}block.ref.target.cast<' - '${natFnType.getDartType(w)}>().asFunction<' - '${funcType.getDartType(w)}>()('); + '${natFnType.getFfiDartType(w)}>().asFunction<' + '${funcType.getFfiDartType(w)}>()('); for (int i = 0; i < params.length; ++i) { s.write('${i == 0 ? '' : ', '}${params[i].name}'); } @@ -109,17 +109,17 @@ $voidPtr $registerClosure(Function fn) { '''); // Write the closure based trampoline function. - s.write(returnType.getDartType(w)); + s.write(returnType.getFfiDartType(w)); s.write(' $closureTrampoline(${blockPtr.getCType(w)} block'); for (int i = 0; i < params.length; ++i) { - s.write(', ${params[i].type.getDartType(w)} ${params[i].name}'); + s.write(', ${params[i].type.getFfiDartType(w)} ${params[i].name}'); } s.write(') {\n'); s.write(' ${isVoid ? '' : 'return '}'); s.write('($closureRegistry[block.ref.target.address]'); - s.write(' as ${returnType.getDartType(w)} Function('); + s.write(' as ${returnType.getFfiDartType(w)} Function('); for (int i = 0; i < params.length; ++i) { - s.write('${i == 0 ? '' : ', '}${params[i].type.getDartType(w)}'); + s.write('${i == 0 ? '' : ', '}${params[i].type.getFfiDartType(w)}'); } s.write('))'); s.write('('); @@ -154,7 +154,7 @@ class $name extends _ObjCBlockBase { /// This block must be invoked by native code running on the same thread as /// the isolate that registered it. Invoking the block on the wrong thread /// will result in a crash. - $name.fromFunction(${w.className} lib, ${funcType.getDartType(w)} fn) : + $name.fromFunction(${w.className} lib, ${funcType.getFfiDartType(w)} fn) : this._(lib.${builtInFunctions.newBlock.name}( _dartFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction< ${trampFuncType.getCType(w)}>($closureTrampoline @@ -175,7 +175,7 @@ class $name extends _ObjCBlockBase { /// /// Note that unlike the default behavior of NativeCallable.listener, listener /// blocks do not keep the isolate alive. - $name.listener(${w.className} lib, ${funcType.getDartType(w)} fn) : + $name.listener(${w.className} lib, ${funcType.getFfiDartType(w)} fn) : this._(lib.${builtInFunctions.newBlock.name}( (_dartFuncListenerTrampoline ??= $nativeCallableType.listener($closureTrampoline $exceptionalReturn)..keepIsolateAlive = false).nativeFunction.cast(), @@ -186,15 +186,15 @@ class $name extends _ObjCBlockBase { } // Call method. - s.write(' ${returnType.getDartType(w)} call('); + s.write(' ${returnType.getFfiDartType(w)} call('); for (int i = 0; i < params.length; ++i) { - s.write('${i == 0 ? '' : ', '}${params[i].type.getDartType(w)}'); + s.write('${i == 0 ? '' : ', '}${params[i].type.getFfiDartType(w)}'); s.write(' ${params[i].name}'); } s.write(''') { ${isVoid ? '' : 'return '}_id.ref.invoke.cast< ${natTrampFnType.getCType(w)}>().asFunction< - ${trampFuncType.getDartType(w)}>()(_id'''); + ${trampFuncType.getFfiDartType(w)}>()(_id'''); for (int i = 0; i < params.length; ++i) { s.write(', ${params[i].name}'); } @@ -222,6 +222,9 @@ class $name extends _ObjCBlockBase { String getCType(Writer w) => PointerType(builtInFunctions.blockStruct).getCType(w); + @override + String getDartType(Writer w) => name; + @override String toString() => '($returnType (^)(${argTypes.join(', ')}))'; } diff --git a/lib/src/code_generator/objc_interface.dart b/lib/src/code_generator/objc_interface.dart index 782d75c9..12ebd14e 100644 --- a/lib/src/code_generator/objc_interface.dart +++ b/lib/src/code_generator/objc_interface.dart @@ -5,7 +5,6 @@ import 'package:ffigen/src/code_generator.dart'; import 'package:logging/logging.dart'; -import '../strings.dart' as strings; import 'binding_string.dart'; import 'utils.dart'; import 'writer.dart'; @@ -281,7 +280,7 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} { if (m.isClass && !_excludedNSObjectClassMethods.contains(m.originalName)) { addMethod(m); - } else if (_isInstanceType(m.returnType)) { + } else if (m.returnType is ObjCInstanceType) { addMethod(m); } } @@ -355,13 +354,8 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} { @override String getCType(Writer w) => PointerType(objCObjectType).getCType(w); - bool _isObject(Type type) => - type is PointerType && type.child == objCObjectType; - - bool _isInstanceType(Type type) => - type is Typealias && - type.originalName == strings.objcInstanceType && - _isObject(type.type); + @override + String getDartType(Writer w) => name; // Utils for converting between the internal types passed to native code, and // the external types visible to the user. For example, ObjCInterfaces are @@ -370,15 +364,11 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} { bool _needsConverting(Type type) => type is ObjCInterface || type is ObjCBlock || - _isObject(type) || - _isInstanceType(type); + type is ObjCObjectPointer || + type is ObjCInstanceType; String _getConvertedType(Type type, Writer w, String enclosingClass) { - if (type is BooleanType) return 'bool'; - if (type is ObjCInterface) return type.name; - if (type is ObjCBlock) return type.name; - if (_isObject(type)) return 'NSObject'; - if (_isInstanceType(type)) return enclosingClass; + if (type is ObjCInstanceType) return enclosingClass; return type.getDartType(w); } @@ -393,8 +383,8 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} { String _doArgConversion(ObjCMethodParam arg) { if (arg.type is ObjCInterface || - _isObject(arg.type) || - _isInstanceType(arg.type) || + arg.type is ObjCObjectPointer || + arg.type is ObjCInstanceType || arg.type is ObjCBlock) { if (arg.isNullable) { return '${arg.name}?._id ?? ffi.nullptr'; @@ -415,10 +405,10 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} { if (type is ObjCBlock) { return '$prefix${type.name}._($value, $library)'; } - if (_isObject(type)) { + if (type is ObjCObjectPointer) { return '${prefix}NSObject._($value, $library, $ownerFlags)'; } - if (_isInstanceType(type)) { + if (type is ObjCInstanceType) { return '$prefix$enclosingClass._($value, $library, $ownerFlags)'; } return prefix + value; diff --git a/lib/src/code_generator/pointer.dart b/lib/src/code_generator/pointer.dart index cacb5b2f..f99ee53b 100644 --- a/lib/src/code_generator/pointer.dart +++ b/lib/src/code_generator/pointer.dart @@ -9,7 +9,15 @@ import 'writer.dart'; /// Represents a pointer. class PointerType extends Type { final Type child; - PointerType(this.child); + + PointerType._(this.child); + + factory PointerType(Type child) { + if (child == objCObjectType) { + return ObjCObjectPointer(); + } + return PointerType._(child); + } @override void addDependencies(Set dependencies) { @@ -33,7 +41,7 @@ class PointerType extends Type { /// Represents a constant array, which has a fixed size. class ConstantArray extends PointerType { final int length; - ConstantArray(this.length, Type child) : super(child); + ConstantArray(this.length, Type child) : super._(child); @override Type get baseArrayType => child.baseArrayType; @@ -50,7 +58,7 @@ class ConstantArray extends PointerType { /// Represents an incomplete array, which has an unknown size. class IncompleteArray extends PointerType { - IncompleteArray(Type child) : super(child); + IncompleteArray(Type child) : super._(child); @override Type get baseArrayType => child.baseArrayType; @@ -61,3 +69,14 @@ class IncompleteArray extends PointerType { @override String cacheKey() => '${child.cacheKey()}[]'; } + +/// A pointer to an NSObject. +class ObjCObjectPointer extends PointerType { + factory ObjCObjectPointer() => _inst; + + static final _inst = ObjCObjectPointer._(); + ObjCObjectPointer._() : super._(objCObjectType); + + @override + String getDartType(Writer w) => 'NSObject'; +} diff --git a/lib/src/code_generator/type.dart b/lib/src/code_generator/type.dart index 8468eca3..1a69b607 100644 --- a/lib/src/code_generator/type.dart +++ b/lib/src/code_generator/type.dart @@ -39,9 +39,14 @@ abstract class Type { /// passed to native code. String getCType(Writer w) => throw 'No mapping for type: $this'; - /// Returns the Dart type of the Type. This is the user visible type that is - /// passed to Dart code. - String getDartType(Writer w) => getCType(w); + /// Returns the Dart type of the Type. This is the type that is passed from + /// FFI to Dart code. + String getFfiDartType(Writer w) => getCType(w); + + /// Returns the user type of the Type. This is the type that is presented to + /// users by the ffigened API to users. For C bindings this is always the same + /// as getFfiDartType. For ObjC bindings this refers to the wrapper object. + String getDartType(Writer w) => getFfiDartType(w); /// Returns the string representation of the Type, for debugging purposes /// only. This string should not be printed as generated code. @@ -62,7 +67,7 @@ abstract class Type { } /// Function to check if the dart and C type string are same. -bool sameDartAndCType(Type t, Writer w) => t.getCType(w) == t.getDartType(w); +bool sameDartAndCType(Type t, Writer w) => t.getCType(w) == t.getFfiDartType(w); /// Base class for all Type bindings. /// @@ -97,7 +102,10 @@ abstract class BindingType extends NoLookUpBinding implements Type { bool get isIncompleteCompound => false; @override - String getDartType(Writer w) => getCType(w); + String getFfiDartType(Writer w) => getCType(w); + + @override + String getDartType(Writer w) => getFfiDartType(w); @override String toString() => originalName; diff --git a/lib/src/code_generator/typealias.dart b/lib/src/code_generator/typealias.dart index c7d95ca5..469a40b6 100644 --- a/lib/src/code_generator/typealias.dart +++ b/lib/src/code_generator/typealias.dart @@ -4,6 +4,7 @@ import 'package:ffigen/src/code_generator.dart'; +import '../strings.dart' as strings; import 'binding_string.dart'; import 'utils.dart'; import 'writer.dart'; @@ -40,6 +41,18 @@ class Typealias extends BindingType { isInternal: isInternal, ))); } + if ((originalName ?? name) == strings.objcInstanceType && + type is ObjCObjectPointer) { + return ObjCInstanceType._( + usr: usr, + originalName: originalName, + dartDoc: dartDoc, + name: name, + type: type, + useDartType: useDartType, + isInternal: isInternal, + ); + } return Typealias._( usr: usr, originalName: originalName, @@ -90,7 +103,7 @@ class Typealias extends BindingType { sb.write(makeDartDoc(dartDoc!)); } sb.write('typedef $name = '); - sb.write('${_useDartType ? type.getDartType(w) : type.getCType(w)};\n'); + sb.write('${_useDartType ? type.getFfiDartType(w) : type.getCType(w)};\n'); return BindingString( type: BindingStringType.typeDef, string: sb.toString()); } @@ -105,13 +118,13 @@ class Typealias extends BindingType { String getCType(Writer w) => name; @override - String getDartType(Writer w) { + String getFfiDartType(Writer w) { // Typealias cannot be used by name in Dart types unless both the C and Dart // type of the underlying types are same. if (sameDartAndCType(type, w)) { return name; } else { - return type.getDartType(w); + return type.getFfiDartType(w); } } @@ -122,3 +135,32 @@ class Typealias extends BindingType { String? getDefaultValue(Writer w, String nativeLib) => type.getDefaultValue(w, nativeLib); } + +/// Objective C's instancetype. +/// +/// This is an alias for an NSObject* that is special cased in code generation. +/// It's only valid as the return type of a method, and always appears as the +/// enclosing class's type, even in inherited methods. +class ObjCInstanceType extends Typealias { + ObjCInstanceType._({ + String? usr, + String? originalName, + String? dartDoc, + required String name, + required Type type, + + /// If true, the binding string uses Dart type instead of C type. + /// + /// E.g if C type is ffi.Void func(ffi.Int32), Dart type is void func(int). + bool useDartType = false, + bool isInternal = false, + }) : super._( + usr: usr, + originalName: originalName, + dartDoc: dartDoc, + name: name, + type: type, + useDartType: useDartType, + isInternal: isInternal, + ); +}