|
1 | 1 | package io.quarkus.qute.deployment;
|
2 | 2 |
|
3 |
| -import java.lang.reflect.Modifier; |
4 | 3 | import java.util.Collection;
|
5 | 4 | import java.util.Collections;
|
6 | 5 | import java.util.HashMap;
|
7 | 6 | import java.util.HashSet;
|
8 | 7 | import java.util.List;
|
9 | 8 | import java.util.Map;
|
| 9 | +import java.util.Objects; |
10 | 10 | import java.util.Set;
|
| 11 | +import java.util.stream.Collectors; |
11 | 12 |
|
12 | 13 | import org.jboss.jandex.ClassInfo;
|
13 | 14 | import org.jboss.jandex.DotName;
|
|
17 | 18 | import org.jboss.jandex.Type;
|
18 | 19 | import org.jboss.jandex.Type.Kind;
|
19 | 20 | import org.jboss.jandex.TypeVariable;
|
| 21 | +import org.jboss.logging.Logger; |
20 | 22 |
|
21 | 23 | import io.quarkus.arc.processor.DotNames;
|
22 | 24 |
|
23 | 25 | public final class Types {
|
24 | 26 |
|
25 | 27 | static final String JAVA_LANG_PREFIX = "java.lang.";
|
26 | 28 |
|
| 29 | + private static final Logger LOG = Logger.getLogger(Types.class); |
| 30 | + |
27 | 31 | static Set<Type> getTypeClosure(ClassInfo classInfo, Map<TypeVariable, Type> resolvedTypeParameters,
|
28 | 32 | IndexView index) {
|
29 | 33 | Set<Type> types = new HashSet<>();
|
@@ -138,58 +142,99 @@ static boolean isAssignableFrom(Type type1, Type type2, IndexView index, Map<Dot
|
138 | 142 |
|
139 | 143 | static class AssignableInfo {
|
140 | 144 |
|
| 145 | + static AssignableInfo from(ClassInfo classInfo, IndexView index) { |
| 146 | + if (classInfo.isInterface()) { |
| 147 | + return new AssignableInfo(null, toNames(index.getAllKnownImplementors(classInfo.name())), |
| 148 | + toNames(index.getAllKnownSubinterfaces(classInfo.name()))); |
| 149 | + } else { |
| 150 | + return new AssignableInfo(toNames(index.getAllKnownSubclasses(classInfo.name())), null, null); |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + private static Set<DotName> toNames(Collection<ClassInfo> classes) { |
| 155 | + return classes.stream().map(ClassInfo::name).collect(Collectors.toSet()); |
| 156 | + } |
| 157 | + |
141 | 158 | final Set<DotName> subclasses;
|
142 | 159 | final Set<DotName> implementors;
|
143 |
| - final Set<DotName> extendingInterfaces; |
| 160 | + final Set<DotName> subInterfaces; |
144 | 161 |
|
145 |
| - public AssignableInfo(Collection<ClassInfo> subclasses, Collection<ClassInfo> implementors, |
146 |
| - Set<DotName> extendingInterfaces) { |
147 |
| - this.subclasses = new HashSet<>(); |
148 |
| - for (ClassInfo subclass : subclasses) { |
149 |
| - this.subclasses.add(subclass.name()); |
150 |
| - } |
151 |
| - this.implementors = new HashSet<>(); |
152 |
| - for (ClassInfo implementor : implementors) { |
153 |
| - this.implementors.add(implementor.name()); |
154 |
| - } |
155 |
| - this.extendingInterfaces = extendingInterfaces; |
| 162 | + AssignableInfo(Set<DotName> subclasses, Set<DotName> implementors, Set<DotName> subInterfaces) { |
| 163 | + this.subclasses = subclasses; |
| 164 | + this.implementors = implementors; |
| 165 | + this.subInterfaces = subInterfaces; |
156 | 166 | }
|
157 | 167 |
|
158 | 168 | boolean isAssignableFrom(DotName clazz) {
|
159 |
| - return subclasses.contains(clazz) || implementors.contains(clazz) || extendingInterfaces.contains(clazz); |
| 169 | + if (subclasses != null && subclasses.contains(clazz)) { |
| 170 | + return true; |
| 171 | + } |
| 172 | + if (implementors != null && implementors.contains(clazz)) { |
| 173 | + return true; |
| 174 | + } |
| 175 | + return subInterfaces != null && subInterfaces.contains(clazz); |
160 | 176 | }
|
161 | 177 |
|
162 | 178 | }
|
163 | 179 |
|
164 |
| - static boolean isAssignableFrom(DotName class1, DotName class2, IndexView index, |
| 180 | + static boolean isAssignableFrom(DotName className1, DotName className2, IndexView index, |
165 | 181 | Map<DotName, AssignableInfo> assignableCache) {
|
166 | 182 | // java.lang.Object is assignable from any type
|
167 |
| - if (class1.equals(DotNames.OBJECT)) { |
| 183 | + if (className1.equals(DotNames.OBJECT)) { |
168 | 184 | return true;
|
169 | 185 | }
|
170 | 186 | // type1 is the same as type2
|
171 |
| - if (class1.equals(class2)) { |
| 187 | + if (className1.equals(className2)) { |
172 | 188 | return true;
|
173 | 189 | }
|
174 |
| - AssignableInfo assignableInfo = assignableCache.get(class1); |
| 190 | + ClassInfo class1 = index.getClassByName(className1); |
| 191 | + AssignableInfo assignableInfo = assignableCache.get(className1); |
175 | 192 | if (assignableInfo == null) {
|
176 |
| - assignableInfo = new AssignableInfo(index.getAllKnownSubclasses(class1), index.getAllKnownImplementors(class1), |
177 |
| - getAllInterfacesExtending(class1, index)); |
178 |
| - assignableCache.put(class1, assignableInfo); |
| 193 | + // No cached info |
| 194 | + assignableInfo = AssignableInfo.from(class1, index); |
| 195 | + assignableCache.put(className1, assignableInfo); |
| 196 | + return assignableInfo.isAssignableFrom(className2); |
| 197 | + } else { |
| 198 | + if (assignableInfo.isAssignableFrom(className2)) { |
| 199 | + return true; |
| 200 | + } |
| 201 | + // Cached info does not match - try to update the assignable info (a computing index is used) |
| 202 | + assignableInfo = AssignableInfo.from(class1, index); |
| 203 | + if (assignableInfo.isAssignableFrom(className2)) { |
| 204 | + // Update the cache |
| 205 | + assignableCache.put(className1, assignableInfo); |
| 206 | + return true; |
| 207 | + } |
179 | 208 | }
|
180 |
| - return assignableInfo.isAssignableFrom(class2); |
| 209 | + return false; |
181 | 210 | }
|
182 | 211 |
|
183 |
| - static void indexHierarchy(ClassInfo classInfo, IndexView index) { |
184 |
| - // Interfaces |
185 |
| - for (DotName interfaceName : classInfo.interfaceNames()) { |
186 |
| - index.getClassByName(interfaceName); |
| 212 | + // This class is not thread-safe |
| 213 | + static class HierarchyIndexer { |
| 214 | + |
| 215 | + final IndexView index; |
| 216 | + final Set<DotName> processed; |
| 217 | + |
| 218 | + public HierarchyIndexer(IndexView index) { |
| 219 | + this.index = Objects.requireNonNull(index); |
| 220 | + this.processed = new HashSet<>(); |
187 | 221 | }
|
188 |
| - // Superclass |
189 |
| - DotName superName = classInfo.superName(); |
190 |
| - if (superName != null && !superName.equals(DotNames.OBJECT)) { |
191 |
| - indexHierarchy(index.getClassByName(superName), index); |
| 222 | + |
| 223 | + void indexHierarchy(ClassInfo classInfo) { |
| 224 | + if (classInfo != null && processed.add(classInfo.name())) { |
| 225 | + LOG.debugf("Index hierarchy of: %s", classInfo); |
| 226 | + // Interfaces |
| 227 | + for (DotName interfaceName : classInfo.interfaceNames()) { |
| 228 | + indexHierarchy(index.getClassByName(interfaceName)); |
| 229 | + } |
| 230 | + // Superclass |
| 231 | + DotName superName = classInfo.superName(); |
| 232 | + if (superName != null && !superName.equals(DotNames.OBJECT)) { |
| 233 | + indexHierarchy(index.getClassByName(superName)); |
| 234 | + } |
| 235 | + } |
192 | 236 | }
|
| 237 | + |
193 | 238 | }
|
194 | 239 |
|
195 | 240 | static Type box(Type type) {
|
@@ -222,19 +267,4 @@ static Type box(Primitive primitive) {
|
222 | 267 | }
|
223 | 268 | }
|
224 | 269 |
|
225 |
| - private static Set<DotName> getAllInterfacesExtending(DotName target, IndexView index) { |
226 |
| - Set<DotName> ret = new HashSet<>(); |
227 |
| - for (ClassInfo clazz : index.getKnownClasses()) { |
228 |
| - if (!Modifier.isInterface(clazz.flags()) |
229 |
| - || clazz.isAnnotation() |
230 |
| - || clazz.isEnum()) { |
231 |
| - continue; |
232 |
| - } |
233 |
| - if (clazz.interfaceNames().contains(target)) { |
234 |
| - ret.add(clazz.name()); |
235 |
| - } |
236 |
| - } |
237 |
| - return ret; |
238 |
| - } |
239 |
| - |
240 | 270 | }
|
0 commit comments