Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
90cee4c
wip
liamappelbe Aug 10, 2026
5b86450
Lazy AST node wrappers
liamappelbe Aug 10, 2026
6e419f8
Don't visit children of excluded elements
liamappelbe Aug 10, 2026
b9226e4
wip
liamappelbe Aug 10, 2026
bddcbee
wip
liamappelbe Aug 11, 2026
a2996b4
wip
liamappelbe Aug 11, 2026
8ae873a
fixes
liamappelbe Aug 11, 2026
afd1c2f
bindings
liamappelbe Aug 11, 2026
96a42e0
Merge branch 'main' into ffigen_config_update_4
liamappelbe Aug 11, 2026
8e7fff0
clean up
liamappelbe Aug 11, 2026
a68e55f
clean up
liamappelbe Aug 11, 2026
21c4256
clean up
liamappelbe Aug 12, 2026
886635f
wip
liamappelbe Aug 12, 2026
ed80eb2
manual clean up
liamappelbe Aug 12, 2026
370fb10
regen
liamappelbe Aug 12, 2026
0a19f86
regen
liamappelbe Aug 12, 2026
79643a1
wip
liamappelbe Aug 12, 2026
80e8e1d
restore comment
liamappelbe Aug 12, 2026
7de78e5
revert unnecessary diff
liamappelbe Aug 12, 2026
592673f
revert spurious diff
liamappelbe Aug 12, 2026
243c02b
revert scope
liamappelbe Aug 12, 2026
5c368de
fix tests
liamappelbe Aug 12, 2026
f449200
clean
liamappelbe Aug 12, 2026
5541c18
docs
liamappelbe Aug 12, 2026
7bdf73b
revert junk
liamappelbe Aug 12, 2026
20d6fe8
clean up
liamappelbe Aug 12, 2026
0f0816c
regen
liamappelbe Aug 12, 2026
073fd94
fix
liamappelbe Aug 12, 2026
7076663
clean up
liamappelbe Aug 12, 2026
369f3d3
fix diffs
liamappelbe Aug 12, 2026
b8545e4
dediff
liamappelbe Aug 13, 2026
ed67128
fmt
liamappelbe Aug 13, 2026
bb3f278
fmt
liamappelbe Aug 13, 2026
f2e02e0
Merge branch 'main' into ffigen_config_update_4
liamappelbe Aug 13, 2026
a4bb0b7
wip
liamappelbe Aug 13, 2026
2af5f68
fix
liamappelbe Aug 13, 2026
02e8c3f
fix some tests
liamappelbe Aug 13, 2026
dab4b83
Fix tests
liamappelbe Aug 13, 2026
0bcbfdd
clean up
liamappelbe Aug 13, 2026
eb76e77
clean up
liamappelbe Aug 13, 2026
bb5543d
fix category bug
liamappelbe Aug 13, 2026
195d2b5
fix
liamappelbe Aug 13, 2026
090ddb6
cleaning
liamappelbe Aug 14, 2026
8fa206f
clean up example
liamappelbe Aug 14, 2026
f74dd48
clean up
liamappelbe Aug 14, 2026
01f2959
clean up
liamappelbe Aug 14, 2026
c68cee0
Merge branch 'main' into ffigen_config_update_4
liamappelbe Aug 14, 2026
f224827
migrate monorepo configs
liamappelbe Aug 14, 2026
05bb981
clean up
liamappelbe Aug 14, 2026
131d081
wip
liamappelbe Aug 17, 2026
c980074
Merge branch 'main' into ffigen_category_filter_bug
liamappelbe Aug 17, 2026
c77aa62
fmt
liamappelbe Aug 17, 2026
33a73b0
clean up
liamappelbe Aug 17, 2026
5452bbe
clean up
liamappelbe Aug 17, 2026
5118a6a
clean up
liamappelbe Aug 17, 2026
81b8b33
fmt
liamappelbe Aug 17, 2026
a661b28
add more missing categories
liamappelbe Aug 17, 2026
ebff678
bump ObjC to 9.6.0-wip
liamappelbe Aug 17, 2026
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 pkgs/ffigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
- Remove `useSupportedTypedefs`, treating it as always true
- Fix [a bug](https://github.com/dart-lang/native/issues/3504) in handling of
small structs in ObjC on mac/iOS x64.
- Fix [a bug](https://github.com/dart-lang/native/issues/3546) in the way that
ObjC category methods returning `instancetype` are filtered.
- Minor Objective-C code generator and function type signature fixes.
- Bump `package:code_assets` dependency to `^2.0.0`.

Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/example/objective_c/avf_audio_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);

/// WARNING: AVAudioFormat is a stub. To generate bindings for this class, include
/// AVAudioFormat in your config's objc-interfaces list.
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/example/swift/swift_api_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);

