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