• 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.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