Skip to content

Commit 7855c80

Browse files
authored
[ffigen] Add flags controlling how transitive deps are pulled in (#1687)
1 parent b90f4f2 commit 7855c80

25 files changed

+3386
-2553
lines changed

pkgs/ffigen/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
- https://github.com/dart-lang/native/issues/1582
77
- https://github.com/dart-lang/native/issues/1594
88
- https://github.com/dart-lang/native/issues/1595
9+
- Add `includeTransitiveObjCInterfaces` and `includeTransitiveObjCProtocols`
10+
config flags, which control whether transitively included ObjC interfaces and
11+
protocols are generated.
12+
- __Breaking change__: `includeTransitiveObjCInterfaces` defaults to false,
13+
which changes the default behavior from pulling in all transitive deps, to
14+
generating them as stubs.
915

1016
## 15.0.0
1117

pkgs/ffigen/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,31 @@ objc-protocols:
810810

811811
</td>
812812
</tr>
813+
814+
<tr>
815+
<td>
816+
include-transitive-objc-interfaces<br><br>
817+
include-transitive-objc-protocols
818+
</td>
819+
<td>
820+
By default, Objective-C interfaces and protocols that are not directly
821+
included by the inclusion rules, but are transitively depended on by
822+
the inclusions, are not fully code genned. Transitively included
823+
interfaces are generated as stubs, and transitive protocols are omitted.
824+
<br>
825+
If these flags are enabled, transitively included interfaces and protocols
826+
are fully code genned.
827+
<br>
828+
<b>Default: false</b>
829+
</td>
830+
<td>
831+
832+
```yaml
833+
include-transitive-objc-interfaces: true
834+
include-transitive-objc-protocols: true
835+
```
836+
</td>
837+
</tr>
813838
</tbody>
814839
</table>
815840

pkgs/ffigen/ffigen.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,12 @@
406406
"include-unused-typedefs": {
407407
"type": "boolean"
408408
},
409+
"include-transitive-objc-interfaces": {
410+
"type": "boolean"
411+
},
412+
"include-transitive-objc-protocols": {
413+
"type": "boolean"
414+
},
409415
"generate-for-package-objective-c": {
410416
"type": "boolean"
411417
},

pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ class ObjCBuiltInFunctions {
143143
// for float return types we need objc_msgSend_fpret.
144144
final _msgSendFuncs = <String, ObjCMsgSendFunc>{};
145145
ObjCMsgSendFunc getMsgSendFunc(Type returnType, List<Parameter> params) {
146+
params = _methodSigParams(params);
147+
returnType = _methodSigType(returnType);
146148
final id = _methodSigId(returnType, params);
147149
return _msgSendFuncs[id] ??= ObjCMsgSendFunc(
148150
'_objc_msgSend_${fnvHash32(id).toRadixString(36)}',
@@ -161,19 +163,50 @@ class ObjCBuiltInFunctions {
161163

162164
String _methodSigId(Type returnType, List<Parameter> params) {
163165
final paramIds = <String>[];
164-
for (final param in params) {
165-
final retainFunc = param.type.generateRetain('');
166-
166+
for (final p in params) {
167167
// The trampoline ID is based on the getNativeType of the param. Objects
168168
// and blocks both have `id` as their native type, but need separate
169169
// trampolines since they have different retain functions. So add the
170-
// retainFunc (if any) to all the param IDs.
171-
paramIds.add('${param.getNativeType()}-${retainFunc ?? ''}');
170+
// retain function (if any) to all the param IDs.
171+
paramIds.add(p.getNativeType(varName: p.type.generateRetain('') ?? ''));
172172
}
173-
final rt = '${returnType.getNativeType()}-${returnType.generateRetain('')}';
173+
final rt =
174+
returnType.getNativeType(varName: returnType.generateRetain('') ?? '');
174175
return '$rt,${paramIds.join(',')}';
175176
}
176177

178+
Type _methodSigType(Type t) {
179+
if (t is FunctionType) {
180+
return FunctionType(
181+
returnType: _methodSigType(t.returnType),
182+
parameters: _methodSigParams(t.parameters),
183+
varArgParameters: _methodSigParams(t.varArgParameters),
184+
);
185+
} else if (t is ObjCBlock) {
186+
return ObjCBlockPointer();
187+
} else if (t is ObjCInterface) {
188+
return ObjCObjectPointer();
189+
} else if (t is ConstantArray) {
190+
return ConstantArray(
191+
t.length,
192+
_methodSigType(t.child),
193+
useArrayType: t.useArrayType,
194+
);
195+
} else if (t is PointerType) {
196+
return PointerType(_methodSigType(t.child));
197+
} else if (t is ObjCNullable) {
198+
return _methodSigType(t.child);
199+
} else if (t is Typealias) {
200+
return _methodSigType(t.type);
201+
}
202+
return t;
203+
}
204+
205+
List<Parameter> _methodSigParams(List<Parameter> params) => params
206+
.map((p) =>
207+
Parameter(type: _methodSigType(p.type), objCConsumed: p.objCConsumed))
208+
.toList();
209+
177210
final _blockTrampolines = <String, ObjCListenerBlockTrampoline>{};
178211
ObjCListenerBlockTrampoline? getListenerBlockTrampoline(ObjCBlock block) {
179212
final id = _methodSigId(block.returnType, block.params);

pkgs/ffigen/lib/src/code_generator/objc_interface.dart

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class ObjCInterface extends BindingType with ObjCMethods {
2222
@override
2323
final ObjCBuiltInFunctions builtInFunctions;
2424

25+
// Filled by ListBindingsVisitation.
26+
bool generateAsStub = false;
27+
2528
ObjCInterface({
2629
super.usr,
2730
required String super.originalName,
@@ -53,27 +56,23 @@ class ObjCInterface extends BindingType with ObjCMethods {
5356

5457
@override
5558
BindingString toBindingString(Writer w) {
56-
String paramsToString(List<Parameter> params) {
57-
final stringParams = <String>[
58-
for (final p in params)
59-
'${_getConvertedType(p.type, w, name)} ${p.name}',
60-
];
61-
return '(${stringParams.join(", ")})';
62-
}
63-
6459
final s = StringBuffer();
6560
s.write('\n');
61+
if (generateAsStub) {
62+
s.write('''
63+
/// WARNING: $name is a stub. To generate bindings for this class, include
64+
/// $name in your config's objc-interfaces list.
65+
///
66+
''');
67+
}
6668
s.write(makeDartDoc(dartDoc ?? originalName));
6769

68-
final methodNamer = createMethodRenamer(w);
69-
7070
final rawObjType = PointerType(objCObjectType).getCType(w);
7171
final wrapObjType = ObjCBuiltInFunctions.objectBase.gen(w);
72-
7372
final superTypeIsInPkgObjc = superType == null;
7473

75-
// Class declaration.
76-
s.write('''class $name extends ${superType?.getDartType(w) ?? wrapObjType} {
74+
s.write('''
75+
class $name extends ${superType?.getDartType(w) ?? wrapObjType} {
7776
$name._($rawObjType pointer,
7877
{bool retain = false, bool release = false}) :
7978
${superTypeIsInPkgObjc ? 'super' : 'super.castFromPointer'}
@@ -88,6 +87,30 @@ class ObjCInterface extends BindingType with ObjCMethods {
8887
{bool retain = false, bool release = false}) :
8988
this._(other, retain: retain, release: release);
9089
90+
${generateAsStub ? '' : _generateMethods(w)}
91+
}
92+
93+
''');
94+
95+
return BindingString(
96+
type: BindingStringType.objcInterface, string: s.toString());
97+
}
98+
99+
String _generateMethods(Writer w) {
100+
String paramsToString(List<Parameter> params) {
101+
final stringParams = <String>[
102+
for (final p in params)
103+
'${_getConvertedType(p.type, w, name)} ${p.name}',
104+
];
105+
return '(${stringParams.join(", ")})';
106+
}
107+
108+
final methodNamer = createMethodRenamer(w);
109+
final wrapObjType = ObjCBuiltInFunctions.objectBase.gen(w);
110+
final s = StringBuffer();
111+
112+
// Class declaration.
113+
s.write('''
91114
/// Returns whether [obj] is an instance of [$name].
92115
static bool isInstance($wrapObjType obj) {
93116
return ${_isKindOfClassMsgSend.invoke(
@@ -215,10 +238,7 @@ class ObjCInterface extends BindingType with ObjCMethods {
215238
s.write('\n }\n');
216239
}
217240

218-
s.write('}\n\n');
219-
220-
return BindingString(
221-
type: BindingStringType.objcInterface, string: s.toString());
241+
return s.toString();
222242
}
223243

224244
@override

pkgs/ffigen/lib/src/code_generator/pointer.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class PointerType extends Type {
1616
factory PointerType(Type child) {
1717
if (child == objCObjectType) {
1818
return ObjCObjectPointer();
19+
} else if (child == objCBlockType) {
20+
return ObjCBlockPointer();
1921
}
2022
return PointerType._(child);
2123
}
@@ -108,6 +110,7 @@ class ObjCObjectPointer extends PointerType {
108110
factory ObjCObjectPointer() => _inst;
109111

110112
static final _inst = ObjCObjectPointer._();
113+
ObjCObjectPointer.__(super.child) : super._();
111114
ObjCObjectPointer._() : super._(objCObjectType);
112115

113116
@override
@@ -143,3 +146,17 @@ class ObjCObjectPointer extends PointerType {
143146
@override
144147
String? generateRetain(String value) => 'objc_retain($value)';
145148
}
149+
150+
/// A pointer to an Objective C block.
151+
class ObjCBlockPointer extends ObjCObjectPointer {
152+
factory ObjCBlockPointer() => _inst;
153+
154+
static final _inst = ObjCBlockPointer._();
155+
ObjCBlockPointer._() : super.__(objCBlockType);
156+
157+
@override
158+
String getDartType(Writer w) => '${w.objcPkgPrefix}.ObjCBlockBase';
159+
160+
@override
161+
String? generateRetain(String value) => 'objc_retainBlock($value)';
162+
}

pkgs/ffigen/lib/src/config_provider/config.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ abstract interface class Config {
7878
/// If enabled, unused typedefs will also be generated.
7979
bool get includeUnusedTypedefs;
8080

81+
/// If enabled, Objective C interfaces that are not explicitly included by
82+
/// the [DeclarationFilters], but are transitively included by other bindings,
83+
/// will be code-genned as if they were included. If disabled, these
84+
/// transitively included interfaces will be generated as stubs instead.
85+
bool get includeTransitiveObjCInterfaces;
86+
87+
/// If enabled, Objective C protocols that are not explicitly included by
88+
/// the [DeclarationFilters], but are transitively included by other bindings,
89+
/// will be code-genned as if they were included. If disabled, these
90+
/// transitively included protocols will not be generated at all.
91+
bool get includeTransitiveObjCProtocols;
92+
8193
/// Undocumented option that changes code generation for package:objective_c.
8294
/// The main difference is whether NSObject etc are imported from
8395
/// package:objective_c (the default) or code genned like any other class.
@@ -195,6 +207,8 @@ abstract interface class Config {
195207
DeclarationFilters? objcInterfaces,
196208
DeclarationFilters? objcProtocols,
197209
bool includeUnusedTypedefs = false,
210+
bool includeTransitiveObjCInterfaces = false,
211+
bool includeTransitiveObjCProtocols = false,
198212
bool generateForPackageObjectiveC = false,
199213
bool sort = false,
200214
bool useSupportedTypedefs = true,
@@ -250,6 +264,8 @@ abstract interface class Config {
250264
objcInterfaces: objcInterfaces ?? DeclarationFilters.excludeAll,
251265
objcProtocols: objcProtocols ?? DeclarationFilters.excludeAll,
252266
includeUnusedTypedefs: includeUnusedTypedefs,
267+
includeTransitiveObjCInterfaces: includeTransitiveObjCInterfaces,
268+
includeTransitiveObjCProtocols: includeTransitiveObjCProtocols,
253269
generateForPackageObjectiveC: generateForPackageObjectiveC,
254270
sort: sort,
255271
useSupportedTypedefs: useSupportedTypedefs,

pkgs/ffigen/lib/src/config_provider/config_impl.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ class ConfigImpl implements Config {
7676
@override
7777
final bool includeUnusedTypedefs;
7878

79+
@override
80+
final bool includeTransitiveObjCInterfaces;
81+
82+
@override
83+
final bool includeTransitiveObjCProtocols;
84+
7985
@override
8086
final bool generateForPackageObjectiveC;
8187

@@ -198,6 +204,8 @@ class ConfigImpl implements Config {
198204
required this.objcInterfaces,
199205
required this.objcProtocols,
200206
required this.includeUnusedTypedefs,
207+
required this.includeTransitiveObjCInterfaces,
208+
required this.includeTransitiveObjCProtocols,
201209
required this.generateForPackageObjectiveC,
202210
required this.sort,
203211
required this.useSupportedTypedefs,

pkgs/ffigen/lib/src/config_provider/yaml_config.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,22 @@ class YamlConfig implements Config {
137137
bool get includeUnusedTypedefs => _includeUnusedTypedefs;
138138
late bool _includeUnusedTypedefs;
139139

140+
/// If enabled, Objective C interfaces that are not explicitly included by
141+
/// the [DeclarationFilters], but are transitively included by other bindings,
142+
/// will be code-genned as if they were included. If disabled, these
143+
/// transitively included interfaces will be generated as stubs instead.
144+
@override
145+
bool get includeTransitiveObjCInterfaces => _includeTransitiveObjCInterfaces;
146+
late bool _includeTransitiveObjCInterfaces;
147+
148+
/// If enabled, Objective C protocols that are not explicitly included by
149+
/// the [DeclarationFilters], but are transitively included by other bindings,
150+
/// will be code-genned as if they were included. If disabled, these
151+
/// transitively included protocols will not be generated at all.
152+
@override
153+
bool get includeTransitiveObjCProtocols => _includeTransitiveObjCProtocols;
154+
late bool _includeTransitiveObjCProtocols;
155+
140156
/// Undocumented option that changes code generation for package:objective_c.
141157
/// The main difference is whether NSObject etc are imported from
142158
/// package:objective_c (the default) or code genned like any other class.
@@ -753,6 +769,20 @@ class YamlConfig implements Config {
753769
resultOrDefault: (node) =>
754770
_includeUnusedTypedefs = node.value as bool,
755771
),
772+
HeterogeneousMapEntry(
773+
key: strings.includeTransitiveObjCInterfaces,
774+
valueConfigSpec: BoolConfigSpec(),
775+
defaultValue: (node) => false,
776+
resultOrDefault: (node) =>
777+
_includeTransitiveObjCInterfaces = node.value as bool,
778+
),
779+
HeterogeneousMapEntry(
780+
key: strings.includeTransitiveObjCProtocols,
781+
valueConfigSpec: BoolConfigSpec(),
782+
defaultValue: (node) => false,
783+
resultOrDefault: (node) =>
784+
_includeTransitiveObjCProtocols = node.value as bool,
785+
),
756786
HeterogeneousMapEntry(
757787
key: strings.generateForPackageObjectiveC,
758788
valueConfigSpec: BoolConfigSpec(),

0 commit comments

Comments
 (0)