/// SwiftClass
extension type SwiftClass._(objc.ObjCObject object$)
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/lib/src/code_generator/imports.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const objcPkgImport = LibraryImport(
importPathWhenImportedByPackageObjC: '../objective_c.dart',
);
const objcMajorVersion = 9;
const objcMinorVersion = 5;
const objcMinorVersion = 6;
const selfImport = LibraryImport('self', '');
final builtInLibraries = {
for (final l in [
Expand Down
21 changes: 20 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_built_in_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,16 @@ const objCBuiltInProtocols = {
};

const objCBuiltInCategories = {
'NSArrayCreation',
'NSAttributedStringCreateFromMarkdown',
'NSAttributedStringFormatting',
'NSDataBase64Encoding',
'NSDataCompression',
'NSDataCreation',
'NSDateCreation',
'NSDictionaryCreation',
'NSExtendedArray',
'NSExtendedAttributedString',
'NSExtendedData',
'NSExtendedDate',
'NSExtendedDictionary',
Expand All @@ -137,8 +145,19 @@ const objCBuiltInCategories = {
'NSExtendedMutableSet',
'NSExtendedOrderedSet',
'NSExtendedSet',
'NSInputStreamExtensions',
'NSLocaleCreation',
'NSMutableArrayCreation',
'NSMutableDataCreation',
'NSMutableDictionaryCreation',
'NSMutableOrderedSetCreation',
'NSMutableSetCreation',
'NSNotificationCreation',
'NSNumberCreation',
'NSNumberIsFloat',
'NSNumberIsBool',
'NSNumberIsFloat',
'NSOrderedSetCreation',
'NSOutputStreamExtensions',
'NSSetCreation',
'NSStringExtensionMethods',
};
22 changes: 17 additions & 5 deletions pkgs/ffigen/lib/src/code_generator/objc_methods.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'local_variables.dart';
import 'native_type.dart';
import 'objc_block.dart';
import 'objc_built_in_functions.dart';
import 'objc_category.dart';
import 'objc_interface.dart';
import 'objc_nullable.dart';
import 'pointer.dart';
Expand Down Expand Up @@ -49,12 +50,12 @@ mixin ObjCMethods {
}
}

void copyMethod(ObjCMethod method) {
void copyMethod(ObjCMethod method, {ObjCCategory? originCategory}) {
// To maintain the pairing between getters and setters after cloning,
// instead of directly cloning the setter, we clone the setter when we clone
// the getter. This lets us, for example, share the symbol between them.
if (method.kind == ObjCMethodKind.propertySetter) return;
final cloned = method.clone();
final cloned = method.clone(originCategory: originCategory);
addMethod(cloned);
addMethod(cloned.setter);
}
Expand Down Expand Up @@ -213,6 +214,7 @@ class ObjCMethod extends AstNode with HasLocalScope {
ObjCMethods? parent;
ObjCMethod? setter;
bool isIncluded = true;
ObjCCategory? originCategory;

@override
void visitChildren(Visitor visitor, {bool omitMethodName = false}) {
Expand Down Expand Up @@ -332,7 +334,11 @@ class ObjCMethod extends AstNode with HasLocalScope {
bool get isInstanceMethod => !isClassMethod;
bool get unavailable => apiAvailability.availability == Availability.none;

ObjCMethod _cloneWithSymbol(Symbol newSymbol, {ObjCMethods? parent}) {
ObjCMethod _cloneWithSymbol(
Symbol newSymbol, {
ObjCMethods? parent,
ObjCCategory? originCategory,
}) {
final clonedMethod = ObjCMethod.withSymbol(
context: context,
originalName: originalName,
Expand All @@ -352,19 +358,25 @@ class ObjCMethod extends AstNode with HasLocalScope {
clonedMethod.parent = parent;
clonedMethod.protocolMethodName = protocolMethodName?.clone();
clonedMethod.isIncluded = isIncluded;
clonedMethod.originCategory = originCategory ?? this.originCategory;
return clonedMethod;
}

ObjCMethod clone({ObjCMethods? parent}) {
ObjCMethod clone({ObjCMethods? parent, ObjCCategory? originCategory}) {
assert(kind != ObjCMethodKind.propertySetter);
final clonedSymbol = symbol.clone();
final clonedMethod = _cloneWithSymbol(clonedSymbol, parent: parent);
final clonedMethod = _cloneWithSymbol(
clonedSymbol,
parent: parent,
originCategory: originCategory,
);
if (setter != null) {
assert(setter!.kind == ObjCMethodKind.propertySetter);
assert(setter!.symbol == symbol);
final clonedSetter = setter!._cloneWithSymbol(
clonedSymbol,
parent: parent,
originCategory: originCategory,
);
clonedSetter.isIncluded = clonedMethod.isIncluded;
clonedMethod.setter = clonedSetter;
Expand Down
10 changes: 9 additions & 1 deletion pkgs/ffigen/lib/src/visitor/apply_config_filters.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,15 @@ class ApplyConfigFiltersVisitation extends Visitation {
if (context.config.objectiveC == null) return;

if (!node.isInternal) {
node.filterMethods((m) => !m.unavailable && m.isIncluded);
node.filterMethods((m) {
if (m.unavailable) return false;
if (m.originCategory != null &&
m.originCategory!.originalName.isNotEmpty &&
!m.originCategory!.isIncluded) {
return false;
}
return m.isIncluded;
});
}
_visitImpl(node, node.isIncluded);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,10 @@ class CopyMethodsFromSuperTypesVisitation extends Visitation {
// methods return instancetype, because the Dart inheritance rules don't
// match the ObjC rules regarding instancetype.
// Also copy all methods from any anonymous categories.
// NOTE: The methods are copied regardless of whether the category is
// included by the config filters, since this method copying visit happens
// before the filtering visit. This is technically a bug, but it's unlikely
// to bother anyone, and the fix would be complicated. So we'll ignore it
// for now.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

😆 Great that we can fix this now!

for (final category in node.categories) {
for (final m in category.methods) {
if (category.shouldCopyMethodToInterface(m)) {
node.copyMethod(m);
node.copyMethod(m, originCategory: category);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion pkgs/ffigen/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ dev_dependencies:
dart_flutter_team_lints: ^3.5.2
json_schema: ^5.1.1
leak_tracker: ^11.0.2
objective_c: ^9.5.0
objective_c:
path: ../objective_c
test: ^1.26.2

dependency_overrides:
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/test/native_objc_test/arc_test_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>()
external void objc_autoreleasePoolPop(ffi.Pointer<ffi.Void> pool);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);

/// BadMethodTestObject
extension type BadMethodTestObject._(objc.ObjCObject object$)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);

/// BadOverrideAunt
extension type BadOverrideAunt._(objc.ObjCObject object$)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<objc.ObjCObjectImpl>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);
typedef AcceptMammal = ffi.Pointer<objc.ObjCBlockImpl>;
typedef DartAcceptMammal = objc.ObjCBlock<ffi.Bool Function(Mammal)>;
typedef AcceptPlatypus = ffi.Pointer<objc.ObjCBlockImpl>;
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/test/native_objc_test/block_test_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);
@ffi.Native<
ffi.Pointer<objc.ObjCBlockImpl> Function(
ffi.Int64,
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/test/native_objc_test/cast_test_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:ffi' as ffi;
import 'package:objective_c/objective_c.dart' as objc;
import 'package:ffi/ffi.dart' as pkg_ffi;

const _$objcVersionCheck = objc.ObjCVersionCheck(9, 5);
const _$objcVersionCheck = objc.ObjCVersionCheck(9, 6);

/// Castaway
extension type Castaway._(objc.ObjCObject object$)
Expand Down
2 changes: 2 additions & 0 deletions pkgs/ffigen/test/native_objc_test/category_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ objc-categories:
- InterfaceOnBuiltInType
- StaticAndInstanceMethodsWithSameNameCategory
- NSString
- NSURLCategory
include-transitive-objc-categories: false
ffi-native:
asset-id: 'package:ffigen/objc_test'
headers:
Expand Down
27 changes: 27 additions & 0 deletions pkgs/ffigen/test/native_objc_test/category_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,32 @@ extension type ChildOfNSString._(objc.ObjCObject object\$)
'''),
);
});

test('Excluded category filtering and instancetype methods', () {
final bindings = File(
path.join(
packagePathForTests,
'test',
'native_objc_test',
'category_test_bindings.dart',
),
).readAsStringSync();

// ExcludedCategory extension and its methods should not be generated.
expect(bindings, isNot(contains('extension ExcludedCategory')));
expect(
bindings,
isNot(contains('excludedCategoryNonInstancetypeMethod')),
);
expect(
bindings,
isNot(contains('excludedCategoryStaticNonInstancetypeMethod')),
);
expect(bindings, isNot(contains('excludedCategoryInstancetypeMethod')));
expect(
bindings,
isNot(contains('excludedCategoryStaticInstancetypeMethod')),
);
});
});
}
7 changes: 7 additions & 0 deletions pkgs/ffigen/test/native_objc_test/category_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
-(instancetype)instancetypeMethod;
@end

@interface Thing (ExcludedCategory)
-(instancetype)excludedCategoryInstancetypeMethod;
+(instancetype)excludedCategoryStaticInstancetypeMethod;
-(int32_t)excludedCategoryNonInstancetypeMethod;
+(int32_t)excludedCategoryStaticNonInstancetypeMethod;
@end

@interface Thing ()
-(int32_t)anonymousCategoryMethod;
+(int32_t)anonymousCategoryStaticMethod;
Expand Down
15 changes: 15 additions & 0 deletions pkgs/ffigen/test/native_objc_test/category_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ -(instancetype)instancetypeMethod {
}
@end

@implementation Thing (ExcludedCategory)
-(instancetype)excludedCategoryInstancetypeMethod {
return [[self class] new];
}
+(instancetype)excludedCategoryStaticInstancetypeMethod {
return [[self class] new];
}
-(int32_t)excludedCategoryNonInstancetypeMethod {
return 111;
}
+(int32_t)excludedCategoryStaticNonInstancetypeMethod {
return 222;
}
@end

@implementation ChildOfThing
@end

Expand Down
Loading
Loading