1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.stub 18 19 import com.android.tools.metalava.model.CallableItem 20 import com.android.tools.metalava.model.ClassItem 21 import com.android.tools.metalava.model.Codebase 22 import com.android.tools.metalava.model.ConstructorItem 23 import com.android.tools.metalava.model.FilterPredicate 24 import com.android.tools.metalava.model.PackageList 25 import com.android.tools.metalava.model.VisibilityLevel 26 27 class StubConstructorManager(codebase: Codebase) { 28 29 private val packages: PackageList = codebase.getPackages() 30 31 /** Map from [ClassItem] to [StubConstructors]. */ 32 private val classToStubConstructors = mutableMapOf<ClassItem, StubConstructors>() 33 34 /** 35 * Contains information about constructors needed when generating stubs for a specific class. 36 */ 37 private class StubConstructors( 38 /** 39 * The default constructor to invoke on the class from subclasses. 40 * 41 * Note that in some cases [stubConstructor] may not be in [ClassItem.constructors], e.g. 42 * when we need to create a constructor to match a public parent class with a non-default 43 * constructor and the one in the code is not a match, e.g. is marked `@hide`. 44 * 45 * Is `null` if the class has a default constructor that is accessible. 46 */ 47 val stubConstructor: ConstructorItem?, 48 49 /** 50 * The constructor that constructors in a stub class must delegate to in their `super` call. 51 * 52 * Is `null` if the super class has a default constructor. 53 */ 54 val superConstructor: ConstructorItem?, 55 ) { 56 companion object { 57 val EMPTY = StubConstructors(null, null) 58 } 59 } 60 61 fun addConstructors(filter: FilterPredicate) { 62 // Let's say we have 63 // class GrandParent { public GrandParent(int) {} } 64 // class Parent { Parent(int) {} } 65 // class Child { public Child(int) {} } 66 // 67 // Here Parent's constructor is not public. For normal stub generation we'd end up with 68 // this: 69 // class GrandParent { public GrandParent(int) {} } 70 // class Parent { } 71 // class Child { public Child(int) {} } 72 // 73 // This doesn't compile - Parent can't have a default constructor since there isn't 74 // one for it to invoke on GrandParent. 75 // 76 // we can generate a fake constructor instead, such as 77 // Parent() { super(0); } 78 // 79 // But it's hard to do this lazily; what if we're generating the Child class first? 80 // Therefore, we'll instead walk over the hierarchy and insert these constructors into the 81 // Item hierarchy such that code generation can find them. 82 // 83 // We also need to handle the throws list, so we can't just unconditionally insert package 84 // private constructors 85 86 // Add constructors to the classes by walking up the super hierarchy and recursively add 87 // constructors; we'll do it recursively to make sure that the superclass has had its 88 // constructors initialized first (such that we can match the parameter lists and throws 89 // signatures), and we use the tag fields to avoid looking at all the internal classes more 90 // than once. 91 packages.allClasses().filter { filter.test(it) }.forEach { addConstructors(it, filter) } 92 } 93 94 /** 95 * Handle computing constructor hierarchy. 96 * 97 * We'll be setting several attributes: [StubConstructors.stubConstructor] : The default 98 * constructor to invoke in this class from subclasses. **NOTE**: This constructor may not be 99 * part of the [ClassItem.constructors] list, e.g. for package private default constructors 100 * we've inserted (because there were no public constructors or constructors not using hidden 101 * parameter types.) 102 * 103 * [StubConstructors.superConstructor] : The super constructor to invoke. 104 */ 105 private fun addConstructors( 106 cls: ClassItem, 107 filter: FilterPredicate, 108 ): StubConstructors { 109 110 // Don't add constructors to interfaces, enums, annotations, etc 111 if (!cls.isClass()) { 112 return StubConstructors.EMPTY 113 } 114 115 // What happens if we have 116 // package foo: 117 // public class A { public A(int) } 118 // package bar 119 // public class B extends A { public B(int) } 120 // If we just try inserting package private constructors here things will NOT work: 121 // package foo: 122 // public class A { public A(int); A() {} } 123 // package bar 124 // public class B extends A { public B(int); B() } 125 // because A <() is not accessible from B() -- it's outside the same package. 126 // 127 // So, we'll need to model the real constructors for all the scenarios where that works. 128 // 129 // The remaining challenge is that there will be some gaps: when we don't have a default 130 // constructor, subclass constructors will have to have an explicit super(args) call to pick 131 // the parent constructor to use. And which one? It generally doesn't matter; just pick one, 132 // but unfortunately, the super constructor can throw exceptions, and in that case the 133 // subclass constructor must also throw all those exceptions (you can't surround a super 134 // call with try/catch.) 135 // 136 // Luckily, this does not seem to be an actual problem with any of the source code that 137 // metalava currently processes. If it did become a problem then the solution would be to 138 // pick super constructors with a compatible set of throws. 139 140 // If this class has already been visited then return the StubConstructors that was created. 141 classToStubConstructors[cls]?.let { 142 return it 143 } 144 145 // Remember that we have visited this class so that it is not visited again. This does not 146 // strictly need to be done before visiting the super classes as there should not be cycles 147 // in the class hierarchy. However, if due to some invalid input there is then doing this 148 // here will prevent those cycles from causing a stack overflow. This will be overridden 149 // with the actual constructors below. 150 classToStubConstructors[cls] = StubConstructors.EMPTY 151 152 // First handle its super class hierarchy to make sure that we've already constructed super 153 // classes. 154 val superClass = cls.filteredSuperclass(filter) 155 val superClassConstructors = superClass?.let { addConstructors(it, filter) } 156 157 val superDefaultConstructor = superClassConstructors?.stubConstructor 158 159 // Find constructor subclasses should delegate to, creating one if necessary. If the stub 160 // will contain a no-args constructor then that is represented as `null` to allow it to be 161 // optimized below. 162 val filteredConstructors = cls.filteredConstructors(filter).toList() 163 val stubConstructor = 164 if (filteredConstructors.isNotEmpty()) { 165 // Pick the best constructor. If that is a no-args constructor then represent that 166 // as `null`. 167 pickBest(filteredConstructors).takeUnless { it.parameters().isEmpty() } 168 } else { 169 // No accessible constructors are available (not even a default implicit 170 // constructor) so a package private constructor is needed. Technically, this will 171 // result in the stub class having a constructor that isn't available at runtime, 172 // but creating subclasses in API packages is not supported. 173 cls.createDefaultConstructor(VisibilityLevel.PACKAGE_PRIVATE) 174 } 175 176 // If neither the constructors in this class nor its subclasses need to add a `super(...)` 177 // call then use a shared object. 178 if (stubConstructor == null && superDefaultConstructor == null) { 179 return StubConstructors.EMPTY 180 } 181 182 return StubConstructors( 183 stubConstructor = stubConstructor, 184 superConstructor = superDefaultConstructor, 185 ) 186 .also { 187 // Save it away for retrieval by subclasses. 188 classToStubConstructors[cls] = it 189 } 190 } 191 192 companion object { 193 /** 194 * Comparator to pick the best [ConstructorItem] to which derived stub classes will 195 * delegate. 196 * 197 * Uses the following rules: 198 * 1. Fewest throwables as they have to be propagated down to constructors that delegate to 199 * it. 200 * 2. Fewest parameters to reduce the size of the `super(...)` call. 201 * 3. Shortest erased parameter types as that should reduce the size of the `super(...)` 202 * call. 203 * 4. Total ordering defined by [CallableItem.comparator] to ensure consistent behavior. 204 * 205 * Returns less than zero if the first [ConstructorItem] passed to `compare(c1, c2)` is the 206 * best option, more if the second [ConstructorItem] is the best option and zero if they are 207 * the same. 208 */ 209 private val bestStubConstructorComparator: Comparator<ConstructorItem> = 210 Comparator.comparingInt<ConstructorItem?>({ it.throwsTypes().size }) 211 .thenComparingInt({ it.parameters().size }) 212 .thenComparingInt({ 213 it.parameters().sumOf { it.type().toErasedTypeString().length } 214 }) 215 .thenComparing(CallableItem.comparator) 216 } 217 218 /** 219 * Pick the best [ConstructorItem] to which derived stub classes will delegate. 220 * 221 * Selects the first [ConstructorItem] in [constructors] which compares less to or equal to all 222 * the other [ConstructorItem]s in the list when compared using [bestStubConstructorComparator]. 223 * That defines a total order so the result is independent of the order of [constructors]. 224 */ 225 private fun pickBest(constructors: List<ConstructorItem>): ConstructorItem { 226 // Try to pick the best constructor to which derived stub classes can delegate. 227 return constructors.reduce { first, second -> 228 val result = bestStubConstructorComparator.compare(first, second) 229 if (result <= 0) first else second 230 } 231 } 232 233 /** 234 * Get the optional synthetic constructor, if created, for [classItem]. 235 * 236 * If a [ClassItem] does not have an accessible constructor then one will be synthesized for use 237 * by subclasses. This method returns that constructor, or `null` if there was no synthetic 238 * constructor. 239 */ 240 fun optionalSyntheticConstructor(classItem: ClassItem): ConstructorItem? { 241 val stubConstructor = classToStubConstructors[classItem]?.stubConstructor ?: return null 242 if (stubConstructor in classItem.constructors()) return null 243 return stubConstructor 244 } 245 246 /** Get the optional super constructor, if needed, for [classItem]. */ 247 fun optionalSuperConstructor(classItem: ClassItem): ConstructorItem? { 248 return classToStubConstructors[classItem]?.superConstructor 249 } 250 } 251