@@ -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