• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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