From f617a3749bfdfdbe76477a874db362046cbf5a3c Mon Sep 17 00:00:00 2001 From: Prerak Mann Date: Thu, 12 Dec 2024 04:12:36 +0530 Subject: [PATCH] Handle dart typedefs in import/export of symbol files. (#1790) --- pkgs/ffigen/CHANGELOG.md | 1 + .../example/shared_bindings/headers/a.h | 7 ++ .../example/shared_bindings/headers/base.h | 4 ++ .../shared_bindings/lib/generated/a_gen.dart | 55 +++++++++++++- .../lib/generated/a_shared_b_gen.dart | 42 +++++++++++ .../lib/generated/base_gen.dart | 16 ++++- .../lib/generated/base_symbols.yaml | 9 +++ .../lib/src/code_generator/imports.dart | 72 +++++++++++++------ .../lib/src/code_generator/typealias.dart | 16 ++--- .../ffigen/lib/src/code_generator/writer.dart | 15 +++- .../lib/src/config_provider/spec_utils.dart | 6 +- pkgs/ffigen/lib/src/strings.dart | 1 + 12 files changed, 205 insertions(+), 39 deletions(-) diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md index fbb1224a8..964eaee34 100644 --- a/pkgs/ffigen/CHANGELOG.md +++ b/pkgs/ffigen/CHANGELOG.md @@ -2,6 +2,7 @@ - Ensure that required symbols are available to FFI even when the final binary is linked with `-dead_strip`. +- Handle dart typedefs in import/export of symbol files. ## 16.0.0 diff --git a/pkgs/ffigen/example/shared_bindings/headers/a.h b/pkgs/ffigen/example/shared_bindings/headers/a.h index ef3bbea37..b2088985f 100644 --- a/pkgs/ffigen/example/shared_bindings/headers/a.h +++ b/pkgs/ffigen/example/shared_bindings/headers/a.h @@ -19,3 +19,10 @@ enum A_Enum{ void a_func1(); void a_func2(struct BaseStruct2 s, union BaseUnion2 u, BaseTypedef2 t); + +void a_func3(BaseNativeTypedef1 i); + +void a_func4(BaseNativeTypedef2 i); + +void a_func5(BaseNativeTypedef3 i); + diff --git a/pkgs/ffigen/example/shared_bindings/headers/base.h b/pkgs/ffigen/example/shared_bindings/headers/base.h index 345cac64e..61909a3fd 100644 --- a/pkgs/ffigen/example/shared_bindings/headers/base.h +++ b/pkgs/ffigen/example/shared_bindings/headers/base.h @@ -20,6 +20,9 @@ union BaseUnion2{ typedef struct BaseStruct1 BaseTypedef1; typedef struct BaseStruct2 BaseTypedef2; +typedef int BaseNativeTypedef1; +typedef BaseNativeTypedef1 BaseNativeTypedef2; +typedef BaseNativeTypedef2 BaseNativeTypedef3; enum BaseEnum{ BASE_ENUM_1, @@ -29,3 +32,4 @@ enum BaseEnum{ #define BASE_MACRO_1 1; void base_func1(BaseTypedef1 t1, BaseTypedef2 t2); + diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart index 8f92d2216..bfcf7bbde 100644 --- a/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart +++ b/pkgs/ffigen/example/shared_bindings/lib/generated/a_gen.dart @@ -63,6 +63,45 @@ class NativeLibraryA { ffi.Void Function(BaseStruct2, BaseUnion2, BaseTypedef2)>>('a_func2'); late final _a_func2 = _a_func2Ptr .asFunction(); + + void a_func3( + int i, + ) { + return _a_func3( + i, + ); + } + + late final _a_func3Ptr = + _lookup>( + 'a_func3'); + late final _a_func3 = _a_func3Ptr.asFunction(); + + void a_func4( + int i, + ) { + return _a_func4( + i, + ); + } + + late final _a_func4Ptr = + _lookup>( + 'a_func4'); + late final _a_func4 = _a_func4Ptr.asFunction(); + + void a_func5( + int i, + ) { + return _a_func5( + i, + ); + } + + late final _a_func5Ptr = + _lookup>( + 'a_func5'); + late final _a_func5 = _a_func5Ptr.asFunction(); } final class BaseStruct1 extends ffi.Struct { @@ -85,16 +124,26 @@ final class BaseUnion2 extends ffi.Union { external int a; } +typedef BaseTypedef1 = BaseStruct1; +typedef BaseTypedef2 = BaseStruct2; +typedef BaseNativeTypedef1 = ffi.Int; +typedef DartBaseNativeTypedef1 = int; +typedef BaseNativeTypedef2 = BaseNativeTypedef1; +typedef BaseNativeTypedef3 = BaseNativeTypedef2; + enum BaseEnum { BASE_ENUM_1(0), BASE_ENUM_2(1); final int value; const BaseEnum(this.value); -} -typedef BaseTypedef1 = BaseStruct1; -typedef BaseTypedef2 = BaseStruct2; + static BaseEnum fromValue(int value) => switch (value) { + 0 => BASE_ENUM_1, + 1 => BASE_ENUM_2, + _ => throw ArgumentError("Unknown value for BaseEnum: $value"), + }; +} final class A_Struct1 extends ffi.Struct { @ffi.Int() diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart index c220cab4f..730cd57cd 100644 --- a/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart +++ b/pkgs/ffigen/example/shared_bindings/lib/generated/a_shared_b_gen.dart @@ -49,6 +49,48 @@ class NativeLibraryASharedB { imp1.BaseTypedef2)>>('a_func2'); late final _a_func2 = _a_func2Ptr.asFunction< void Function(imp1.BaseStruct2, imp1.BaseUnion2, imp1.BaseTypedef2)>(); + + void a_func3( + imp1.DartBaseNativeTypedef1 i, + ) { + return _a_func3( + i, + ); + } + + late final _a_func3Ptr = + _lookup>( + 'a_func3'); + late final _a_func3 = + _a_func3Ptr.asFunction(); + + void a_func4( + imp1.DartBaseNativeTypedef1 i, + ) { + return _a_func4( + i, + ); + } + + late final _a_func4Ptr = + _lookup>( + 'a_func4'); + late final _a_func4 = + _a_func4Ptr.asFunction(); + + void a_func5( + imp1.DartBaseNativeTypedef1 i, + ) { + return _a_func5( + i, + ); + } + + late final _a_func5Ptr = + _lookup>( + 'a_func5'); + late final _a_func5 = + _a_func5Ptr.asFunction(); } final class A_Struct1 extends ffi.Struct { diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart b/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart index 729765a92..224400fd1 100644 --- a/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart +++ b/pkgs/ffigen/example/shared_bindings/lib/generated/base_gen.dart @@ -59,15 +59,25 @@ final class BaseUnion2 extends ffi.Union { external int a; } +typedef BaseTypedef1 = BaseStruct1; +typedef BaseTypedef2 = BaseStruct2; +typedef BaseNativeTypedef1 = ffi.Int; +typedef DartBaseNativeTypedef1 = int; +typedef BaseNativeTypedef2 = BaseNativeTypedef1; +typedef BaseNativeTypedef3 = BaseNativeTypedef2; + enum BaseEnum { BASE_ENUM_1(0), BASE_ENUM_2(1); final int value; const BaseEnum(this.value); -} -typedef BaseTypedef1 = BaseStruct1; -typedef BaseTypedef2 = BaseStruct2; + static BaseEnum fromValue(int value) => switch (value) { + 0 => BASE_ENUM_1, + 1 => BASE_ENUM_2, + _ => throw ArgumentError("Unknown value for BaseEnum: $value"), + }; +} const int BASE_MACRO_1 = 1; diff --git a/pkgs/ffigen/example/shared_bindings/lib/generated/base_symbols.yaml b/pkgs/ffigen/example/shared_bindings/lib/generated/base_symbols.yaml index ad455faa4..4d9ef56a5 100644 --- a/pkgs/ffigen/example/shared_bindings/lib/generated/base_symbols.yaml +++ b/pkgs/ffigen/example/shared_bindings/lib/generated/base_symbols.yaml @@ -16,6 +16,15 @@ files: name: BaseUnion1 c:@U@BaseUnion2: name: BaseUnion2 + c:base.h@T@BaseNativeTypedef1: + name: BaseNativeTypedef1 + dart-name: DartBaseNativeTypedef1 + c:base.h@T@BaseNativeTypedef2: + name: BaseNativeTypedef2 + dart-name: DartBaseNativeTypedef1 + c:base.h@T@BaseNativeTypedef3: + name: BaseNativeTypedef3 + dart-name: DartBaseNativeTypedef1 c:base.h@T@BaseTypedef1: name: BaseTypedef1 c:base.h@T@BaseTypedef2: diff --git a/pkgs/ffigen/lib/src/code_generator/imports.dart b/pkgs/ffigen/lib/src/code_generator/imports.dart index a71bbf38a..046b2c5fe 100644 --- a/pkgs/ffigen/lib/src/code_generator/imports.dart +++ b/pkgs/ffigen/lib/src/code_generator/imports.dart @@ -45,8 +45,17 @@ class ImportedType extends Type { final String nativeType; final String? defaultValue; - ImportedType(this.libraryImport, this.cType, this.dartType, this.nativeType, - [this.defaultValue]); + /// Whether the [dartType] is an import from the [libraryImport]. + final bool importedDartType; + + ImportedType( + this.libraryImport, + this.cType, + this.dartType, + this.nativeType, { + this.defaultValue, + this.importedDartType = false, + }); @override String getCType(Writer w) { @@ -55,7 +64,14 @@ class ImportedType extends Type { } @override - String getFfiDartType(Writer w) => cType == dartType ? getCType(w) : dartType; + String getFfiDartType(Writer w) { + if (importedDartType) { + w.markImportUsed(libraryImport); + return '${libraryImport.prefix}.$dartType'; + } else { + return cType == dartType ? getCType(w) : dartType; + } + } @override String getNativeType({String varName = ''}) => '$nativeType $varName'; @@ -107,30 +123,42 @@ final self = LibraryImport('self', ''); final voidType = ImportedType(ffiImport, 'Void', 'void', 'void'); -final unsignedCharType = - ImportedType(ffiImport, 'UnsignedChar', 'int', 'unsigned char', '0'); +final unsignedCharType = ImportedType( + ffiImport, 'UnsignedChar', 'int', 'unsigned char', + defaultValue: '0'); final signedCharType = - ImportedType(ffiImport, 'SignedChar', 'int', 'char', '0'); -final charType = ImportedType(ffiImport, 'Char', 'int', 'char', '0'); -final unsignedShortType = - ImportedType(ffiImport, 'UnsignedShort', 'int', 'unsigned short', '0'); -final shortType = ImportedType(ffiImport, 'Short', 'int', 'short', '0'); -final unsignedIntType = - ImportedType(ffiImport, 'UnsignedInt', 'int', 'unsigned', '0'); -final intType = ImportedType(ffiImport, 'Int', 'int', 'int', '0'); -final unsignedLongType = - ImportedType(ffiImport, 'UnsignedLong', 'int', 'unsigned long', '0'); -final longType = ImportedType(ffiImport, 'Long', 'int', 'long', '0'); + ImportedType(ffiImport, 'SignedChar', 'int', 'char', defaultValue: '0'); +final charType = + ImportedType(ffiImport, 'Char', 'int', 'char', defaultValue: '0'); +final unsignedShortType = ImportedType( + ffiImport, 'UnsignedShort', 'int', 'unsigned short', + defaultValue: '0'); +final shortType = + ImportedType(ffiImport, 'Short', 'int', 'short', defaultValue: '0'); +final unsignedIntType = ImportedType( + ffiImport, 'UnsignedInt', 'int', 'unsigned', + defaultValue: '0'); +final intType = ImportedType(ffiImport, 'Int', 'int', 'int', defaultValue: '0'); +final unsignedLongType = ImportedType( + ffiImport, 'UnsignedLong', 'int', 'unsigned long', + defaultValue: '0'); +final longType = + ImportedType(ffiImport, 'Long', 'int', 'long', defaultValue: '0'); final unsignedLongLongType = ImportedType( - ffiImport, 'UnsignedLongLong', 'int', 'unsigned long long', '0'); + ffiImport, 'UnsignedLongLong', 'int', 'unsigned long long', + defaultValue: '0'); final longLongType = - ImportedType(ffiImport, 'LongLong', 'int', 'long long', '0'); + ImportedType(ffiImport, 'LongLong', 'int', 'long long', defaultValue: '0'); -final floatType = ImportedType(ffiImport, 'Float', 'double', 'float', '0.0'); -final doubleType = ImportedType(ffiImport, 'Double', 'double', 'double', '0.0'); +final floatType = + ImportedType(ffiImport, 'Float', 'double', 'float', defaultValue: '0.0'); +final doubleType = + ImportedType(ffiImport, 'Double', 'double', 'double', defaultValue: '0.0'); -final sizeType = ImportedType(ffiImport, 'Size', 'int', 'size_t', '0'); -final wCharType = ImportedType(ffiImport, 'WChar', 'int', 'wchar_t', '0'); +final sizeType = + ImportedType(ffiImport, 'Size', 'int', 'size_t', defaultValue: '0'); +final wCharType = + ImportedType(ffiImport, 'WChar', 'int', 'wchar_t', defaultValue: '0'); final objCObjectType = ImportedType(objcPkgImport, 'ObjCObject', 'ObjCObject', 'void'); diff --git a/pkgs/ffigen/lib/src/code_generator/typealias.dart b/pkgs/ffigen/lib/src/code_generator/typealias.dart index 739e00d47..aa5110eb7 100644 --- a/pkgs/ffigen/lib/src/code_generator/typealias.dart +++ b/pkgs/ffigen/lib/src/code_generator/typealias.dart @@ -19,7 +19,7 @@ import 'writer.dart'; class Typealias extends BindingType { final Type type; String? _ffiDartAliasName; - String? _dartAliasName; + String? dartAliasName; /// Creates a Typealias. /// @@ -75,7 +75,7 @@ class Typealias extends BindingType { bool genFfiDartType = false, super.isInternal, }) : _ffiDartAliasName = genFfiDartType ? 'Dart$name' : null, - _dartAliasName = + dartAliasName = (!genFfiDartType && type is! Typealias && !type.sameDartAndCType) ? 'Dart$name' : null, @@ -95,8 +95,8 @@ class Typealias extends BindingType { if (_ffiDartAliasName != null) { _ffiDartAliasName = w.topLevelUniqueNamer.makeUnique(_ffiDartAliasName!); } - if (_dartAliasName != null) { - _dartAliasName = w.topLevelUniqueNamer.makeUnique(_dartAliasName!); + if (dartAliasName != null) { + dartAliasName = w.topLevelUniqueNamer.makeUnique(dartAliasName!); } final sb = StringBuffer(); @@ -107,8 +107,8 @@ class Typealias extends BindingType { if (_ffiDartAliasName != null) { sb.write('typedef $_ffiDartAliasName = ${type.getFfiDartType(w)};\n'); } - if (_dartAliasName != null) { - sb.write('typedef $_dartAliasName = ${type.getDartType(w)};\n'); + if (dartAliasName != null) { + sb.write('typedef $dartAliasName = ${type.getDartType(w)};\n'); } return BindingString( type: BindingStringType.typeDef, string: sb.toString()); @@ -142,8 +142,8 @@ class Typealias extends BindingType { @override String getDartType(Writer w) { if (generateBindings) { - if (_dartAliasName != null) { - return _dartAliasName!; + if (dartAliasName != null) { + return dartAliasName!; } else if (type.sameDartAndCType) { return getFfiDartType(w); } diff --git a/pkgs/ffigen/lib/src/code_generator/writer.dart b/pkgs/ffigen/lib/src/code_generator/writer.dart index 338d8b3eb..65537720f 100644 --- a/pkgs/ffigen/lib/src/code_generator/writer.dart +++ b/pkgs/ffigen/lib/src/code_generator/writer.dart @@ -390,13 +390,26 @@ class Writer { strings.ffiNative: usesFfiNative, }, strings.symbols: { - for (final b in bindings) b.usr: {strings.name: b.name}, + for (final b in bindings) b.usr: _makeSymbolMapValue(b), }, }, }, }; } + Map _makeSymbolMapValue(Binding b) { + final dartName = b is Typealias ? getTypedefDartAliasName(b) : null; + return { + strings.name: b.name, + if (dartName != null) strings.dartName: dartName, + }; + } + + String? getTypedefDartAliasName(Type b) { + if (b is! Typealias) return null; + return b.dartAliasName ?? getTypedefDartAliasName(b.type); + } + static String _objcImport(String entryPoint, String outDir) { final frameworkHeader = parseObjCFrameworkHeader(entryPoint); diff --git a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart index c76bec386..646d737f6 100644 --- a/pkgs/ffigen/lib/src/config_provider/spec_utils.dart +++ b/pkgs/ffigen/lib/src/config_provider/spec_utils.dart @@ -58,8 +58,10 @@ void loadImportedTypes(YamlMap fileConfig, for (final key in symbols.keys) { final usr = key as String; final value = symbols[usr]! as YamlMap; - final name = value['name'] as String; - usrTypeMappings[usr] = ImportedType(libraryImport, name, name, name); + final name = value[strings.name] as String; + final dartName = (value[strings.dartName] as String?) ?? name; + usrTypeMappings[usr] = ImportedType(libraryImport, name, dartName, name, + importedDartType: true); } } diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart index 3dbb936ef..c6e1c65aa 100644 --- a/pkgs/ffigen/lib/src/strings.dart +++ b/pkgs/ffigen/lib/src/strings.dart @@ -170,6 +170,7 @@ const formatVersion = 'format_version'; const symbolFileFormatVersion = '1.0.0'; const files = 'files'; const usedConfig = 'used-config'; +const dartName = 'dart-name'; const import = 'import'; const defaultSymbolFileImportPrefix = 'imp';