Better typedefs for `NativeFunction`s (#621)
* Fix #614
* Update test expectations
* Hacky fix
* Refactor
* Fix analysis
* Fix analysis
* Fix analysis
diff --git a/CHANGELOG.md b/CHANGELOG.md
index efc5af3..d9d4b88 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<NativeFunction<Function>>`, 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 fe7f3c4..a0ae7a2 100644
--- a/example/libclang-example/generated_bindings.dart
+++ b/example/libclang-example/generated_bindings.dart
@@ -9747,10 +9747,10 @@
///
/// 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<ffi.NativeFunction<CXCursorVisitor_function>>;
+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 @@
/// 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<CXSourceLocation> inclusion_stack,
- ffi.UnsignedInt include_len,
- CXClientData client_data)>>;
+typedef CXInclusionVisitor
+ = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitor_function>>;
+typedef CXInclusionVisitor_function = ffi.Void Function(
+ CXFile included_file,
+ ffi.Pointer<CXSourceLocation> 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 @@
///
/// The visitor should return one of the \c CXVisitorResult values
/// to direct \c clang_Type_visitFields.
-typedef CXFieldVisitor = ffi.Pointer<
- ffi
- .NativeFunction<ffi.Int32 Function(CXCursor C, CXClientData client_data)>>;
+typedef CXFieldVisitor
+ = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
+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 d4c74f6..675c85b 100644
--- a/lib/src/code_generator/func_type.dart
+++ b/lib/src/code_generator/func_type.dart
@@ -113,26 +113,35 @@
/// Represents a NativeFunction<Function>.
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<Binding> 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 6a47f61..c7d95ca 100644
--- a/lib/src/code_generator/typealias.dart
+++ b/lib/src/code_generator/typealias.dart
@@ -18,16 +18,45 @@
final Type type;
final bool _useDartType;
- Typealias({
+ factory Typealias({
+ 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,
+ }) {
+ 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,
-
- /// 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,
}) : _useDartType = useDartType,
@@ -47,6 +76,13 @@
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 8455b7a..a6f4456 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 @@
late final _func4 = _func4Ptr.asFunction<void Function(Typedef1)>();
}
-typedef Typedef1
- = ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Handle)>>;
+typedef Typedef1 = ffi.Pointer<ffi.NativeFunction<Typedef1_function>>;
+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 f77a57b..ca4e5e6 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 @@
}
typedef WithTypedefReturnType
- = ffi.Pointer<ffi.NativeFunction<InsideReturnType Function()>>;
-typedef InsideReturnType = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+ = ffi.Pointer<ffi.NativeFunction<WithTypedefReturnType_function>>;
+typedef WithTypedefReturnType_function = InsideReturnType Function();
+typedef InsideReturnType
+ = ffi.Pointer<ffi.NativeFunction<InsideReturnType_function>>;
+typedef InsideReturnType_function = ffi.Void Function();
final class Struct2 extends ffi.Struct {
external VoidFuncPointer constFuncPointer;
}
-typedef VoidFuncPointer = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+typedef VoidFuncPointer
+ = ffi.Pointer<ffi.NativeFunction<VoidFuncPointer_function>>;
+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 2a769db..8dc7297 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 @@
}
typedef ArithmeticOperation
- = ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Int a, ffi.Int b)>>;
+ = ffi.Pointer<ffi.NativeFunction<ArithmeticOperation_function>>;
+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 885f641..b0b171a 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 @@
}
typedef NamedFunctionProto
- = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+ = ffi.Pointer<ffi.NativeFunction<NamedFunctionProto_function>>;
+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 2bc7688..d30109c 100644
--- a/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -7430,10 +7430,10 @@
}
/// 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<ffi.NativeFunction<CXCursorVisitor_function>>;
+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 @@
/// 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<CXSourceLocation> inclusion_stack,
- ffi.UnsignedInt include_len,
- CXClientData client_data)>>;
+typedef CXInclusionVisitor
+ = ffi.Pointer<ffi.NativeFunction<CXInclusionVisitor_function>>;
+typedef CXInclusionVisitor_function = ffi.Void Function(
+ CXFile included_file,
+ ffi.Pointer<CXSourceLocation> inclusion_stack,
+ ffi.UnsignedInt include_len,
+ CXClientData client_data);
abstract class CXEvalResultKind {
static const int CXEval_Int = 1;
@@ -8224,9 +8224,10 @@
}
/// Visitor invoked for each field found by a traversal.
-typedef CXFieldVisitor = ffi.Pointer<
- ffi
- .NativeFunction<ffi.Int32 Function(CXCursor C, CXClientData client_data)>>;
+typedef CXFieldVisitor
+ = ffi.Pointer<ffi.NativeFunction<CXFieldVisitor_function>>;
+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 984fad3..492da42 100644
--- a/test/large_integration_tests/_expected_sqlite_bindings.dart
+++ b/test/large_integration_tests/_expected_sqlite_bindings.dart
@@ -10855,7 +10855,8 @@
typedef sqlite3_int64 = sqlite_int64;
typedef sqlite_int64 = ffi.LongLong;
typedef sqlite3_syscall_ptr
- = ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>;
+ = ffi.Pointer<ffi.NativeFunction<sqlite3_syscall_ptr_function>>;
+typedef sqlite3_syscall_ptr_function = ffi.Void Function();
final class sqlite3_mem_methods extends ffi.Struct {
/// Memory allocation function
@@ -12008,14 +12009,14 @@
xDestroy)>> xCreateFunction;
}
-typedef fts5_extension_function = ffi.Pointer<
- ffi.NativeFunction<
- ffi.Void Function(
- ffi.Pointer<Fts5ExtensionApi> pApi,
- ffi.Pointer<Fts5Context> pFts,
- ffi.Pointer<sqlite3_context> pCtx,
- ffi.Int nVal,
- ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal)>>;
+typedef fts5_extension_function
+ = ffi.Pointer<ffi.NativeFunction<fts5_extension_function_function>>;
+typedef fts5_extension_function_function = ffi.Void Function(
+ ffi.Pointer<Fts5ExtensionApi> pApi,
+ ffi.Pointer<Fts5Context> pFts,
+ ffi.Pointer<sqlite3_context> pCtx,
+ ffi.Int nVal,
+ ffi.Pointer<ffi.Pointer<sqlite3_value>> apVal);
const String SQLITE_VERSION = '3.32.3';