@@ -177,6 +177,133 @@ class AstProxyApi extends Api {
177
177
(ApiField field) => ! field.isAttached,
178
178
);
179
179
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
+
180
307
@override
181
308
String toString () {
182
309
return '(ProxyApi name:$name methods:$methods field:$fields '
0 commit comments