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

Commit

Permalink
Typedefs for Dart types (#625)
Browse files Browse the repository at this point in the history
* WIP fix #386

* Fix tests

* fmt

* Update test expectations

* Update test expectations

* Fix analysis

* fmt

* Fix more tests

* WIP refactor

* Refactor `sameFfiDartAndCType` to not require a `Writer`

* Fix tests

* Fix analysis

* Update test expectations

* Format

* Fix typedef_test.dart

* Fix nits

* Fix tests

* Fmt

* Regenerate more test files

* Regen more tests

* Remove accidental commit
  • Loading branch information
liamappelbe committed Oct 11, 2023
1 parent 7689e24 commit ffaef08
Show file tree
Hide file tree
Showing 21 changed files with 388 additions and 202 deletions.
1 change: 1 addition & 0 deletions example/c_json/cjson_generated_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,7 @@ final class cJSON_Hooks extends ffi.Struct {
}

typedef cJSON_bool = ffi.Int;
typedef DartcJSON_bool = int;

const int CJSON_VERSION_MAJOR = 1;

Expand Down
21 changes: 15 additions & 6 deletions example/libclang-example/generated_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9748,8 +9748,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<CXCursorVisitor_function>>;
typedef CXCursorVisitor_function = ffi.Int32 Function(
= ffi.Pointer<ffi.NativeFunction<CXCursorVisitorFunction>>;
typedef CXCursorVisitorFunction = ffi.Int32 Function(
CXCursor cursor, CXCursor parent, CXClientData client_data);
typedef DartCXCursorVisitorFunction = int Function(
CXCursor cursor, CXCursor parent, CXClientData client_data);

/// Describes how the traversal of the children of a particular
Expand Down Expand Up @@ -10426,12 +10428,17 @@ typedef DartClang_toggleCrashRecovery = void Function(int isEnabled);
/// 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<CXInclusionVisitor_function>>;
typedef CXInclusionVisitor_function = ffi.Void Function(
= ffi.Pointer<ffi.NativeFunction<CXInclusionVisitorFunction>>;
typedef CXInclusionVisitorFunction = ffi.Void Function(
CXFile included_file,
ffi.Pointer<CXSourceLocation> inclusion_stack,
ffi.UnsignedInt include_len,
CXClientData client_data);
typedef DartCXInclusionVisitorFunction = void Function(
CXFile included_file,
ffi.Pointer<CXSourceLocation> inclusion_stack,
int 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 @@ -11104,8 +11111,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<CXFieldVisitor_function>>;
typedef CXFieldVisitor_function = ffi.Int32 Function(
= ffi.Pointer<ffi.NativeFunction<CXFieldVisitorFunction>>;
typedef CXFieldVisitorFunction = ffi.Int32 Function(
CXCursor C, CXClientData client_data);
typedef DartCXFieldVisitorFunction = int Function(
CXCursor C, CXClientData client_data);
typedef NativeClang_Type_visitFields = ffi.UnsignedInt Function(
CXType T, CXFieldVisitor visitor, CXClientData client_data);
Expand Down
27 changes: 9 additions & 18 deletions lib/src/code_generator/func.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ class Func extends LookUpBinding {
late final String funcPointerName;

/// Contains typealias for function type if [exposeFunctionTypedefs] is true.
Typealias? _exposedCFunctionTypealias;
Typealias? _exposedDartFunctionTypealias;
Typealias? _exposedFunctionTypealias;

/// [originalName] is looked up in dynamic library, if not
/// provided, takes the value of [name].
Expand Down Expand Up @@ -85,15 +84,10 @@ class Func extends LookUpBinding {
// Get function name with first letter in upper case.
final upperCaseName = name[0].toUpperCase() + name.substring(1);
if (exposeFunctionTypedefs) {
_exposedCFunctionTypealias = Typealias(
name: 'Native$upperCaseName',
_exposedFunctionTypealias = Typealias(
name: upperCaseName,
type: functionType,
isInternal: true,
);
_exposedDartFunctionTypealias = Typealias(
name: 'Dart$upperCaseName',
type: functionType,
useDartType: true,
genFfiDartType: true,
isInternal: true,
);
}
Expand All @@ -115,12 +109,10 @@ class Func extends LookUpBinding {
p.name = paramNamer.makeUnique(p.name);
}

final cType = exposeFunctionTypedefs
? _exposedCFunctionTypealias!.name
: functionType.getCType(w, writeArgumentNames: false);
final dartType = exposeFunctionTypedefs
? _exposedDartFunctionTypealias!.name
: functionType.getFfiDartType(w, writeArgumentNames: false);
final cType = _exposedFunctionTypealias?.getCType(w) ??
functionType.getCType(w, writeArgumentNames: false);
final dartType = _exposedFunctionTypealias?.getFfiDartType(w) ??
functionType.getFfiDartType(w, writeArgumentNames: false);

if (ffiNativeConfig.enabled) {
final assetString = ffiNativeConfig.asset != null
Expand Down Expand Up @@ -180,8 +172,7 @@ class Func extends LookUpBinding {
dependencies.add(this);
functionType.addDependencies(dependencies);
if (exposeFunctionTypedefs) {
_exposedCFunctionTypealias!.addDependencies(dependencies);
_exposedDartFunctionTypealias!.addDependencies(dependencies);
_exposedFunctionTypealias!.addDependencies(dependencies);
}
}
}
Expand Down
52 changes: 29 additions & 23 deletions lib/src/code_generator/objc_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,11 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} {
}
}

static bool _isInstanceType(Type type) =>
type is ObjCInstanceType ||
(type is ObjCNullable && type.child is ObjCInstanceType);
static bool _isInstanceType(Type type) {
if (type is ObjCInstanceType) return true;
final baseType = type.typealiasType;
return baseType is ObjCNullable && baseType.child is ObjCInstanceType;
}

void addMethod(ObjCMethod method) {
final oldMethod = methods[method.originalName];
Expand Down Expand Up @@ -400,27 +402,29 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} {
// passed to native as Pointer<ObjCObject>, but the user sees the Dart wrapper
// class. These methods need to be kept in sync.
bool _needsConverting(Type type) =>
type is ObjCInterface ||
type is ObjCBlock ||
type is ObjCObjectPointer ||
type is ObjCInstanceType ||
type is ObjCNullable;
type.typealiasType is ObjCInterface ||
type.typealiasType is ObjCBlock ||
type.typealiasType is ObjCObjectPointer ||
type.typealiasType is ObjCNullable;

String _getConvertedType(Type type, Writer w, String enclosingClass) {
if (type is ObjCInstanceType) return enclosingClass;
if (type is ObjCNullable && type.child is ObjCInstanceType) {
final baseType = type.typealiasType;
if (baseType is ObjCNullable && baseType.child is ObjCInstanceType) {
return '$enclosingClass?';
}
return type.getDartType(w);
}

String _doArgConversion(ObjCMethodParam arg) {
if (arg.type is ObjCNullable) {
final baseType = arg.type.typealiasType;
if (baseType is ObjCNullable) {
return '${arg.name}?._id ?? ffi.nullptr';
} else if (arg.type is ObjCInterface ||
arg.type is ObjCObjectPointer ||
arg.type is ObjCInstanceType ||
arg.type is ObjCBlock) {
} else if (arg.type is ObjCInstanceType ||
baseType is ObjCInterface ||
baseType is ObjCObjectPointer ||
baseType is ObjCBlock) {
return '${arg.name}._id';
}
return arg.name;
Expand All @@ -429,22 +433,24 @@ class $name extends ${superType?.name ?? '_ObjCWrapper'} {
String _doReturnConversion(Type type, String value, String enclosingClass,
String library, bool isOwnedReturn) {
var prefix = '';
if (type is ObjCNullable) {
var baseType = type.typealiasType;
if (baseType is ObjCNullable) {
prefix = '$value.address == 0 ? null : ';
type = type.child;
type = baseType.child;
baseType = type.typealiasType;
}
final ownerFlags = 'retain: ${!isOwnedReturn}, release: true';
if (type is ObjCInterface) {
return '$prefix${type.name}._($value, $library, $ownerFlags)';
if (type is ObjCInstanceType) {
return '$prefix$enclosingClass._($value, $library, $ownerFlags)';
}
if (type is ObjCBlock) {
return '$prefix${type.name}._($value, $library)';
if (baseType is ObjCInterface) {
return '$prefix${baseType.name}._($value, $library, $ownerFlags)';
}
if (type is ObjCObjectPointer) {
return '${prefix}NSObject._($value, $library, $ownerFlags)';
if (baseType is ObjCBlock) {
return '$prefix${baseType.name}._($value, $library)';
}
if (type is ObjCInstanceType) {
return '$prefix$enclosingClass._($value, $library, $ownerFlags)';
if (baseType is ObjCObjectPointer) {
return '${prefix}NSObject._($value, $library, $ownerFlags)';
}
return prefix + value;
}
Expand Down
64 changes: 40 additions & 24 deletions lib/src/code_generator/typealias.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@ import 'writer.dart';
/// ```
class Typealias extends BindingType {
final Type type;
final bool _useDartType;
String? _ffiDartAliasName;
String? _dartAliasName;

/// Creates a Typealias.
///
/// If [genFfiDartType] is true, a binding is generated for the Ffi Dart type
/// in addition to the C type. See [Type.getFfiDartType].
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 genFfiDartType = false,
bool isInternal = false,
}) {
final funcType = _getFunctionTypeFromPointer(type);
if (funcType != null) {
type = PointerType(NativeFunc(Typealias._(
name: '${name}_function',
name: '${name}Function',
type: funcType,
useDartType: useDartType,
genFfiDartType: genFfiDartType,
isInternal: isInternal,
)));
}
Expand All @@ -49,7 +50,7 @@ class Typealias extends BindingType {
dartDoc: dartDoc,
name: name,
type: type,
useDartType: useDartType,
genFfiDartType: genFfiDartType,
isInternal: isInternal,
);
}
Expand All @@ -59,7 +60,7 @@ class Typealias extends BindingType {
dartDoc: dartDoc,
name: name,
type: type,
useDartType: useDartType,
genFfiDartType: genFfiDartType,
isInternal: isInternal,
);
}
Expand All @@ -70,12 +71,16 @@ class Typealias extends BindingType {
String? dartDoc,
required String name,
required this.type,
bool useDartType = false,
bool genFfiDartType = false,
bool isInternal = false,
}) : _useDartType = useDartType,
}) : _ffiDartAliasName = genFfiDartType ? 'Dart$name' : null,
_dartAliasName =
(!genFfiDartType && type is! Typealias && !type.sameDartAndCType)
? 'Dart$name'
: null,
super(
usr: usr,
name: name,
name: genFfiDartType ? 'Native$name' : name,
dartDoc: dartDoc,
originalName: originalName,
isInternal: isInternal,
Expand All @@ -98,12 +103,24 @@ class Typealias extends BindingType {

@override
BindingString toBindingString(Writer w) {
if (_ffiDartAliasName != null) {
_ffiDartAliasName = w.topLevelUniqueNamer.makeUnique(_ffiDartAliasName!);
}
if (_dartAliasName != null) {
_dartAliasName = w.topLevelUniqueNamer.makeUnique(_dartAliasName!);
}

final sb = StringBuffer();
if (dartDoc != null) {
sb.write(makeDartDoc(dartDoc!));
}
sb.write('typedef $name = ');
sb.write('${_useDartType ? type.getFfiDartType(w) : type.getCType(w)};\n');
sb.write('typedef $name = ${type.getCType(w)};\n');
if (_ffiDartAliasName != null) {
sb.write('typedef $_ffiDartAliasName = ${type.getFfiDartType(w)};\n');
}
if (_dartAliasName != null) {
sb.write('typedef $_dartAliasName = ${type.getDartType(w)};\n');
}
return BindingString(
type: BindingStringType.typeDef, string: sb.toString());
}
Expand All @@ -119,15 +136,18 @@ class Typealias extends BindingType {

@override
String getFfiDartType(Writer w) {
// Typealias cannot be used by name in Dart types unless both the C and Dart
// type of the underlying types are same.
if (type.sameFfiDartAndCType) {
if (_ffiDartAliasName != null) {
return _ffiDartAliasName!;
} else if (type.sameFfiDartAndCType) {
return name;
} else {
return type.getFfiDartType(w);
}
}

@override
String getDartType(Writer w) => _dartAliasName ?? type.getDartType(w);

@override
bool get sameFfiDartAndCType => type.sameFfiDartAndCType;

Expand All @@ -154,19 +174,15 @@ class ObjCInstanceType extends Typealias {
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 genFfiDartType = false,
bool isInternal = false,
}) : super._(
usr: usr,
originalName: originalName,
dartDoc: dartDoc,
name: name,
type: type,
useDartType: useDartType,
genFfiDartType: genFfiDartType,
isInternal: isInternal,
);
}
Loading

0 comments on commit ffaef08

Please sign in to comment.