From 2811206593bdff3f3322059e060869bbdc95f8cf Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Tue, 19 Sep 2023 14:30:48 -0700 Subject: [PATCH] Better typedefs for `NativeFunction`s (#621) * Fix #614 * Update test expectations * Hacky fix * Refactor * Fix analysis * Fix analysis * Fix analysis --- CHANGELOG.md | 2 + .../libclang-example/generated_bindings.dart | 29 +++++++------- lib/src/code_generator/func_type.dart | 25 ++++++++---- lib/src/code_generator/typealias.dart | 40 ++++++++++++++++++- .../_expected_dart_handle_bindings.dart | 4 +- ...expected_native_func_typedef_bindings.dart | 11 +++-- ..._expected_struct_fptr_fields_bindings.dart | 3 +- .../_expected_typedef_bindings.dart | 3 +- .../_expected_libclang_bindings.dart | 29 +++++++------- .../_expected_sqlite_bindings.dart | 19 ++++----- 10 files changed, 111 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efc5af31..d9d4b880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Fix return_of_invalid_type analysis error for ObjCBlocks. - Fix crash in ObjC methods and blocks that return structs by value. - Fix ObjC methods returning instancetype having the wrong type in sublasses. +- When generating typedefs for `Pointer>`, also + generate a typedef for the `Function`. - Bump min SDK version to 3.2.0-114.0.dev. # 9.0.1 diff --git a/example/libclang-example/generated_bindings.dart b/example/libclang-example/generated_bindings.dart index fe7f3c4f..a0ae7a2e 100644 --- a/example/libclang-example/generated_bindings.dart +++ b/example/libclang-example/generated_bindings.dart @@ -9747,10 +9747,10 @@ typedef DartClang_getIBOutletCollectionType = CXType Function(CXCursor arg0); /// /// The visitor should return one of the \c CXChildVisitResult values /// to direct clang_visitCursorChildren(). -typedef CXCursorVisitor = ffi.Pointer< - ffi.NativeFunction< - ffi.Int32 Function( - CXCursor cursor, CXCursor parent, CXClientData client_data)>>; +typedef CXCursorVisitor + = ffi.Pointer>; +typedef CXCursorVisitor_function = ffi.Int32 Function( + CXCursor cursor, CXCursor parent, CXClientData client_data); /// Describes how the traversal of the children of a particular /// cursor should proceed after visiting a particular child cursor. @@ -10425,13 +10425,13 @@ typedef DartClang_toggleCrashRecovery = void Function(int isEnabled); /// the second and third arguments provide the inclusion stack. The /// array is sorted in order of immediate inclusion. For example, /// the first element refers to the location that included 'included_file'. -typedef CXInclusionVisitor = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - CXFile included_file, - ffi.Pointer inclusion_stack, - ffi.UnsignedInt include_len, - CXClientData client_data)>>; +typedef CXInclusionVisitor + = ffi.Pointer>; +typedef CXInclusionVisitor_function = ffi.Void Function( + CXFile included_file, + ffi.Pointer inclusion_stack, + ffi.UnsignedInt include_len, + CXClientData client_data); typedef NativeClang_getInclusions = ffi.Void Function( CXTranslationUnit tu, CXInclusionVisitor visitor, CXClientData client_data); typedef DartClang_getInclusions = void Function( @@ -11103,9 +11103,10 @@ typedef DartClang_indexLoc_getCXSourceLocation = CXSourceLocation Function( /// /// The visitor should return one of the \c CXVisitorResult values /// to direct \c clang_Type_visitFields. -typedef CXFieldVisitor = ffi.Pointer< - ffi - .NativeFunction>; +typedef CXFieldVisitor + = ffi.Pointer>; +typedef CXFieldVisitor_function = ffi.Int32 Function( + CXCursor C, CXClientData client_data); typedef NativeClang_Type_visitFields = ffi.UnsignedInt Function( CXType T, CXFieldVisitor visitor, CXClientData client_data); typedef DartClang_Type_visitFields = int Function( diff --git a/lib/src/code_generator/func_type.dart b/lib/src/code_generator/func_type.dart index d4c74f68..675c85b1 100644 --- a/lib/src/code_generator/func_type.dart +++ b/lib/src/code_generator/func_type.dart @@ -113,26 +113,35 @@ class FunctionType extends Type { /// Represents a NativeFunction. class NativeFunc extends Type { - final FunctionType type; + // Either a FunctionType or a Typealias of a FunctionType. + final Type _type; - NativeFunc(this.type); + NativeFunc(this._type) { + assert(_type is FunctionType || _type is Typealias); + } + + FunctionType get type { + if (_type is Typealias) { + return _type.typealiasType as FunctionType; + } + return _type as FunctionType; + } @override void addDependencies(Set dependencies) { - type.addDependencies(dependencies); + _type.addDependencies(dependencies); } @override String getCType(Writer w) => - '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>'; + '${w.ffiLibraryPrefix}.NativeFunction<${_type.getCType(w)}>'; @override - String getDartType(Writer w) => - '${w.ffiLibraryPrefix}.NativeFunction<${type.getCType(w)}>'; + String getDartType(Writer w) => getCType(w); @override - String toString() => 'NativeFunction<${type.toString()}>'; + String toString() => 'NativeFunction<${_type.toString()}>'; @override - String cacheKey() => 'NatFn(${type.cacheKey()})'; + String cacheKey() => 'NatFn(${_type.cacheKey()})'; } diff --git a/lib/src/code_generator/typealias.dart b/lib/src/code_generator/typealias.dart index 6a47f618..c7d95ca5 100644 --- a/lib/src/code_generator/typealias.dart +++ b/lib/src/code_generator/typealias.dart @@ -18,18 +18,47 @@ class Typealias extends BindingType { final Type type; final bool _useDartType; - Typealias({ + factory Typealias({ String? usr, String? originalName, String? dartDoc, required String name, - required this.type, + 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, + }) { + final funcType = _getFunctionTypeFromPointer(type); + if (funcType != null) { + type = PointerType(NativeFunc(Typealias._( + name: '${name}_function', + type: funcType, + useDartType: useDartType, + isInternal: isInternal, + ))); + } + return Typealias._( + usr: usr, + originalName: originalName, + dartDoc: dartDoc, + name: name, + type: type, + useDartType: useDartType, + isInternal: isInternal, + ); + } + + Typealias._({ + String? usr, + String? originalName, + String? dartDoc, + required String name, + required this.type, + bool useDartType = false, + bool isInternal = false, }) : _useDartType = useDartType, super( usr: usr, @@ -47,6 +76,13 @@ class Typealias extends BindingType { type.addDependencies(dependencies); } + static FunctionType? _getFunctionTypeFromPointer(Type type) { + if (type is! PointerType) return null; + final pointee = type.child; + if (pointee is! NativeFunc) return null; + return pointee.type; + } + @override BindingString toBindingString(Writer w) { final sb = StringBuffer(); diff --git a/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart index 8455b7ad..a6f44566 100644 --- a/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart +++ b/test/header_parser_tests/expected_bindings/_expected_dart_handle_bindings.dart @@ -68,8 +68,8 @@ class NativeLibrary { late final _func4 = _func4Ptr.asFunction(); } -typedef Typedef1 - = ffi.Pointer>; +typedef Typedef1 = ffi.Pointer>; +typedef Typedef1_function = ffi.Void Function(ffi.Handle); final class Struct1 extends ffi.Opaque {} diff --git a/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart index f77a57bf..ca4e5e61 100644 --- a/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart +++ b/test/header_parser_tests/expected_bindings/_expected_native_func_typedef_bindings.dart @@ -73,11 +73,16 @@ final class Struct extends ffi.Struct { } typedef WithTypedefReturnType - = ffi.Pointer>; -typedef InsideReturnType = ffi.Pointer>; + = ffi.Pointer>; +typedef WithTypedefReturnType_function = InsideReturnType Function(); +typedef InsideReturnType + = ffi.Pointer>; +typedef InsideReturnType_function = ffi.Void Function(); final class Struct2 extends ffi.Struct { external VoidFuncPointer constFuncPointer; } -typedef VoidFuncPointer = ffi.Pointer>; +typedef VoidFuncPointer + = ffi.Pointer>; +typedef VoidFuncPointer_function = ffi.Void Function(); diff --git a/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart index 2a769db2..8dc72979 100644 --- a/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart +++ b/test/header_parser_tests/expected_bindings/_expected_struct_fptr_fields_bindings.dart @@ -69,4 +69,5 @@ final class S extends ffi.Struct { } typedef ArithmeticOperation - = ffi.Pointer>; + = ffi.Pointer>; +typedef ArithmeticOperation_function = ffi.Int Function(ffi.Int a, ffi.Int b); diff --git a/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart b/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart index 885f6410..b0b171a0 100644 --- a/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart +++ b/test/header_parser_tests/expected_bindings/_expected_typedef_bindings.dart @@ -92,7 +92,8 @@ final class Struct1 extends ffi.Struct { } typedef NamedFunctionProto - = ffi.Pointer>; + = ffi.Pointer>; +typedef NamedFunctionProto_function = ffi.Void Function(); final class AnonymousStructInTypedef extends ffi.Opaque {} diff --git a/test/large_integration_tests/_expected_libclang_bindings.dart b/test/large_integration_tests/_expected_libclang_bindings.dart index 2bc76881..d30109c4 100644 --- a/test/large_integration_tests/_expected_libclang_bindings.dart +++ b/test/large_integration_tests/_expected_libclang_bindings.dart @@ -7430,10 +7430,10 @@ abstract class CXChildVisitResult { } /// Visitor invoked for each cursor found by a traversal. -typedef CXCursorVisitor = ffi.Pointer< - ffi.NativeFunction< - ffi.Int32 Function( - CXCursor cursor, CXCursor parent, CXClientData client_data)>>; +typedef CXCursorVisitor + = ffi.Pointer>; +typedef CXCursorVisitor_function = ffi.Int32 Function( + CXCursor cursor, CXCursor parent, CXClientData client_data); /// Opaque pointer representing client data that will be passed through to /// various callbacks and visitors. @@ -7761,13 +7761,13 @@ abstract class CXCompletionContext { /// Visitor invoked for each file in a translation unit (used with /// clang_getInclusions()). -typedef CXInclusionVisitor = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - CXFile included_file, - ffi.Pointer inclusion_stack, - ffi.UnsignedInt include_len, - CXClientData client_data)>>; +typedef CXInclusionVisitor + = ffi.Pointer>; +typedef CXInclusionVisitor_function = ffi.Void Function( + CXFile included_file, + ffi.Pointer inclusion_stack, + ffi.UnsignedInt include_len, + CXClientData client_data); abstract class CXEvalResultKind { static const int CXEval_Int = 1; @@ -8224,9 +8224,10 @@ abstract class CXIndexOptFlags { } /// Visitor invoked for each field found by a traversal. -typedef CXFieldVisitor = ffi.Pointer< - ffi - .NativeFunction>; +typedef CXFieldVisitor + = ffi.Pointer>; +typedef CXFieldVisitor_function = ffi.Int32 Function( + CXCursor C, CXClientData client_data); const int CINDEX_VERSION_MAJOR = 0; diff --git a/test/large_integration_tests/_expected_sqlite_bindings.dart b/test/large_integration_tests/_expected_sqlite_bindings.dart index 984fad3c..492da429 100644 --- a/test/large_integration_tests/_expected_sqlite_bindings.dart +++ b/test/large_integration_tests/_expected_sqlite_bindings.dart @@ -10855,7 +10855,8 @@ final class sqlite3_vfs extends ffi.Struct { typedef sqlite3_int64 = sqlite_int64; typedef sqlite_int64 = ffi.LongLong; typedef sqlite3_syscall_ptr - = ffi.Pointer>; + = ffi.Pointer>; +typedef sqlite3_syscall_ptr_function = ffi.Void Function(); final class sqlite3_mem_methods extends ffi.Struct { /// Memory allocation function @@ -12008,14 +12009,14 @@ final class fts5_api extends ffi.Struct { xDestroy)>> xCreateFunction; } -typedef fts5_extension_function = ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer pApi, - ffi.Pointer pFts, - ffi.Pointer pCtx, - ffi.Int nVal, - ffi.Pointer> apVal)>>; +typedef fts5_extension_function + = ffi.Pointer>; +typedef fts5_extension_function_function = ffi.Void Function( + ffi.Pointer pApi, + ffi.Pointer pFts, + ffi.Pointer pCtx, + ffi.Int nVal, + ffi.Pointer> apVal); const String SQLITE_VERSION = '3.32.3';