Skip to content

Commit da16269

Browse files
[pigeon] Adds Dart implementation of ProxyApi (flutter#6043)
Part of flutter#134777
1 parent 3be3ec1 commit da16269

16 files changed

+8259
-128
lines changed

packages/pigeon/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 17.2.0
2+
3+
* [dart] Adds implementation for `@ProxyApi`.
4+
15
## 17.1.3
26

37
* [objc] Fixes double prefixes added to enum names.

packages/pigeon/lib/ast.dart

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,133 @@ class AstProxyApi extends Api {
177177
(ApiField field) => !field.isAttached,
178178
);
179179

180+
/// A list of AstProxyApis where each `extends` the API that follows it.
181+
///
182+
/// Returns an empty list if this api does not extend a ProxyApi.
183+
///
184+
/// This method assumes the super classes of each ProxyApi doesn't create a
185+
/// loop. Throws a [ArgumentError] if a loop is found.
186+
///
187+
/// This method also assumes that all super classes are ProxyApis. Otherwise,
188+
/// throws an [ArgumentError].
189+
Iterable<AstProxyApi> allSuperClasses() {
190+
final List<AstProxyApi> superClassChain = <AstProxyApi>[];
191+
192+
if (superClass != null && !superClass!.isProxyApi) {
193+
throw ArgumentError(
194+
'Could not find a ProxyApi for super class: ${superClass!.baseName}',
195+
);
196+
}
197+
198+
AstProxyApi? currentProxyApi = superClass?.associatedProxyApi;
199+
while (currentProxyApi != null) {
200+
if (superClassChain.contains(currentProxyApi)) {
201+
throw ArgumentError(
202+
'Loop found when processing super classes for a ProxyApi: '
203+
'$name, ${superClassChain.map((AstProxyApi api) => api.name)}',
204+
);
205+
}
206+
207+
superClassChain.add(currentProxyApi);
208+
209+
if (currentProxyApi.superClass != null &&
210+
!currentProxyApi.superClass!.isProxyApi) {
211+
throw ArgumentError(
212+
'Could not find a ProxyApi for super class: '
213+
'${currentProxyApi.superClass!.baseName}',
214+
);
215+
}
216+
217+
currentProxyApi = currentProxyApi.superClass?.associatedProxyApi;
218+
}
219+
220+
return superClassChain;
221+
}
222+
223+
/// All ProxyApis this API `implements` and all the interfaces those APIs
224+
/// `implements`.
225+
Iterable<AstProxyApi> apisOfInterfaces() => _recursiveFindAllInterfaceApis();
226+
227+
/// All methods inherited from interfaces and the interfaces of interfaces.
228+
Iterable<Method> flutterMethodsFromInterfaces() sync* {
229+
for (final AstProxyApi proxyApi in apisOfInterfaces()) {
230+
yield* proxyApi.methods;
231+
}
232+
}
233+
234+
/// A list of Flutter methods inherited from the ProxyApi that this ProxyApi
235+
/// `extends`.
236+
///
237+
/// This also recursively checks the ProxyApi that the super class `extends`
238+
/// and so on.
239+
///
240+
/// This also includes methods that super classes inherited from interfaces
241+
/// with `implements`.
242+
Iterable<Method> flutterMethodsFromSuperClasses() sync* {
243+
for (final AstProxyApi proxyApi in allSuperClasses().toList().reversed) {
244+
yield* proxyApi.flutterMethods;
245+
}
246+
if (superClass != null) {
247+
final Set<AstProxyApi> interfaceApisFromSuperClasses =
248+
superClass!.associatedProxyApi!._recursiveFindAllInterfaceApis();
249+
for (final AstProxyApi proxyApi in interfaceApisFromSuperClasses) {
250+
yield* proxyApi.methods;
251+
}
252+
}
253+
}
254+
255+
/// Whether the api has a method that callbacks to Dart to add a new instance
256+
/// to the InstanceManager.
257+
///
258+
/// This is possible as long as no callback methods are required to
259+
/// instantiate the class.
260+
bool hasCallbackConstructor() {
261+
return flutterMethods
262+
.followedBy(flutterMethodsFromSuperClasses())
263+
.followedBy(flutterMethodsFromInterfaces())
264+
.every((Method method) => !method.isRequired);
265+
}
266+
267+
// Recursively search for all the interfaces apis from a list of names of
268+
// interfaces.
269+
//
270+
// This method assumes that all interfaces are ProxyApis and an api doesn't
271+
// contains itself as an interface. Otherwise, throws an [ArgumentError].
272+
Set<AstProxyApi> _recursiveFindAllInterfaceApis([
273+
Set<AstProxyApi> seenApis = const <AstProxyApi>{},
274+
]) {
275+
final Set<AstProxyApi> allInterfaces = <AstProxyApi>{};
276+
277+
allInterfaces.addAll(
278+
interfaces.map(
279+
(TypeDeclaration type) {
280+
if (!type.isProxyApi) {
281+
throw ArgumentError(
282+
'Could not find a valid ProxyApi for an interface: $type',
283+
);
284+
} else if (seenApis.contains(type.associatedProxyApi)) {
285+
throw ArgumentError(
286+
'A ProxyApi cannot be a super class of itself: ${type.baseName}',
287+
);
288+
}
289+
return type.associatedProxyApi!;
290+
},
291+
),
292+
);
293+
294+
// Adds the current api since it would be invalid for it to be an interface
295+
// of itself.
296+
final Set<AstProxyApi> newSeenApis = <AstProxyApi>{...seenApis, this};
297+
298+
for (final AstProxyApi interfaceApi in <AstProxyApi>{...allInterfaces}) {
299+
allInterfaces.addAll(
300+
interfaceApi._recursiveFindAllInterfaceApis(newSeenApis),
301+
);
302+
}
303+
304+
return allInterfaces;
305+
}
306+
180307
@override
181308
String toString() {
182309
return '(ProxyApi name:$name methods:$methods field:$fields '

0 commit comments

Comments
 (0)