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

Better typedefs for NativeFunctions #621

Merged
merged 7 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming-wise, it feels like this one should be without _function and the other one should be renamed _pointer. Do you know if we have a lot of uses of these typedefs, if it would be worth the breaking change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but we could avoid a breaking change by putting this behind a flag. Eg, add a flag called use-function-typedefs, and if it's true we add _pointer to the existing definition and then define the function definition with the current name. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just merge as is.

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