1 /* 2 * Copyright 2019 Google Inc. All Rights Reserved. 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.google.turbine.processing; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 import static java.util.Objects.requireNonNull; 21 22 import com.google.common.base.Splitter; 23 import com.google.common.base.Supplier; 24 import com.google.common.base.Verify; 25 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableMap; 27 import com.google.common.collect.ImmutableSet; 28 import com.google.turbine.binder.bound.TypeBoundClass; 29 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; 30 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; 31 import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; 32 import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; 33 import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; 34 import com.google.turbine.binder.env.CompoundEnv; 35 import com.google.turbine.binder.env.Env; 36 import com.google.turbine.binder.lookup.LookupKey; 37 import com.google.turbine.binder.lookup.LookupResult; 38 import com.google.turbine.binder.lookup.TopLevelIndex; 39 import com.google.turbine.binder.sym.ClassSymbol; 40 import com.google.turbine.binder.sym.FieldSymbol; 41 import com.google.turbine.binder.sym.MethodSymbol; 42 import com.google.turbine.binder.sym.PackageSymbol; 43 import com.google.turbine.binder.sym.ParamSymbol; 44 import com.google.turbine.binder.sym.RecordComponentSymbol; 45 import com.google.turbine.binder.sym.Symbol; 46 import com.google.turbine.binder.sym.TyVarSymbol; 47 import com.google.turbine.model.TurbineConstantTypeKind; 48 import com.google.turbine.processing.TurbineElement.TurbineExecutableElement; 49 import com.google.turbine.processing.TurbineElement.TurbineFieldElement; 50 import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement; 51 import com.google.turbine.processing.TurbineElement.TurbinePackageElement; 52 import com.google.turbine.processing.TurbineElement.TurbineParameterElement; 53 import com.google.turbine.processing.TurbineElement.TurbineRecordComponentElement; 54 import com.google.turbine.processing.TurbineElement.TurbineTypeElement; 55 import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement; 56 import com.google.turbine.processing.TurbineTypeMirror.TurbineArrayType; 57 import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType; 58 import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType; 59 import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType; 60 import com.google.turbine.processing.TurbineTypeMirror.TurbineIntersectionType; 61 import com.google.turbine.processing.TurbineTypeMirror.TurbineNoType; 62 import com.google.turbine.processing.TurbineTypeMirror.TurbineNullType; 63 import com.google.turbine.processing.TurbineTypeMirror.TurbinePackageType; 64 import com.google.turbine.processing.TurbineTypeMirror.TurbinePrimitiveType; 65 import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable; 66 import com.google.turbine.processing.TurbineTypeMirror.TurbineVoidType; 67 import com.google.turbine.processing.TurbineTypeMirror.TurbineWildcardType; 68 import com.google.turbine.tree.Tree.Ident; 69 import com.google.turbine.type.Type; 70 import com.google.turbine.type.Type.ArrayTy; 71 import com.google.turbine.type.Type.ClassTy; 72 import com.google.turbine.type.Type.ErrorTy; 73 import com.google.turbine.type.Type.IntersectionTy; 74 import com.google.turbine.type.Type.MethodTy; 75 import com.google.turbine.type.Type.PrimTy; 76 import com.google.turbine.type.Type.TyVar; 77 import com.google.turbine.type.Type.WildTy; 78 import java.util.HashMap; 79 import java.util.Map; 80 import java.util.concurrent.atomic.AtomicInteger; 81 import javax.lang.model.element.Element; 82 import javax.lang.model.element.VariableElement; 83 import javax.lang.model.type.NoType; 84 import javax.lang.model.type.NullType; 85 import javax.lang.model.type.TypeMirror; 86 87 /** 88 * A factoy for turbine's implementations of {@link Element} and {@link TypeMirror}. 89 * 90 * <p>The model provided by those interfaces contains cycles between types and elements, e.g. {@link 91 * Element#asType} and {@link javax.lang.model.type.DeclaredType#asElement}. Turbine's internal 92 * model uses an immutable representation of classes and types which cannot represent cycles 93 * directly. Instead, the implementations in {@link TurbineElement} and {@link TurbineTypeMirror} 94 * maintain a reference to this class, and use it to lazily construct edges in the type and element 95 * graph. 96 */ 97 public class ModelFactory { 98 99 public Env<ClassSymbol, ? extends TypeBoundClass> env; 100 101 private final AtomicInteger round = new AtomicInteger(0); 102 round(CompoundEnv<ClassSymbol, TypeBoundClass> env, TopLevelIndex tli)103 public void round(CompoundEnv<ClassSymbol, TypeBoundClass> env, TopLevelIndex tli) { 104 this.env = env; 105 this.tli = tli; 106 round.getAndIncrement(); 107 cha.round(env); 108 } 109 110 private final HashMap<Type, TurbineTypeMirror> typeCache = new HashMap<>(); 111 112 private final Map<FieldSymbol, TurbineFieldElement> fieldCache = new HashMap<>(); 113 private final Map<MethodSymbol, TurbineExecutableElement> methodCache = new HashMap<>(); 114 private final Map<ClassSymbol, TurbineTypeElement> classCache = new HashMap<>(); 115 private final Map<ParamSymbol, TurbineParameterElement> paramCache = new HashMap<>(); 116 private final Map<RecordComponentSymbol, TurbineRecordComponentElement> recordComponentCache = 117 new HashMap<>(); 118 private final Map<TyVarSymbol, TurbineTypeParameterElement> tyParamCache = new HashMap<>(); 119 private final Map<PackageSymbol, TurbinePackageElement> packageCache = new HashMap<>(); 120 121 private final HashMap<CharSequence, ClassSymbol> inferSymbolCache = new HashMap<>(); 122 123 private final ClassHierarchy cha; 124 private final ClassLoader processorLoader; 125 126 private TopLevelIndex tli; 127 ModelFactory( Env<ClassSymbol, ? extends TypeBoundClass> env, ClassLoader processorLoader, TopLevelIndex tli)128 public ModelFactory( 129 Env<ClassSymbol, ? extends TypeBoundClass> env, 130 ClassLoader processorLoader, 131 TopLevelIndex tli) { 132 this.env = requireNonNull(env); 133 this.cha = new ClassHierarchy(env); 134 this.processorLoader = requireNonNull(processorLoader); 135 this.tli = requireNonNull(tli); 136 } 137 asTypeMirror(Type type)138 TypeMirror asTypeMirror(Type type) { 139 return typeCache.computeIfAbsent(type, this::createTypeMirror); 140 } 141 142 /** 143 * Returns a supplier that memoizes the result of the input supplier. 144 * 145 * <p>It ensures that the results are invalidated after each annotation processing round, to 146 * support computations that depend on information in the current round and which might change in 147 * future, e.g. as additional types are generated. 148 */ memoize(Supplier<T> s)149 <T> Supplier<T> memoize(Supplier<T> s) { 150 return new Supplier<T>() { 151 T v; 152 int initializedInRound = -1; 153 154 @Override 155 public T get() { 156 int r = round.get(); 157 if (initializedInRound != r) { 158 v = s.get(); 159 initializedInRound = r; 160 } 161 return v; 162 } 163 }; 164 } 165 166 /** Creates a {@link TurbineTypeMirror} backed by a {@link Type}. */ createTypeMirror(Type type)167 private TurbineTypeMirror createTypeMirror(Type type) { 168 switch (type.tyKind()) { 169 case PRIM_TY: 170 if (((PrimTy) type).primkind() == TurbineConstantTypeKind.STRING) { 171 return new TurbineDeclaredType(this, ClassTy.STRING); 172 } 173 return new TurbinePrimitiveType(this, (PrimTy) type); 174 case CLASS_TY: 175 return new TurbineDeclaredType(this, (ClassTy) type); 176 case ARRAY_TY: 177 return new TurbineArrayType(this, (ArrayTy) type); 178 case VOID_TY: 179 return new TurbineVoidType(this); 180 case WILD_TY: 181 return new TurbineWildcardType(this, (WildTy) type); 182 case TY_VAR: 183 return new TurbineTypeVariable(this, (TyVar) type); 184 case INTERSECTION_TY: 185 IntersectionTy intersectionTy = (IntersectionTy) type; 186 switch (intersectionTy.bounds().size()) { 187 case 0: 188 return createTypeMirror(ClassTy.OBJECT); 189 case 1: 190 return createTypeMirror(getOnlyElement(intersectionTy.bounds())); 191 default: 192 return new TurbineIntersectionType(this, intersectionTy); 193 } 194 case NONE_TY: 195 return new TurbineNoType(this); 196 case METHOD_TY: 197 return new TurbineExecutableType(this, (MethodTy) type); 198 case ERROR_TY: 199 return new TurbineErrorType(this, (ErrorTy) type); 200 } 201 throw new AssertionError(type.tyKind()); 202 } 203 204 /** Creates a list of {@link TurbineTypeMirror}s backed by the given {@link Type}s. */ asTypeMirrors(Iterable<? extends Type> types)205 ImmutableList<TypeMirror> asTypeMirrors(Iterable<? extends Type> types) { 206 ImmutableList.Builder<TypeMirror> result = ImmutableList.builder(); 207 for (Type type : types) { 208 result.add(asTypeMirror(type)); 209 } 210 return result.build(); 211 } 212 noType()213 NoType noType() { 214 return (NoType) asTypeMirror(Type.NONE); 215 } 216 packageType(PackageSymbol symbol)217 NoType packageType(PackageSymbol symbol) { 218 return new TurbinePackageType(this, symbol); 219 } 220 nullType()221 public NullType nullType() { 222 return new TurbineNullType(this); 223 } 224 225 /** Creates an {@link Element} backed by the given {@link Symbol}. */ element(Symbol symbol)226 Element element(Symbol symbol) { 227 switch (symbol.symKind()) { 228 case CLASS: 229 return typeElement((ClassSymbol) symbol); 230 case TY_PARAM: 231 return typeParameterElement((TyVarSymbol) symbol); 232 case METHOD: 233 return executableElement((MethodSymbol) symbol); 234 case FIELD: 235 return fieldElement((FieldSymbol) symbol); 236 case PARAMETER: 237 return parameterElement((ParamSymbol) symbol); 238 case RECORD_COMPONENT: 239 return recordComponentElement((RecordComponentSymbol) symbol); 240 case PACKAGE: 241 return packageElement((PackageSymbol) symbol); 242 case MODULE: 243 break; 244 } 245 throw new AssertionError(symbol.symKind()); 246 } 247 noElement(String name)248 Element noElement(String name) { 249 return new TurbineNoTypeElement(this, name); 250 } 251 fieldElement(FieldSymbol symbol)252 TurbineFieldElement fieldElement(FieldSymbol symbol) { 253 return fieldCache.computeIfAbsent(symbol, k -> new TurbineFieldElement(this, symbol)); 254 } 255 executableElement(MethodSymbol symbol)256 TurbineExecutableElement executableElement(MethodSymbol symbol) { 257 return methodCache.computeIfAbsent(symbol, k -> new TurbineExecutableElement(this, symbol)); 258 } 259 typeElement(ClassSymbol symbol)260 public TurbineTypeElement typeElement(ClassSymbol symbol) { 261 Verify.verify(!symbol.simpleName().equals("package-info"), "%s", symbol); 262 return classCache.computeIfAbsent(symbol, k -> new TurbineTypeElement(this, symbol)); 263 } 264 packageElement(PackageSymbol symbol)265 TurbinePackageElement packageElement(PackageSymbol symbol) { 266 return packageCache.computeIfAbsent(symbol, k -> new TurbinePackageElement(this, symbol)); 267 } 268 parameterElement(ParamSymbol sym)269 VariableElement parameterElement(ParamSymbol sym) { 270 return paramCache.computeIfAbsent(sym, k -> new TurbineParameterElement(this, sym)); 271 } 272 recordComponentElement(RecordComponentSymbol sym)273 VariableElement recordComponentElement(RecordComponentSymbol sym) { 274 return recordComponentCache.computeIfAbsent( 275 sym, k -> new TurbineRecordComponentElement(this, sym)); 276 } 277 typeParameterElement(TyVarSymbol sym)278 TurbineTypeParameterElement typeParameterElement(TyVarSymbol sym) { 279 return tyParamCache.computeIfAbsent(sym, k -> new TurbineTypeParameterElement(this, sym)); 280 } 281 elements(ImmutableSet<? extends Symbol> symbols)282 ImmutableSet<Element> elements(ImmutableSet<? extends Symbol> symbols) { 283 ImmutableSet.Builder<Element> result = ImmutableSet.builder(); 284 for (Symbol symbol : symbols) { 285 result.add(element(symbol)); 286 } 287 return result.build(); 288 } 289 inferSymbol(CharSequence name)290 public ClassSymbol inferSymbol(CharSequence name) { 291 return inferSymbolCache.computeIfAbsent(name, key -> inferSymbolImpl(name)); 292 } 293 inferSymbolImpl(CharSequence name)294 private ClassSymbol inferSymbolImpl(CharSequence name) { 295 LookupResult lookup = tli.scope().lookup(new LookupKey(asIdents(name))); 296 if (lookup == null) { 297 return null; 298 } 299 ClassSymbol sym = (ClassSymbol) lookup.sym(); 300 for (Ident bit : lookup.remaining()) { 301 sym = getSymbol(sym).children().get(bit.value()); 302 if (sym == null) { 303 return null; 304 } 305 } 306 return sym; 307 } 308 asIdents(CharSequence name)309 private static ImmutableList<Ident> asIdents(CharSequence name) { 310 ImmutableList.Builder<Ident> result = ImmutableList.builder(); 311 for (String bit : Splitter.on('.').split(name)) { 312 result.add(new Ident(-1, bit)); 313 } 314 return result.build(); 315 } 316 317 /** 318 * Returns the {@link TypeBoundClass} for the given {@link ClassSymbol} from the current 319 * environment. 320 */ getSymbol(ClassSymbol sym)321 TypeBoundClass getSymbol(ClassSymbol sym) { 322 return env.get(sym); 323 } 324 getMethodInfo(MethodSymbol method)325 MethodInfo getMethodInfo(MethodSymbol method) { 326 TypeBoundClass info = getSymbol(method.owner()); 327 for (MethodInfo m : info.methods()) { 328 if (m.sym().equals(method)) { 329 return m; 330 } 331 } 332 return null; 333 } 334 getParamInfo(ParamSymbol sym)335 ParamInfo getParamInfo(ParamSymbol sym) { 336 MethodInfo info = getMethodInfo(sym.owner()); 337 for (ParamInfo p : info.parameters()) { 338 if (p.sym().equals(sym)) { 339 return p; 340 } 341 } 342 return null; 343 } 344 getRecordComponentInfo(RecordComponentSymbol sym)345 RecordComponentInfo getRecordComponentInfo(RecordComponentSymbol sym) { 346 TypeBoundClass info = getSymbol(sym.owner()); 347 for (RecordComponentInfo component : info.components()) { 348 if (component.sym().equals(sym)) { 349 return component; 350 } 351 } 352 return null; 353 } 354 getFieldInfo(FieldSymbol symbol)355 FieldInfo getFieldInfo(FieldSymbol symbol) { 356 TypeBoundClass info = getSymbol(symbol.owner()); 357 requireNonNull(info, symbol.owner().toString()); 358 for (FieldInfo field : info.fields()) { 359 if (field.sym().equals(symbol)) { 360 return field; 361 } 362 } 363 throw new AssertionError(symbol); 364 } 365 getTyVarInfo(TyVarSymbol tyVar)366 TyVarInfo getTyVarInfo(TyVarSymbol tyVar) { 367 Symbol owner = tyVar.owner(); 368 Verify.verifyNotNull(owner); // TODO(cushon): capture variables 369 ImmutableMap<TyVarSymbol, TyVarInfo> tyParams; 370 switch (owner.symKind()) { 371 case METHOD: 372 tyParams = getMethodInfo((MethodSymbol) owner).tyParams(); 373 break; 374 case CLASS: 375 tyParams = getSymbol((ClassSymbol) owner).typeParameterTypes(); 376 break; 377 default: 378 throw new AssertionError(owner.symKind()); 379 } 380 return tyParams.get(tyVar); 381 } 382 enclosingClass(Symbol sym)383 static ClassSymbol enclosingClass(Symbol sym) { 384 switch (sym.symKind()) { 385 case CLASS: 386 return (ClassSymbol) sym; 387 case TY_PARAM: 388 return enclosingClass(((TyVarSymbol) sym).owner()); 389 case METHOD: 390 return ((MethodSymbol) sym).owner(); 391 case FIELD: 392 return ((FieldSymbol) sym).owner(); 393 case PARAMETER: 394 return ((ParamSymbol) sym).owner().owner(); 395 case RECORD_COMPONENT: 396 return ((RecordComponentSymbol) sym).owner(); 397 case PACKAGE: 398 case MODULE: 399 throw new IllegalArgumentException(sym.toString()); 400 } 401 throw new AssertionError(sym.symKind()); 402 } 403 cha()404 ClassHierarchy cha() { 405 return cha; 406 } 407 processorLoader()408 ClassLoader processorLoader() { 409 return processorLoader; 410 } 411 tli()412 TopLevelIndex tli() { 413 return tli; 414 } 415 } 416