Skip to content
This repository has been archived by the owner on Jan 28, 2024. It is now read-only.

Commit

Permalink
Better typedefs for NativeFunctions (#621)
Browse files Browse the repository at this point in the history
* Fix #614

* Update test expectations

* Hacky fix

* Refactor

* Fix analysis

* Fix analysis

* Fix analysis
  • Loading branch information
liamappelbe authored Sep 19, 2023
1 parent cb4754b commit 2811206
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 54 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 15 additions & 14 deletions example/libclang-example/generated_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<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.
Expand Down Expand Up @@ -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<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(
Expand Down Expand Up @@ -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<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(
Expand Down
25 changes: 17 additions & 8 deletions lib/src/code_generator/func_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,35 @@ class FunctionType extends Type {

/// 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()})';
}
40 changes: 38 additions & 2 deletions lib/src/code_generator/typealias.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ class NativeLibrary {
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 {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,16 @@ final class Struct extends ffi.Struct {
}

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();
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ final class S extends ffi.Struct {
}

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);
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ final class Struct1 extends ffi.Struct {
}

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 {}

Expand Down
29 changes: 15 additions & 14 deletions test/large_integration_tests/_expected_libclang_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<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.
Expand Down Expand Up @@ -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<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;
Expand Down Expand Up @@ -8224,9 +8224,10 @@ abstract class CXIndexOptFlags {
}

/// 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;

Expand Down
19 changes: 10 additions & 9 deletions test/large_integration_tests/_expected_sqlite_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.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
Expand Down Expand Up @@ -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<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';

Expand Down

0 comments on commit 2811206

Please sign in to comment.