• 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.base.Preconditions.checkState;
20 
21 import com.google.common.base.CharMatcher;
22 import com.google.common.base.Joiner;
23 import com.google.common.base.Splitter;
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.Multimap;
26 import com.google.common.collect.MultimapBuilder;
27 import com.google.turbine.binder.sym.ClassSymbol;
28 import com.google.turbine.binder.sym.FieldSymbol;
29 import com.google.turbine.binder.sym.PackageSymbol;
30 import com.google.turbine.binder.sym.Symbol;
31 import com.google.turbine.model.Const;
32 import com.google.turbine.model.TurbineFlag;
33 import com.google.turbine.model.TurbineVisibility;
34 import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
35 import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
36 import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
37 import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType;
38 import com.google.turbine.type.AnnoInfo;
39 import java.io.PrintWriter;
40 import java.io.Writer;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import javax.lang.model.element.AnnotationMirror;
46 import javax.lang.model.element.AnnotationValue;
47 import javax.lang.model.element.Element;
48 import javax.lang.model.element.ElementKind;
49 import javax.lang.model.element.ExecutableElement;
50 import javax.lang.model.element.Name;
51 import javax.lang.model.element.PackageElement;
52 import javax.lang.model.element.TypeElement;
53 import javax.lang.model.type.DeclaredType;
54 import javax.lang.model.type.TypeMirror;
55 import javax.lang.model.util.Elements;
56 import org.jspecify.annotations.Nullable;
57 
58 /** An implementation of {@link Elements} backed by turbine's {@link Element}. */
59 @SuppressWarnings("nullness") // TODO(cushon): Address nullness diagnostics.
60 public class TurbineElements implements Elements {
61 
62   private final ModelFactory factory;
63   private final TurbineTypes types;
64 
TurbineElements(ModelFactory factory, TurbineTypes types)65   public TurbineElements(ModelFactory factory, TurbineTypes types) {
66     this.factory = factory;
67     this.types = types;
68   }
69 
asSymbol(Element element)70   private static Symbol asSymbol(Element element) {
71     if (!(element instanceof TurbineElement)) {
72       throw new IllegalArgumentException(element.toString());
73     }
74     return ((TurbineElement) element).sym();
75   }
76 
77   @Override
getPackageElement(CharSequence name)78   public PackageElement getPackageElement(CharSequence name) {
79     ImmutableList<String> packageName = ImmutableList.copyOf(Splitter.on('.').split(name));
80     if (factory.tli().lookupPackage(packageName) == null) {
81       return null;
82     }
83     return factory.packageElement(new PackageSymbol(Joiner.on('/').join(packageName)));
84   }
85 
86   @Override
getTypeElement(CharSequence name)87   public TypeElement getTypeElement(CharSequence name) {
88     ClassSymbol sym = factory.inferSymbol(name);
89     if (sym == null) {
90       return null;
91     }
92     if (factory.getSymbol(sym) == null) {
93       return null;
94     }
95     return factory.typeElement(sym);
96   }
97 
98   @Override
getElementValuesWithDefaults( AnnotationMirror a)99   public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
100       AnnotationMirror a) {
101     return ((TurbineAnnotationMirror) a).getElementValuesWithDefaults();
102   }
103 
104   @Override
getDocComment(Element e)105   public String getDocComment(Element e) {
106     if (!(e instanceof TurbineElement)) {
107       throw new IllegalArgumentException(e.toString());
108     }
109     String comment = ((TurbineElement) e).javadoc();
110     if (comment == null) {
111       return null;
112     }
113     StringBuilder sb = new StringBuilder();
114     boolean first = true;
115     for (String line : Splitter.on('\n').split(comment)) {
116       int start = 0;
117       if (!first) {
118         sb.append('\n');
119         while (start < line.length() && CharMatcher.whitespace().matches(line.charAt(start))) {
120           start++;
121         }
122         while (start < line.length() && line.charAt(start) == '*') {
123           start++;
124         }
125       }
126       sb.append(line, start, line.length());
127       first = false;
128     }
129     return sb.toString();
130   }
131 
132   @Override
isDeprecated(Element element)133   public boolean isDeprecated(Element element) {
134     if (!(element instanceof TurbineElement)) {
135       throw new IllegalArgumentException(element.toString());
136     }
137     for (AnnoInfo a : ((TurbineElement) element).annos()) {
138       if (a.sym().equals(ClassSymbol.DEPRECATED)) {
139         return true;
140       }
141     }
142     return false;
143   }
144 
145   @Override
getBinaryName(TypeElement element)146   public Name getBinaryName(TypeElement element) {
147     if (!(element instanceof TurbineTypeElement)) {
148       throw new IllegalArgumentException(element.toString());
149     }
150     return getName(((TurbineTypeElement) element).sym().binaryName().replace('/', '.'));
151   }
152 
153   /**
154    * {@inheritDoc}
155    *
156    * @throws IllegalArgumentException for module elements
157    */
158   @Override
getPackageOf(Element element)159   public PackageElement getPackageOf(Element element) {
160     Symbol sym = asSymbol(element);
161     return factory.packageElement(packageSymbol(sym));
162   }
163 
packageSymbol(Symbol sym)164   private static PackageSymbol packageSymbol(Symbol sym) {
165     if (sym.symKind().equals(Symbol.Kind.PACKAGE)) {
166       return (PackageSymbol) sym;
167     }
168     return ModelFactory.enclosingClass(sym).owner();
169   }
170 
171   @Override
getAllMembers(TypeElement type)172   public List<? extends Element> getAllMembers(TypeElement type) {
173     ClassSymbol s = (ClassSymbol) asSymbol(type);
174     PackageSymbol from = packageSymbol(s);
175 
176     // keep track of processed methods grouped by their names, to handle overrides more efficiently
177     Multimap<String, TurbineExecutableElement> methods =
178         MultimapBuilder.linkedHashKeys().linkedHashSetValues().build();
179 
180     // collect all members of each transitive supertype of the input
181     ImmutableList.Builder<Element> results = ImmutableList.builder();
182     for (ClassSymbol superType : factory.cha().transitiveSupertypes(s)) {
183       // Most of JSR-269 is implemented on top of turbine's model, instead of the Element and
184       // TypeMirror wrappers. We don't do that here because we need most of the Elements returned
185       // by getEnclosedElements anyways, and the work below benefits from some of the caching done
186       // by TurbineElement.
187       for (Element el : factory.typeElement(superType).getEnclosedElements()) {
188         Symbol sym = asSymbol(el);
189         switch (sym.symKind()) {
190           case METHOD:
191             TurbineExecutableElement m = (TurbineExecutableElement) el;
192             if (shouldAdd(s, from, methods, m)) {
193               methods.put(m.info().name(), m);
194               results.add(el);
195             }
196             break;
197           case FIELD:
198             if (shouldAdd(s, from, (TurbineFieldElement) el)) {
199               results.add(el);
200             }
201             break;
202           default:
203             results.add(el);
204         }
205       }
206     }
207     return results.build();
208   }
209 
shouldAdd( ClassSymbol s, PackageSymbol from, Multimap<String, TurbineExecutableElement> methods, TurbineExecutableElement m)210   private boolean shouldAdd(
211       ClassSymbol s,
212       PackageSymbol from,
213       Multimap<String, TurbineExecutableElement> methods,
214       TurbineExecutableElement m) {
215     if (m.sym().owner().equals(s)) {
216       // always include methods (and constructors) declared in the given type
217       return true;
218     }
219     if (m.getKind() == ElementKind.CONSTRUCTOR) {
220       // skip constructors from super-types, because the spec says so
221       return false;
222     }
223     if (!isVisible(from, packageSymbol(m.sym()), TurbineVisibility.fromAccess(m.info().access()))) {
224       // skip invisible methods in supers
225       return false;
226     }
227     // otherwise check if we've seen methods that override, or are overridden by, the
228     // current method
229     Set<TurbineExecutableElement> overrides = new HashSet<>();
230     Set<TurbineExecutableElement> overridden = new HashSet<>();
231     String name = m.info().name();
232     for (TurbineExecutableElement other : methods.get(name)) {
233       if (overrides(m, other, (TypeElement) m.getEnclosingElement())) {
234         overrides.add(other);
235         continue;
236       }
237       if (overrides(other, m, (TypeElement) other.getEnclosingElement())) {
238         overridden.add(other);
239         continue;
240       }
241     }
242     if (!overridden.isEmpty()) {
243       // We've already processed method(s) that override this one; nothing to do here.
244       // If that's true, and we've *also* processed a methods that this one overrides,
245       // something has gone terribly wrong: since overriding is transitive the results
246       // contain a pair of methods that override each other.
247       checkState(overrides.isEmpty());
248       return false;
249     }
250     // Add this method, and remove any methods we've already processed that it overrides.
251     for (TurbineExecutableElement override : overrides) {
252       methods.remove(name, override);
253     }
254     return true;
255   }
256 
shouldAdd(ClassSymbol s, PackageSymbol from, TurbineFieldElement f)257   private static boolean shouldAdd(ClassSymbol s, PackageSymbol from, TurbineFieldElement f) {
258     FieldSymbol sym = f.sym();
259     if (sym.owner().equals(s)) {
260       // always include fields declared in the given type
261       return true;
262     }
263     if (!isVisible(from, packageSymbol(sym), TurbineVisibility.fromAccess(f.info().access()))) {
264       // skip invisible fields in supers
265       return false;
266     }
267     return true;
268   }
269 
270   /**
271    * Returns true if an element with the given {@code visibility} and located in package {@code
272    * from} is visible to elements in package {@code to}.
273    */
isVisible( PackageSymbol from, PackageSymbol to, TurbineVisibility visibility)274   private static boolean isVisible(
275       PackageSymbol from, PackageSymbol to, TurbineVisibility visibility) {
276     switch (visibility) {
277       case PUBLIC:
278       case PROTECTED:
279         break;
280       case PACKAGE:
281         return from.equals(to);
282       case PRIVATE:
283         return false;
284     }
285     return true;
286   }
287 
288   @Override
getAllAnnotationMirrors(Element element)289   public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element element) {
290     return ((TurbineElement) element).getAllAnnotationMirrors();
291   }
292 
293   @Override
hides(Element hider, Element hidden)294   public boolean hides(Element hider, Element hidden) {
295     if (!(hider instanceof TurbineElement)) {
296       throw new IllegalArgumentException(hider.toString());
297     }
298     if (!(hidden instanceof TurbineElement)) {
299       throw new IllegalArgumentException(hidden.toString());
300     }
301     return hides((TurbineElement) hider, (TurbineElement) hidden);
302   }
303 
hides(TurbineElement hider, TurbineElement hidden)304   private boolean hides(TurbineElement hider, TurbineElement hidden) {
305     if (!hider.sym().symKind().equals(hidden.sym().symKind())) {
306       return false;
307     }
308     if (!hider.getSimpleName().equals(hidden.getSimpleName())) {
309       return false;
310     }
311     if (hider.sym().equals(hidden.sym())) {
312       return false;
313     }
314     if (!isVisibleForHiding(hider, hidden)) {
315       return false;
316     }
317     if (hider.sym().symKind().equals(Symbol.Kind.METHOD)) {
318       int access = ((TurbineExecutableElement) hider).info().access();
319       if ((access & TurbineFlag.ACC_STATIC) != TurbineFlag.ACC_STATIC) {
320         return false;
321       }
322       // Static interface methods shouldn't be able to hide static methods in super-interfaces,
323       // but include them anyways for bug-compatibility with javac, see:
324       // https://bugs.openjdk.java.net/browse/JDK-8275746
325       if (!types.isSubsignature(
326           (TurbineExecutableType) hider.asType(), (TurbineExecutableType) hidden.asType())) {
327         return false;
328       }
329     }
330     Element containingHider = containingClass(hider);
331     Element containingHidden = containingClass(hidden);
332     if (containingHider == null || containingHidden == null) {
333       return false;
334     }
335     if (!types.isSubtype(containingHider.asType(), containingHidden.asType())) {
336       return false;
337     }
338     return true;
339   }
340 
containingClass(TurbineElement element)341   private static @Nullable Element containingClass(TurbineElement element) {
342     Element enclosing = element.getEnclosingElement();
343     if (enclosing == null) {
344       return null;
345     }
346     if (!isClassOrInterface(enclosing.getKind())) {
347       // The immediately enclosing element of a field or method is a class. For classes, annotation
348       // processing only deals with top-level and nested (but not local or anonymous) classes,
349       // so the immediately enclosing element is either an enclosing class or a package symbol.
350       return null;
351     }
352     return enclosing;
353   }
354 
isClassOrInterface(ElementKind kind)355   private static boolean isClassOrInterface(ElementKind kind) {
356     return kind.isClass() || kind.isInterface();
357   }
358 
isVisibleForHiding(TurbineElement hider, TurbineElement hidden)359   private static boolean isVisibleForHiding(TurbineElement hider, TurbineElement hidden) {
360     int access;
361     switch (hidden.sym().symKind()) {
362       case CLASS:
363         access = ((TurbineTypeElement) hidden).info().access();
364         break;
365       case FIELD:
366         access = ((TurbineFieldElement) hidden).info().access();
367         break;
368       case METHOD:
369         access = ((TurbineExecutableElement) hidden).info().access();
370         break;
371       default:
372         return false;
373     }
374     return isVisible(
375         packageSymbol(asSymbol(hider)),
376         packageSymbol(asSymbol(hidden)),
377         TurbineVisibility.fromAccess(access));
378   }
379 
380   @Override
overrides( ExecutableElement overrider, ExecutableElement overridden, TypeElement type)381   public boolean overrides(
382       ExecutableElement overrider, ExecutableElement overridden, TypeElement type) {
383     if (!overrider.getSimpleName().contentEquals(overridden.getSimpleName())) {
384       return false;
385     }
386     TypeMirror a = overrider.asType();
387     TypeMirror b = types.asMemberOfInternal((DeclaredType) type.asType(), overridden);
388     if (b == null) {
389       return false;
390     }
391     if (!types.isSubsignature((TurbineExecutableType) a, (TurbineExecutableType) b)) {
392       return false;
393     }
394     return isVisible(
395         packageSymbol(asSymbol(overrider)),
396         packageSymbol(asSymbol(overridden)),
397         TurbineVisibility.fromAccess(((TurbineExecutableElement) overridden).info().access()));
398   }
399 
400   @Override
getConstantExpression(Object value)401   public String getConstantExpression(Object value) {
402     if (value instanceof Byte) {
403       return new Const.ByteValue((Byte) value).toString();
404     }
405     if (value instanceof Long) {
406       return new Const.LongValue((Long) value).toString();
407     }
408     if (value instanceof Float) {
409       return new Const.FloatValue((Float) value).toString();
410     }
411     if (value instanceof Double) {
412       return new Const.DoubleValue((Double) value).toString();
413     }
414     if (value instanceof Short) {
415       // Special-case short for consistency with javac, see:
416       // https://bugs.openjdk.java.net/browse/JDK-8227617
417       return String.format("(short)%d", (Short) value);
418     }
419     if (value instanceof String) {
420       return new Const.StringValue((String) value).toString();
421     }
422     if (value instanceof Character) {
423       return new Const.CharValue((Character) value).toString();
424     }
425     return String.valueOf(value);
426   }
427 
428   @Override
printElements(Writer w, Element... elements)429   public void printElements(Writer w, Element... elements) {
430     PrintWriter pw = new PrintWriter(w, true);
431     for (Element element : elements) {
432       pw.println(element.toString());
433     }
434   }
435 
436   @Override
getName(CharSequence cs)437   public Name getName(CharSequence cs) {
438     return new TurbineName(cs.toString());
439   }
440 
441   @Override
isFunctionalInterface(TypeElement type)442   public boolean isFunctionalInterface(TypeElement type) {
443     throw new UnsupportedOperationException();
444   }
445 }
446