Skip to content

Commit f2a0e28

Browse files
[swift2objc] Filtering Support (#1730)
1 parent a70c1a2 commit f2a0e28

11 files changed

+826
-13
lines changed

pkgs/swift2objc/lib/src/config.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:path/path.dart' as path;
22

3+
import 'ast/_core/interfaces/declaration.dart';
4+
35
const defaultTempDirPrefix = 'swift2objc_temp_';
46
const symbolgraphFileSuffix = '.symbols.json';
57

@@ -32,12 +34,21 @@ class Config {
3234
/// intermediate files after generating the wrapper.
3335
final Uri? tempDir;
3436

35-
const Config({
36-
required this.input,
37-
required this.outputFile,
38-
this.tempDir,
39-
this.preamble,
40-
});
37+
/// Filter function to filter APIs
38+
///
39+
/// APIs can be filtered by name
40+
///
41+
/// Includes all declarations by default
42+
final bool Function(Declaration declaration) include;
43+
44+
static bool _defaultInclude(_) => true;
45+
46+
const Config(
47+
{required this.input,
48+
required this.outputFile,
49+
this.tempDir,
50+
this.preamble,
51+
this.include = Config._defaultInclude});
4152
}
4253

4354
/// Used to specify the inputs in the `config` object.

pkgs/swift2objc/lib/src/generate_wrapper.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ Future<void> generateWrapper(Config config) async {
4646
};
4747

4848
final declarations = parseAst(symbolgraphJson);
49-
final transformedDeclarations = transform(declarations);
50-
49+
final transformedDeclarations =
50+
transform(declarations, filter: config.include);
5151
final wrapperCode = generate(
5252
transformedDeclarations,
5353
moduleName: sourceModule,

pkgs/swift2objc/lib/src/parser/parsers/parse_declarations.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ List<Declaration> parseDeclarations(ParsedSymbolgraph symbolgraph) {
2525
return declarations.topLevelOnly;
2626
}
2727

28+
// TODO(https://github.com/dart-lang/native/issues/1815): Support for extensions
2829
Declaration parseDeclaration(
2930
ParsedSymbol parsedSymbol,
3031
ParsedSymbolgraph symbolgraph,
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import '../../ast/_core/interfaces/declaration.dart';
2+
import '../../ast/_core/interfaces/enum_declaration.dart';
3+
import '../../ast/_core/interfaces/function_declaration.dart';
4+
import '../../ast/_core/interfaces/variable_declaration.dart';
5+
import '../../ast/_core/shared/parameter.dart';
6+
import '../../ast/_core/shared/referred_type.dart';
7+
import '../../ast/declarations/compounds/class_declaration.dart';
8+
import '../../ast/declarations/compounds/members/initializer_declaration.dart';
9+
import '../../ast/declarations/compounds/protocol_declaration.dart';
10+
import '../../ast/declarations/compounds/struct_declaration.dart';
11+
12+
// TODO(https://github.com/dart-lang/native/issues/1814): Type restrictions have not yet been implemented in system
13+
class DependencyVisitor {
14+
final Iterable<Declaration> declarations;
15+
Set<Declaration> visitedDeclarations = {};
16+
17+
DependencyVisitor(this.declarations);
18+
19+
Set<Declaration> visit(Declaration dec) {
20+
final dependencies = <Declaration>{};
21+
22+
Iterable<Declaration> d = [dec];
23+
24+
while (true) {
25+
final deps = d.fold<Set<String>>(
26+
{}, (previous, element) => previous.union(visitDeclaration(element)));
27+
final depDecls = declarations.where((d) => deps.contains(d.id));
28+
if (depDecls.isEmpty ||
29+
(dependencies.union(depDecls.toSet()).length) ==
30+
dependencies.length) {
31+
break;
32+
} else {
33+
dependencies.addAll(depDecls);
34+
d = depDecls;
35+
}
36+
}
37+
38+
visitedDeclarations.addAll(dependencies);
39+
40+
return dependencies;
41+
}
42+
43+
Set<String> visitDeclaration(Declaration decl, [Set<String>? context]) {
44+
final cont = context ??= {};
45+
46+
// switch between declarations
47+
if (decl is ClassDeclaration) {
48+
visitClass(decl, cont);
49+
} else if (decl is ProtocolDeclaration) {
50+
visitProtocol(decl, cont);
51+
} else if (decl is StructDeclaration) {
52+
visitStruct(decl, cont);
53+
} else if (decl is FunctionDeclaration) {
54+
visitFunction(decl, cont);
55+
} else if (decl is VariableDeclaration) {
56+
visitVariable(decl, cont);
57+
} else if (decl is EnumDeclaration) {
58+
visitEnum(decl, cont);
59+
}
60+
61+
return cont;
62+
}
63+
64+
Set<String> visitEnum(EnumDeclaration decl, [Set<String>? context]) {
65+
final cont = context ??= {};
66+
67+
// visit nested declarations
68+
for (var n in decl.nestedDeclarations) {
69+
visitDeclaration(n, cont);
70+
}
71+
72+
// visit protocols
73+
for (var p in decl.conformedProtocols) {
74+
visitProtocol(p.declaration, cont);
75+
}
76+
77+
// ensure generic types do not enter
78+
cont.removeWhere(
79+
(t) => decl.typeParams.map((type) => type.name).contains(t));
80+
81+
return cont;
82+
}
83+
84+
Set<String> visitStruct(StructDeclaration decl, [Set<String>? context]) {
85+
final cont = context ??= {};
86+
87+
// visit variables
88+
for (var d in decl.properties) {
89+
visitVariable(d, cont);
90+
}
91+
92+
// visit methods
93+
for (var m in decl.methods) {
94+
visitFunction(m, cont);
95+
}
96+
97+
// visit initializers
98+
for (var i in decl.initializers) {
99+
visitInitializer(i, cont);
100+
}
101+
102+
// visit nested declarations
103+
for (var n in decl.nestedDeclarations) {
104+
visitDeclaration(n, cont);
105+
}
106+
107+
// visit protocols
108+
for (var p in decl.conformedProtocols) {
109+
visitProtocol(p.declaration, cont);
110+
}
111+
112+
// ensure generic types do not enter
113+
cont.removeWhere(
114+
(t) => decl.typeParams.map((type) => type.name).contains(t));
115+
116+
return cont;
117+
}
118+
119+
Set<String> visitClass(ClassDeclaration decl, [Set<String>? context]) {
120+
final cont = context ??= {};
121+
122+
// visit variables
123+
for (var d in decl.properties) {
124+
visitVariable(d, cont);
125+
}
126+
127+
// visit methods
128+
for (var m in decl.methods) {
129+
visitFunction(m, cont);
130+
}
131+
132+
// visit initializers
133+
for (var i in decl.initializers) {
134+
visitInitializer(i, cont);
135+
}
136+
137+
// visit super if any
138+
if (decl.superClass != null) {
139+
visitDeclaration(decl.superClass!.declaration, cont);
140+
}
141+
142+
// visit nested declarations
143+
for (var n in decl.nestedDeclarations) {
144+
visitDeclaration(n, cont);
145+
}
146+
147+
// visit protocols
148+
for (var p in decl.conformedProtocols) {
149+
visitProtocol(p.declaration, cont);
150+
}
151+
152+
// ensure generic types do not enter
153+
cont.removeWhere(
154+
(t) => decl.typeParams.map((type) => type.name).contains(t));
155+
156+
return cont;
157+
}
158+
159+
Set<String> visitProtocol(ProtocolDeclaration decl, [Set<String>? context]) {
160+
final cont = context ??= {};
161+
162+
// visit variables
163+
for (var d in decl.properties) {
164+
visitVariable(d, cont);
165+
}
166+
167+
// visit methods
168+
for (var m in decl.methods) {
169+
visitFunction(m, cont);
170+
}
171+
172+
// visit initializers
173+
for (var i in decl.initializers) {
174+
visitInitializer(i, cont);
175+
}
176+
177+
// visit nested declarations
178+
for (var n in decl.nestedDeclarations) {
179+
visitDeclaration(n, cont);
180+
}
181+
182+
// visit protocols
183+
for (var p in decl.conformedProtocols) {
184+
visitProtocol(p.declaration, cont);
185+
}
186+
187+
// ensure generic types do not enter
188+
cont.removeWhere(
189+
(t) => decl.typeParams.map((type) => type.name).contains(t));
190+
191+
return cont;
192+
}
193+
194+
Set<String> visitInitializer(InitializerDeclaration decl,
195+
[Set<String>? context]) {
196+
final cont = context ??= {};
197+
198+
// similar to `visitMethod`, except no return type
199+
for (var p in decl.params) {
200+
visitParameter(p, cont);
201+
}
202+
203+
return cont;
204+
}
205+
206+
Set<String> visitFunction(FunctionDeclaration decl, [Set<String>? context]) {
207+
final cont = context ??= {};
208+
209+
// visit parameters
210+
for (var p in decl.params) {
211+
visitParameter(p, cont);
212+
}
213+
214+
// ensure generic types do not enter
215+
cont.removeWhere(
216+
(t) => decl.typeParams.map((type) => type.name).contains(t));
217+
218+
// visit return type
219+
visitType(decl.returnType, cont);
220+
221+
return cont;
222+
}
223+
224+
Set<String> visitParameter(Parameter decl, [Set<String>? context]) {
225+
final cont = context ??= {};
226+
227+
// just visit type of parameter
228+
visitType(decl.type, cont);
229+
230+
return cont;
231+
}
232+
233+
Set<String> visitVariable(VariableDeclaration decl, [Set<String>? context]) {
234+
final cont = context ??= {};
235+
236+
// just return property type
237+
visitType(decl.type, cont);
238+
239+
return cont;
240+
}
241+
242+
Set<String> visitType(ReferredType type, [Set<String>? context]) {
243+
final cont = context ??= {};
244+
245+
// we need to confirm the types located
246+
// check what kind of type [type] is
247+
switch (type) {
248+
case DeclaredType():
249+
cont.add(type.id);
250+
break;
251+
case GenericType():
252+
// do nothing
253+
break;
254+
case OptionalType():
255+
visitType(type.child, cont);
256+
}
257+
return cont;
258+
}
259+
}

pkgs/swift2objc/lib/src/transformer/transform.dart

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,40 @@ import '../ast/_core/interfaces/nestable_declaration.dart';
88
import '../ast/declarations/compounds/class_declaration.dart';
99
import '../ast/declarations/compounds/struct_declaration.dart';
1010
import '../ast/declarations/globals/globals.dart';
11+
import '_core/dependencies.dart';
1112
import '_core/unique_namer.dart';
1213
import 'transformers/transform_compound.dart';
1314
import 'transformers/transform_globals.dart';
1415

1516
typedef TransformationMap = Map<Declaration, Declaration>;
1617

17-
List<Declaration> transform(List<Declaration> declarations) {
18+
Set<Declaration> generateDependencies(
19+
Iterable<Declaration> decls, Iterable<Declaration> allDecls) {
20+
final visitor = DependencyVisitor(allDecls);
21+
for (final dec in decls) {
22+
visitor.visit(dec);
23+
}
24+
25+
return visitor.visitedDeclarations;
26+
}
27+
28+
/// Transforms the given declarations into the desired ObjC wrapped declarations
29+
List<Declaration> transform(List<Declaration> declarations,
30+
{required bool Function(Declaration) filter}) {
1831
final transformationMap = <Declaration, Declaration>{};
1932

33+
final declarations0 = declarations.where(filter).toSet();
34+
declarations0.addAll(generateDependencies(declarations0, declarations));
35+
2036
final globalNamer = UniqueNamer(
21-
declarations.map((declaration) => declaration.name),
37+
declarations0.map((declaration) => declaration.name),
2238
);
2339

2440
final globals = Globals(
25-
functions: declarations.whereType<GlobalFunctionDeclaration>().toList(),
26-
variables: declarations.whereType<GlobalVariableDeclaration>().toList(),
41+
functions: declarations0.whereType<GlobalFunctionDeclaration>().toList(),
42+
variables: declarations0.whereType<GlobalVariableDeclaration>().toList(),
2743
);
28-
final nonGlobals = declarations
44+
final nonGlobals = declarations0
2945
.where(
3046
(declaration) =>
3147
declaration is! GlobalFunctionDeclaration &&

0 commit comments

Comments
 (0)