Skip to content

More quick fixes for bugs related to #1250 #1339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 17, 2024
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
8 changes: 6 additions & 2 deletions pkgs/ffigen/lib/src/code_generator/func.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Func extends LookUpBinding {
final bool exposeFunctionTypedefs;
final bool isLeaf;
final bool objCReturnsRetained;
final bool useNameForLookup;
final FfiNativeConfig ffiNativeConfig;
late final String funcPointerName;

Expand All @@ -64,6 +65,7 @@ class Func extends LookUpBinding {
this.exposeFunctionTypedefs = false,
this.isLeaf = false,
this.objCReturnsRetained = false,
this.useNameForLookup = false,
super.isInternal,
this.ffiNativeConfig = const FfiNativeConfig(enabled: false),
}) : functionType = FunctionType(
Expand Down Expand Up @@ -92,6 +94,8 @@ class Func extends LookUpBinding {
}
}

String get _lookupName => useNameForLookup ? name : originalName;

@override
BindingString toBindingString(Writer w) {
final s = StringBuffer();
Expand Down Expand Up @@ -152,7 +156,7 @@ ${makeNativeAnnotation(
w,
nativeType: cType,
dartName: nativeFuncName,
nativeSymbolName: originalName,
nativeSymbolName: _lookupName,
isLeaf: isLeaf,
)}
external $ffiReturnType $nativeFuncName($ffiArgDeclString);
Expand Down Expand Up @@ -198,7 +202,7 @@ $dartReturnType $enclosingFuncName($dartArgDeclString) {
// Write function pointer.
s.write('''
late final $funcPointerName = ${w.lookupFuncIdentifier}<
${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$originalName');
${w.ffiLibraryPrefix}.NativeFunction<$cType>>('$_lookupName');
late final $funcVarName = $funcPointerName.asFunction<$dartType>($isLeafString);

''');
Expand Down
3 changes: 2 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ pointer.ref.invoke.cast<$natTrampFnType>().asFunction<$trampFuncFfiDartType>()(
argsReceived.add(t.getNativeType(varName: argName));
retains.add(t.generateRetain(argName) ?? argName);
}
final fnName = _wrapListenerBlock!.originalName;
final fnName = _wrapListenerBlock!.name;
final blockTypedef = w.objCLevelUniqueNamer.makeUnique('ListenerBlock');

final s = StringBuffer();
Expand Down Expand Up @@ -275,6 +275,7 @@ $blockTypedef $fnName($blockTypedef block) {
objCReturnsRetained: true,
isLeaf: true,
isInternal: true,
useNameForLookup: true,
ffiNativeConfig: const FfiNativeConfig(enabled: true),
)..addDependencies(dependencies);
}
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_nullable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ObjCNullable extends Type {

@override
String getNativeType({String varName = ''}) =>
'${child.getNativeType()} _Nullable $varName';
child.getNativeType(varName: varName);

@override
bool get sameFfiDartAndCType => child.sameFfiDartAndCType;
Expand Down
15 changes: 15 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,18 @@ String findDart() {
throw Exception(
"Couldn't find Dart executable near ${Platform.resolvedExecutable}");
}

/// Attempts to parse an absolute path to an ObjC framework header. Returns an
/// importable path if successful, otherwise returns null.
String? parseObjCFrameworkHeader(String path) {
final match = _frameworkHeaderRegex.firstMatch(path);

if (match == null) {
return null;
}

return '${match[1]}/${match[2]}';
}

final _frameworkHeaderRegex = RegExp(
r'.*/Library(?:/.*/|/)Frameworks/([^/]+)\.framework(?:/.*/|/)Headers/(.*)');
15 changes: 13 additions & 2 deletions pkgs/ffigen/lib/src/code_generator/writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,18 @@ class Writer {
};
}

static String _objcImport(String entryPoint, String outDir) {
final frameworkHeader = parseObjCFrameworkHeader(entryPoint);

if (frameworkHeader == null) {
// If it's not a framework header, use a relative import.
return '#import "${p.relative(entryPoint, from: outDir)}"\n';
}

// If it's a framework header, use a <> style import.
return '#import <$frameworkHeader>';
}

/// Writes the Objective C code needed for the bindings, if any. Returns null
/// if there are no bindings that need generated ObjC code. This function does
/// not generate the output file, but the [outFilename] does affect the
Expand All @@ -410,8 +422,7 @@ class Writer {
''');

for (final entryPoint in nativeEntryPoints) {
final path = p.relative(entryPoint, from: outDir);
s.write('#include "$path"\n');
s.write(_objcImport(entryPoint, outDir));
}

var empty = true;
Expand Down
1 change: 1 addition & 0 deletions pkgs/ffigen/test/native_objc_test/block_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedefs:
- NullableObjectBlock
- BlockBlock
- ListenerBlock
- ObjectListenerBlock
- NullableListenerBlock
- StructListenerBlock
- NSStringListenerBlock
Expand Down
11 changes: 11 additions & 0 deletions pkgs/ffigen/test/native_objc_test/block_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ void main() {
expect(isCalled, isTrue);
});

test('Object listener block', () async {
final hasRun = Completer<void>();
final block = DartObjectListenerBlock.listener((DummyObject x) {
expect(x, isNotNull);
hasRun.complete();
});

BlockTester.callObjectListener_(block);
await hasRun.future;
});

test('Nullable listener block', () async {
final hasRun = Completer<void>();
final block = DartNullableListenerBlock.listener((DummyObject? x) {
Expand Down
2 changes: 2 additions & 0 deletions pkgs/ffigen/test/native_objc_test/block_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef DummyObject* (^ObjectBlock)(DummyObject*);
typedef DummyObject* _Nullable (^NullableObjectBlock)(DummyObject* _Nullable);
typedef IntBlock (^BlockBlock)(IntBlock);
typedef void (^ListenerBlock)(IntBlock);
typedef void (^ObjectListenerBlock)(DummyObject*);
typedef void (^NullableListenerBlock)(DummyObject* _Nullable);
typedef void (^StructListenerBlock)(struct Vec2, Vec4, NSObject*);
typedef void (^NSStringListenerBlock)(NSString*);
Expand All @@ -61,6 +62,7 @@ typedef void (^NoTrampolineListenerBlock)(int32_t, Vec4, const char*);
+ (DummyObject*)callObjectBlock:(ObjectBlock)block NS_RETURNS_RETAINED;
+ (nullable DummyObject*)callNullableObjectBlock:(NullableObjectBlock)block;
+ (void)callListener:(ListenerBlock)block;
+ (void)callObjectListener:(ObjectListenerBlock)block;
+ (void)callNullableListener:(NullableListenerBlock)block;
+ (void)callStructListener:(StructListenerBlock)block;
+ (void)callNSStringListener:(NSStringListenerBlock)block x:(int32_t)x;
Expand Down
4 changes: 4 additions & 0 deletions pkgs/ffigen/test/native_objc_test/block_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ + (NSThread*)callWithBlockOnNewThread:(ListenerBlock)block {
object:block];
}

+ (void)callObjectListener:(ObjectListenerBlock)block {
block([DummyObject alloc]);
}

+ (void)callNullableListener:(NullableListenerBlock)block {
block(nil);
}
Expand Down
50 changes: 50 additions & 0 deletions pkgs/ffigen/test/unit_tests/objc_framework_header_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:ffigen/src/code_generator/utils.dart';
import 'package:test/test.dart';

void main() {
group('ObjC framework header test', () {
test('parsing', () {
expect(parseObjCFrameworkHeader(''), null);
expect(parseObjCFrameworkHeader('/Foo/Bar.h'), null);
expect(parseObjCFrameworkHeader('/Library/a/b/c/Headers/Bar.h'), null);
expect(
parseObjCFrameworkHeader(
'/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/'
'Library/Frameworks/AppKit.framework/Versions/C/Headers/NSMatrix.h'),
'AppKit/NSMatrix.h');
expect(
parseObjCFrameworkHeader(
'/System/Library/Frameworks/Photos.framework/Versions/Current/'
'Headers/SomeHeader.h'),
'Photos/SomeHeader.h');
expect(
parseObjCFrameworkHeader(
'/Library/Frameworks/macFUSE.framework/Headers/macFUSE.h'),
'macFUSE/macFUSE.h');
expect(
parseObjCFrameworkHeader(
'/Library/Frameworks/Foo.framework/Headers/Bar.h'),
'Foo/Bar.h');
expect(
parseObjCFrameworkHeader(
'a/b/c/Library/Frameworks/Foo.framework/Headers/Bar.h'),
'Foo/Bar.h');
expect(
parseObjCFrameworkHeader(
'/Library/a/b/c/Frameworks/Foo.framework/Headers/Bar.h'),
'Foo/Bar.h');
expect(
parseObjCFrameworkHeader(
'/Library/Frameworks/Foo.framework/a/b/c/Headers/Bar.h'),
'Foo/Bar.h');
expect(
parseObjCFrameworkHeader(
'/Library/Frameworks/Foo.framework/Headers/a/b/c/Bar.h'),
'Foo/a/b/c/Bar.h');
});
});
}
Loading