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