Skip to content

Commit f890a91

Browse files
authored
[ffigen] Method filtering (#1511)
1 parent 029838d commit f890a91

18 files changed

+411
-71
lines changed

pkgs/ffigen/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
classes as opposed to `abstract` classes.
2929
- Fix some bugs in the way ObjC method families and ownership annotations were
3030
being handled: https://github.com/dart-lang/native/issues/1446
31+
- Apply the existing `member-rename` option to ObjC interface and protocol
32+
methods and properties.
33+
- Add a `member-filter` option that filters ObjC interface and protocol methods
34+
and properties.
3135

3236
## 13.0.0
3337

pkgs/ffigen/README.md

Lines changed: 109 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ dart run ffigen --compiler-opts "-I/headers
190190
</td>
191191
</tr>
192192
<tr>
193-
<td>compiler-opts-automatic -> macos -> include-c-standard-library</td>
193+
<td>compiler-opts-automatic.macos.include-c-standard-library</td>
194194
<td>Tries to automatically find and add C standard library path to
195195
compiler-opts on macos.<br>
196196
<b>Default: true</b>
@@ -213,7 +213,8 @@ compiler-opts-automatic:
213213
Options -<br>
214214
- Include/Exclude declarations.<br>
215215
- Rename declarations.<br>
216-
- Rename enum and struct members.<br>
216+
- Rename enum, struct, and union members, function parameters, and ObjC
217+
interface and protocol methods and properties.<br>
217218
- Expose symbol-address for functions and globals.<br>
218219
</td>
219220
<td>
@@ -313,7 +314,7 @@ include-unused-typedefs: true
313314
</td>
314315
</tr>
315316
<tr>
316-
<td>functions -> expose-typedefs</td>
317+
<td>functions.expose-typedefs</td>
317318
<td>Generate the typedefs to Native and Dart type of a function<br>
318319
<b>Default: Inline types are used and no typedefs to Native/Dart
319320
type are generated.</b>
@@ -336,7 +337,7 @@ functions:
336337
</td>
337338
</tr>
338339
<tr>
339-
<td>functions -> leaf</td>
340+
<td>functions.leaf</td>
340341
<td>Set isLeaf:true for functions.<br>
341342
<b>Default: all functions are excluded.</b>
342343
</td>
@@ -358,7 +359,7 @@ functions:
358359
</td>
359360
</tr>
360361
<tr>
361-
<td>functions -> variadic-arguments</td>
362+
<td>functions.variadic-arguments</td>
362363
<td>Generate multiple functions with different variadic arguments.<br>
363364
<b>Default: var args for any function are ignored.</b>
364365
</td>
@@ -378,7 +379,7 @@ functions:
378379
</td>
379380
</tr>
380381
<tr>
381-
<td>structs -> pack</td>
382+
<td>structs.pack</td>
382383
<td>Override the @Packed(X) annotation for generated structs.<br><br>
383384
<i>Options - none, 1, 2, 4, 8, 16</i><br>
384385
You can use RegExp to match with the <b>generated</b> names.<br><br>
@@ -417,8 +418,8 @@ comments:
417418
</td>
418419
</tr>
419420
<tr>
420-
<td>structs -> dependency-only<br><br>
421-
unions -> dependency-only
421+
<td>structs.dependency-only<br><br>
422+
unions.dependency-only
422423
</td>
423424
<td>If `opaque`, generates empty `Opaque` structs/unions if they
424425
were not included in config (but were added since they are a dependency) and
@@ -613,7 +614,7 @@ language: 'objc'
613614
</td>
614615
</tr>
615616
<tr>
616-
<td>output -> objc-bindings</td>
617+
<td>output.objc-bindings</td>
617618
<td>
618619
Choose where the generated ObjC code (if any) is placed. The default path
619620
is `'${output.bindings}.m'`, so if your Dart bindings are in
@@ -635,7 +636,7 @@ output:
635636
</td>
636637
</tr>
637638
<tr>
638-
<td>output -> symbol-file</td>
639+
<td>output.symbol-file</td>
639640
<td>Generates a symbol file yaml containing all types defined in the generated output.</td>
640641
<td>
641642

@@ -651,7 +652,7 @@ output:
651652
</td>
652653
</tr>
653654
<tr>
654-
<td>import -> symbol-files</td>
655+
<td>import.symbol-files</td>
655656
<td>Import symbols from a symbol file. Used for sharing type definitions from other pacakges.</td>
656657
<td>
657658

@@ -751,7 +752,7 @@ objc-protocols:
751752

752753
<tr>
753754
<td>
754-
objc-interfaces -> module<br><br>objc-protocols -> module
755+
objc-interfaces.module<br><br>objc-protocols.module
755756
</td>
756757
<td>
757758
Adds a module prefix to the interface/protocol name when loading it
@@ -778,6 +779,37 @@ objc-interfaces:
778779

779780
</td>
780781
</tr>
782+
783+
<tr>
784+
<td>
785+
objc-interfaces.member-filter<br><br>objc-protocols.member-filter
786+
</td>
787+
<td>
788+
Filters interface and protocol methods and properties. This is a map from
789+
interface name to a list of method include and exclude rules. The
790+
interface name can be a regexp. The include and exclude rules work exactly
791+
like any other declaration. See
792+
<a href="#how-does-objc-method-filtering-work">below</a> for more details.
793+
</td>
794+
<td>
795+
796+
```yaml
797+
objc-interfaces:
798+
member-filter:
799+
MyInterface:
800+
include:
801+
- "someMethod:withArg:"
802+
# Since MyInterface has an include rule, all other methods
803+
# are excluded by default.
804+
objc-protocols:
805+
member-filter:
806+
NS.*: # Matches all protocols starting with NS.
807+
exclude:
808+
- copy.* # Remove all copy methods from these protocols.
809+
```
810+
811+
</td>
812+
</tr>
781813
</tbody>
782814
</table>
783815

@@ -884,7 +916,7 @@ be future-proof against new additions to the enums.
884916
This happens when an excluded struct/union is a dependency to some included declaration.
885917
(A dependency means a struct is being passed/returned by a function or is member of another struct in some way)
886918

887-
Note: If you supply `structs` -> `dependency-only` as `opaque` ffigen will generate
919+
Note: If you supply `structs.dependency-only` as `opaque` ffigen will generate
888920
these struct dependencies as `Opaque` if they were only passed by reference(pointer).
889921
```yaml
890922
structs:
@@ -968,12 +1000,74 @@ Ffigen can sometimes generate a lot of logs, especially when it's parsing a lot
9681000
### How can type definitions be shared?
9691001

9701002
Ffigen can share type definitions using symbol files.
971-
- A package can generate a symbol file using the `output -> symbol-file` config.
972-
- And another package can then import this, using `import -> symbol-files` config.
1003+
- A package can generate a symbol file using the `output.symbol-file` config.
1004+
- And another package can then import this, using `import.symbol-files` config.
9731005
- Doing so will reuse all the types such as Struct/Unions, and will automatically
9741006
exclude generating other types (E.g functions, enums, macros).
9751007

9761008
Checkout `examples/shared_bindings` for details.
9771009

9781010
For manually reusing definitions from another package, the `library-imports`
9791011
and `type-map` config can be used.
1012+
1013+
### How does ObjC method filtering work?
1014+
1015+
Methods and properties on ObjC interfaces and protocols can be filtered using
1016+
the `member-filter` option under `objc-interfaces` and `objc-protocols`. For
1017+
simplicity we'll focus on interface methods, but the same rules apply to
1018+
properties and protocols. There are two parts to the filtering process: matching
1019+
the interface, and then filtering the method.
1020+
1021+
The syntax of `member-filter` is a YAML map from a pattern to some
1022+
`include`/`exclude` rules, and `include` and `exclude` are each a list of
1023+
patterns.
1024+
1025+
```yaml
1026+
objc-interfaces:
1027+
member-filter:
1028+
MyInterface: # Matches an interface.
1029+
include:
1030+
- "someMethod:withArg:" # Matches a method.
1031+
exclude:
1032+
- someOtherMethod # Matches a method.
1033+
```
1034+
1035+
The interface matching logic is the same as the matching logic for the
1036+
`member-rename` option:
1037+
1038+
- The pattern is compared against the original name of the interface (before any
1039+
renaming is applied).
1040+
- The pattern may be a string or a regexp, but in either case they must match
1041+
the entire interface name.
1042+
- If the pattern contains only alphanumeric characters, or `_`, it is treated as
1043+
a string rather than a regex.
1044+
- String patterns take precedence over regexps. That is, if an interface matches
1045+
both a regexp pattern, and a string pattern, it uses the string pattern's
1046+
`include`/`exclude` rules.
1047+
1048+
The method filtering logic uses the same `include`/`exclude` rules as the rest
1049+
of the config:
1050+
1051+
- `include` and `exclude` are a list of patterns.
1052+
- The patterns are compared against the original name of the method, before
1053+
renaming.
1054+
- The patterns can be strings or regexps, but must match the entire method name.
1055+
- The method name is in ObjC selector syntax, which means that the method name
1056+
and all the external parameter names are concatenated together with `:`
1057+
characters. This is the same name you'll see in ObjC's API documentation.
1058+
- **NOTE:** Since the pattern must match the entire method name, and most ObjC
1059+
method names end with a `:`, it's a good idea to surround the pattern with
1060+
quotes, `"`. Otherwise YAML will think you're defining a map key.
1061+
- If no `include` or `exclude` rules are defined, all methods are included,
1062+
regardless of the top level `exclude-all-by-default` rule.
1063+
- If only `include` rules are `defined`, all non-matching methods are excluded.
1064+
- If only `exclude` rules are `defined`, all non-matching methods are included.
1065+
- If both `include` and `exclude` rules are defined, the `exclude` rules take
1066+
precedence. That is, if a method name matches both an `include` rule and an
1067+
`exclude` rule, the method is excluded. All non-matching methods are also
1068+
excluded.
1069+
1070+
The property filtering rules live in the same `objc-interfaces.member-filter`
1071+
option as the methods. There is no distinction between methods and properties in
1072+
the filters. The protocol filtering rules live in
1073+
`objc-protocols.member-filter`.

pkgs/ffigen/ffigen.schema.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@
341341
"member-rename": {
342342
"$ref": "#/$defs/memberRename"
343343
},
344+
"member-filter": {
345+
"$ref": "#/$defs/memberFilter"
346+
},
344347
"module": {
345348
"$ref": "#/$defs/objcModule"
346349
}
@@ -362,6 +365,9 @@
362365
"member-rename": {
363366
"$ref": "#/$defs/memberRename"
364367
},
368+
"member-filter": {
369+
"$ref": "#/$defs/memberFilter"
370+
},
365371
"module": {
366372
"$ref": "#/$defs/objcModule"
367373
}
@@ -561,6 +567,14 @@
561567
"opaque"
562568
]
563569
},
570+
"memberFilter": {
571+
"type": "object",
572+
"patternProperties": {
573+
".*": {
574+
"$ref": "#/$defs/includeExclude"
575+
}
576+
}
577+
},
564578
"objcModule": {
565579
"type": "object",
566580
"patternProperties": {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,16 @@ enum ObjCMethodFamily {
154154

155155
class ObjCProperty {
156156
final String originalName;
157+
final String name;
157158
String? dartName;
158159

159-
ObjCProperty(this.originalName);
160+
ObjCProperty({required this.originalName, required this.name});
160161
}
161162

162163
class ObjCMethod {
163164
final String? dartDoc;
164165
final String originalName;
166+
final String name;
165167
final ObjCProperty? property;
166168
Type returnType;
167169
final List<Parameter> params;
@@ -177,6 +179,7 @@ class ObjCMethod {
177179

178180
ObjCMethod({
179181
required this.originalName,
182+
required this.name,
180183
this.property,
181184
this.dartDoc,
182185
required this.kind,
@@ -232,7 +235,7 @@ class ObjCMethod {
232235
// just run the name through uniqueNamer. Instead they need to share
233236
// the dartName, which is run through uniqueNamer.
234237
if (property!.dartName == null) {
235-
property!.dartName = uniqueNamer.makeUnique(property!.originalName);
238+
property!.dartName = uniqueNamer.makeUnique(property!.name);
236239
}
237240
return property!.dartName!;
238241
}
@@ -241,7 +244,7 @@ class ObjCMethod {
241244
// foo:
242245
// foo:someArgName:
243246
// So replace all ':' with '_'.
244-
return uniqueNamer.makeUnique(originalName.replaceAll(':', '_'));
247+
return uniqueNamer.makeUnique(name.replaceAll(':', '_'));
245248
}
246249

247250
bool sameAs(ObjCMethod other) {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,21 +305,29 @@ abstract interface class DeclarationFilters {
305305
/// Applies renaming and returns the result.
306306
String rename(Declaration declaration);
307307

308-
/// Applies member renaming and returns the result.
308+
/// Applies member renaming and returns the result. Used for struct/union
309+
/// fields, enum elements, function params, and ObjC
310+
/// interface/protocol methods/properties.
309311
String renameMember(Declaration declaration, String member);
310312

313+
/// Whether a member of a declaration should be included. Used for ObjC
314+
/// interface/protocol methods/properties.
315+
bool shouldIncludeMember(Declaration declaration, String member);
316+
311317
factory DeclarationFilters({
312318
bool Function(Declaration declaration)? shouldInclude,
313319
bool Function(Declaration declaration)? shouldIncludeSymbolAddress,
314320
String Function(Declaration declaration)? rename,
315321
String Function(Declaration declaration, String member)? renameMember,
322+
bool Function(Declaration declaration, String member)? shouldIncludeMember,
316323
}) =>
317324
DeclarationFiltersImpl(
318325
shouldIncludeFunc: shouldInclude ?? (_) => false,
319326
shouldIncludeSymbolAddressFunc:
320327
shouldIncludeSymbolAddress ?? (_) => false,
321328
renameFunc: rename ?? (declaration) => declaration.originalName,
322329
renameMemberFunc: renameMember ?? (_, member) => member,
330+
shouldIncludeMemberFunc: shouldIncludeMember ?? (_, __) => true,
323331
);
324332

325333
static final excludeAll = DeclarationFilters();

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,17 @@ class DeclarationFiltersImpl implements DeclarationFilters {
249249
shouldIncludeSymbolAddressFunc(declaration);
250250
final bool Function(Declaration declaration) shouldIncludeSymbolAddressFunc;
251251

252+
@override
253+
bool shouldIncludeMember(Declaration declaration, String member) =>
254+
shouldIncludeMemberFunc(declaration, member);
255+
final bool Function(Declaration declaration, String member)
256+
shouldIncludeMemberFunc;
257+
252258
DeclarationFiltersImpl({
253259
required this.renameFunc,
254260
required this.renameMemberFunc,
255261
required this.shouldIncludeFunc,
256262
required this.shouldIncludeSymbolAddressFunc,
263+
required this.shouldIncludeMemberFunc,
257264
});
258265
}

0 commit comments

Comments
 (0)