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