1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.doclets.toolkit; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.SortedSet; 36 import java.util.TreeSet; 37 38 import javax.lang.model.element.AnnotationMirror; 39 import javax.lang.model.element.Element; 40 import javax.lang.model.element.ExecutableElement; 41 import javax.lang.model.element.ModuleElement; 42 import javax.lang.model.element.PackageElement; 43 import javax.lang.model.element.TypeElement; 44 import javax.lang.model.element.VariableElement; 45 import javax.lang.model.type.TypeMirror; 46 import javax.lang.model.util.Elements; 47 import javax.tools.FileObject; 48 import javax.tools.JavaFileManager.Location; 49 50 import com.sun.source.tree.CompilationUnitTree; 51 import com.sun.source.util.JavacTask; 52 import com.sun.source.util.TreePath; 53 import com.sun.tools.doclint.DocLint; 54 import com.sun.tools.javac.api.BasicJavacTask; 55 import com.sun.tools.javac.code.Attribute; 56 import com.sun.tools.javac.code.Flags; 57 import com.sun.tools.javac.code.Scope; 58 import com.sun.tools.javac.code.Symbol; 59 import com.sun.tools.javac.code.Symbol.ClassSymbol; 60 import com.sun.tools.javac.code.Symbol.MethodSymbol; 61 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 62 import com.sun.tools.javac.code.Symbol.PackageSymbol; 63 import com.sun.tools.javac.code.Symbol.VarSymbol; 64 import com.sun.tools.javac.code.TypeTag; 65 import com.sun.tools.javac.comp.AttrContext; 66 import com.sun.tools.javac.comp.Env; 67 import com.sun.tools.javac.model.JavacElements; 68 import com.sun.tools.javac.model.JavacTypes; 69 import com.sun.tools.javac.util.Names; 70 71 import jdk.javadoc.internal.doclets.toolkit.util.Utils; 72 import jdk.javadoc.internal.tool.ToolEnvironment; 73 import jdk.javadoc.internal.tool.DocEnvImpl; 74 75 import static com.sun.tools.javac.code.Kinds.Kind.*; 76 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 77 78 import static javax.lang.model.element.ElementKind.*; 79 80 /** 81 * A quarantine class to isolate all the workarounds and bridges to 82 * a locality. This class should eventually disappear once all the 83 * standard APIs support the needed interfaces. 84 * 85 * 86 * <p><b>This is NOT part of any supported API. 87 * If you write code that depends on this, you do so at your own risk. 88 * This code and its internal interfaces are subject to change or 89 * deletion without notice.</b> 90 */ 91 public class WorkArounds { 92 93 public final BaseConfiguration configuration; 94 public final ToolEnvironment toolEnv; 95 public final Utils utils; 96 97 private DocLint doclint; 98 99 public WorkArounds(BaseConfiguration configuration) { 100 this.configuration = configuration; 101 this.utils = this.configuration.utils; 102 this.toolEnv = ((DocEnvImpl)this.configuration.docEnv).toolEnv; 103 } 104 105 Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>(); 106 // TODO: fix this up correctly 107 public void runDocLint(TreePath path) { 108 CompilationUnitTree unit = path.getCompilationUnit(); 109 if (doclint != null && shouldCheck.computeIfAbsent(unit, doclint::shouldCheck)) { 110 doclint.scan(path); 111 } 112 } 113 114 // TODO: fix this up correctly 115 public void initDocLint(Collection<String> opts, Collection<String> customTagNames, String htmlVersion) { 116 ArrayList<String> doclintOpts = new ArrayList<>(); 117 boolean msgOptionSeen = false; 118 119 for (String opt : opts) { 120 if (opt.startsWith(DocLint.XMSGS_OPTION)) { 121 if (opt.equals(DocLint.XMSGS_CUSTOM_PREFIX + "none")) 122 return; 123 msgOptionSeen = true; 124 } 125 doclintOpts.add(opt); 126 } 127 128 if (!msgOptionSeen) { 129 doclintOpts.add(DocLint.XMSGS_OPTION); 130 } 131 132 String sep = ""; 133 StringBuilder customTags = new StringBuilder(); 134 for (String customTag : customTagNames) { 135 customTags.append(sep); 136 customTags.append(customTag); 137 sep = DocLint.SEPARATOR; 138 } 139 doclintOpts.add(DocLint.XCUSTOM_TAGS_PREFIX + customTags.toString()); 140 doclintOpts.add(DocLint.XHTML_VERSION_PREFIX + htmlVersion); 141 142 JavacTask t = BasicJavacTask.instance(toolEnv.context); 143 doclint = new DocLint(); 144 // standard doclet normally generates H1, H2 145 doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2"); 146 doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false); 147 } 148 149 // TODO: fix this up correctly 150 public boolean haveDocLint() { 151 return (doclint == null); 152 } 153 154 // TODO: jx.l.m directSuperTypes don't work for things like Enum, 155 // so we use javac directly, investigate why jx.l.m is not cutting it. 156 public List<TypeMirror> interfaceTypesOf(TypeMirror type) { 157 com.sun.tools.javac.util.List<com.sun.tools.javac.code.Type> interfaces = 158 ((DocEnvImpl)configuration.docEnv).toolEnv.getTypes().interfaces((com.sun.tools.javac.code.Type)type); 159 if (interfaces.isEmpty()) { 160 return Collections.emptyList(); 161 } 162 List<TypeMirror> list = new ArrayList<>(interfaces.size()); 163 for (com.sun.tools.javac.code.Type t : interfaces) { 164 list.add((TypeMirror)t); 165 } 166 return list; 167 } 168 169 /* 170 * TODO: This method exists because of a bug in javac which does not 171 * handle "@deprecated tag in package-info.java", when this issue 172 * is fixed this method and its uses must be jettisoned. 173 */ 174 public boolean isDeprecated0(Element e) { 175 if (!utils.getDeprecatedTrees(e).isEmpty()) { 176 return true; 177 } 178 JavacTypes jctypes = ((DocEnvImpl)configuration.docEnv).toolEnv.typeutils; 179 TypeMirror deprecatedType = utils.getDeprecatedType(); 180 for (AnnotationMirror anno : e.getAnnotationMirrors()) { 181 if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), deprecatedType)) 182 return true; 183 } 184 return false; 185 } 186 187 // TODO: fix jx.l.m add this method. 188 public boolean isSynthesized(AnnotationMirror aDesc) { 189 return ((Attribute)aDesc).isSynthesized(); 190 } 191 192 // TODO: fix the caller 193 public Object getConstValue(VariableElement ve) { 194 return ((VarSymbol)ve).getConstValue(); 195 } 196 197 // TODO: DocTrees: Trees.getPath(Element e) is slow a factor 4-5 times. 198 public Map<Element, TreePath> getElementToTreePath() { 199 return toolEnv.elementToTreePath; 200 } 201 202 // TODO: we need ElementUtils.getPackage to cope with input strings 203 // to return the proper unnamedPackage for all supported releases. 204 PackageElement getUnnamedPackage() { 205 return (toolEnv.source.allowModules()) 206 ? toolEnv.syms.unnamedModule.unnamedPackage 207 : toolEnv.syms.noModule.unnamedPackage; 208 } 209 210 // TODO: implement in either jx.l.m API (preferred) or DocletEnvironment. 211 FileObject getJavaFileObject(PackageElement packageElement) { 212 return ((PackageSymbol)packageElement).sourcefile; 213 } 214 215 // TODO: needs to ported to jx.l.m. 216 public TypeElement searchClass(TypeElement klass, String className) { 217 // search by qualified name first 218 TypeElement te = configuration.docEnv.getElementUtils().getTypeElement(className); 219 if (te != null) { 220 return te; 221 } 222 223 // search inner classes 224 for (TypeElement ite : utils.getClasses(klass)) { 225 TypeElement innerClass = searchClass(ite, className); 226 if (innerClass != null) { 227 return innerClass; 228 } 229 } 230 231 // check in this package 232 te = utils.findClassInPackageElement(utils.containingPackage(klass), className); 233 if (te != null) { 234 return te; 235 } 236 237 ClassSymbol tsym = (ClassSymbol)klass; 238 // make sure that this symbol has been completed 239 // TODO: do we need this anymore ? 240 if (tsym.completer != null) { 241 tsym.complete(); 242 } 243 244 // search imports 245 if (tsym.sourcefile != null) { 246 247 //### This information is available only for source classes. 248 Env<AttrContext> compenv = toolEnv.getEnv(tsym); 249 if (compenv == null) { 250 return null; 251 } 252 Names names = tsym.name.table.names; 253 Scope s = compenv.toplevel.namedImportScope; 254 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 255 if (sym.kind == TYP) { 256 return (TypeElement)sym; 257 } 258 } 259 260 s = compenv.toplevel.starImportScope; 261 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 262 if (sym.kind == TYP) { 263 return (TypeElement)sym; 264 } 265 } 266 } 267 268 return null; // not found 269 } 270 271 // TODO: need to re-implement this using j.l.m. correctly!, this has 272 // implications on testInterface, the note here is that javac's supertype 273 // does the right thing returning Parameters in scope. 274 /** 275 * Return the type containing the method that this method overrides. 276 * It may be a <code>TypeElement</code> or a <code>TypeParameterElement</code>. 277 * @param method target 278 * @return a type 279 */ 280 public TypeMirror overriddenType(ExecutableElement method) { 281 if (utils.isStatic(method)) { 282 return null; 283 } 284 MethodSymbol sym = (MethodSymbol)method; 285 ClassSymbol origin = (ClassSymbol) sym.owner; 286 for (com.sun.tools.javac.code.Type t = toolEnv.getTypes().supertype(origin.type); 287 t.hasTag(TypeTag.CLASS); 288 t = toolEnv.getTypes().supertype(t)) { 289 ClassSymbol c = (ClassSymbol) t.tsym; 290 for (com.sun.tools.javac.code.Symbol sym2 : c.members().getSymbolsByName(sym.name)) { 291 if (sym.overrides(sym2, origin, toolEnv.getTypes(), true)) { 292 // Ignore those methods that may be a simple overridden 293 // and allow the real API method to be found. 294 if (sym2.type.hasTag(TypeTag.METHOD) && 295 utils.isSimpleOverride((MethodSymbol)sym2)) { 296 continue; 297 } 298 return t; 299 } 300 } 301 } 302 return null; 303 } 304 305 // TODO: the method jx.l.m.Elements::overrides does not check 306 // the return type, see JDK-8174840 until that is resolved, 307 // use a copy of the same method, with a return type check. 308 309 // Note: the rider.overrides call in this method *must* be consistent 310 // with the call in overrideType(....), the method above. 311 public boolean overrides(ExecutableElement e1, ExecutableElement e2, TypeElement cls) { 312 MethodSymbol rider = (MethodSymbol)e1; 313 MethodSymbol ridee = (MethodSymbol)e2; 314 ClassSymbol origin = (ClassSymbol)cls; 315 316 return rider.name == ridee.name && 317 318 // not reflexive as per JLS 319 rider != ridee && 320 321 // we don't care if ridee is static, though that wouldn't 322 // compile 323 !rider.isStatic() && 324 325 // Symbol.overrides assumes the following 326 ridee.isMemberOf(origin, toolEnv.getTypes()) && 327 328 // check access, signatures and check return types 329 rider.overrides(ridee, origin, toolEnv.getTypes(), true); 330 } 331 332 // TODO: jx.l.m ? 333 public Location getLocationForModule(ModuleElement mdle) { 334 ModuleSymbol msym = (ModuleSymbol)mdle; 335 return msym.sourceLocation != null 336 ? msym.sourceLocation 337 : msym.classLocation; 338 } 339 340 //------------------Start of Serializable Implementation---------------------// 341 private final static Map<TypeElement, NewSerializedForm> serializedForms = new HashMap<>(); 342 343 public SortedSet<VariableElement> getSerializableFields(Utils utils, TypeElement klass) { 344 NewSerializedForm sf = serializedForms.get(klass); 345 if (sf == null) { 346 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 347 serializedForms.put(klass, sf); 348 } 349 return sf.fields; 350 } 351 352 public SortedSet<ExecutableElement> getSerializationMethods(Utils utils, TypeElement klass) { 353 NewSerializedForm sf = serializedForms.get(klass); 354 if (sf == null) { 355 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 356 serializedForms.put(klass, sf); 357 } 358 return sf.methods; 359 } 360 361 public boolean definesSerializableFields(Utils utils, TypeElement klass) { 362 if (!utils.isSerializable(klass) || utils.isExternalizable(klass)) { 363 return false; 364 } else { 365 NewSerializedForm sf = serializedForms.get(klass); 366 if (sf == null) { 367 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 368 serializedForms.put(klass, sf); 369 } 370 return sf.definesSerializableFields; 371 } 372 } 373 374 /* TODO we need a clean port to jx.l.m 375 * The serialized form is the specification of a class' serialization state. 376 * <p> 377 * 378 * It consists of the following information: 379 * <p> 380 * 381 * <pre> 382 * 1. Whether class is Serializable or Externalizable. 383 * 2. Javadoc for serialization methods. 384 * a. For Serializable, the optional readObject, writeObject, 385 * readResolve and writeReplace. 386 * serialData tag describes, in prose, the sequence and type 387 * of optional data written by writeObject. 388 * b. For Externalizable, writeExternal and readExternal. 389 * serialData tag describes, in prose, the sequence and type 390 * of optional data written by writeExternal. 391 * 3. Javadoc for serialization data layout. 392 * a. For Serializable, the name,type and description 393 * of each Serializable fields. 394 * b. For Externalizable, data layout is described by 2(b). 395 * </pre> 396 * 397 */ 398 static class NewSerializedForm { 399 400 final Utils utils; 401 final Elements elements; 402 403 final SortedSet<ExecutableElement> methods; 404 405 /* List of FieldDocImpl - Serializable fields. 406 * Singleton list if class defines Serializable fields explicitly. 407 * Otherwise, list of default serializable fields. 408 * 0 length list for Externalizable. 409 */ 410 final SortedSet<VariableElement> fields; 411 412 /* True if class specifies serializable fields explicitly. 413 * using special static member, serialPersistentFields. 414 */ 415 boolean definesSerializableFields = false; 416 417 // Specially treated field/method names defined by Serialization. 418 private static final String SERIALIZABLE_FIELDS = "serialPersistentFields"; 419 private static final String READOBJECT = "readObject"; 420 private static final String WRITEOBJECT = "writeObject"; 421 private static final String READRESOLVE = "readResolve"; 422 private static final String WRITEREPLACE = "writeReplace"; 423 private static final String READOBJECTNODATA = "readObjectNoData"; 424 425 NewSerializedForm(Utils utils, Elements elements, TypeElement te) { 426 this.utils = utils; 427 this.elements = elements; 428 methods = new TreeSet<>(utils.makeGeneralPurposeComparator()); 429 fields = new TreeSet<>(utils.makeGeneralPurposeComparator()); 430 if (utils.isExternalizable(te)) { 431 /* look up required public accessible methods, 432 * writeExternal and readExternal. 433 */ 434 String[] readExternalParamArr = {"java.io.ObjectInput"}; 435 String[] writeExternalParamArr = {"java.io.ObjectOutput"}; 436 437 ExecutableElement md = findMethod(te, "readExternal", Arrays.asList(readExternalParamArr)); 438 if (md != null) { 439 methods.add(md); 440 } 441 md = findMethod((ClassSymbol) te, "writeExternal", Arrays.asList(writeExternalParamArr)); 442 if (md != null) { 443 methods.add(md); 444 } 445 } else if (utils.isSerializable(te)) { 446 VarSymbol dsf = getDefinedSerializableFields((ClassSymbol) te); 447 if (dsf != null) { 448 /* Define serializable fields with array of ObjectStreamField. 449 * Each ObjectStreamField should be documented by a 450 * serialField tag. 451 */ 452 definesSerializableFields = true; 453 fields.add((VariableElement) dsf); 454 } else { 455 456 /* Calculate default Serializable fields as all 457 * non-transient, non-static fields. 458 * Fields should be documented by serial tag. 459 */ 460 computeDefaultSerializableFields((ClassSymbol) te); 461 } 462 463 /* Check for optional customized readObject, writeObject, 464 * readResolve and writeReplace, which can all contain 465 * the serialData tag. */ 466 addMethodIfExist((ClassSymbol) te, READOBJECT); 467 addMethodIfExist((ClassSymbol) te, WRITEOBJECT); 468 addMethodIfExist((ClassSymbol) te, READRESOLVE); 469 addMethodIfExist((ClassSymbol) te, WRITEREPLACE); 470 addMethodIfExist((ClassSymbol) te, READOBJECTNODATA); 471 } 472 } 473 474 private VarSymbol getDefinedSerializableFields(ClassSymbol def) { 475 Names names = def.name.table.names; 476 477 /* SERIALIZABLE_FIELDS can be private, 478 */ 479 for (Symbol sym : def.members().getSymbolsByName(names.fromString(SERIALIZABLE_FIELDS))) { 480 if (sym.kind == VAR) { 481 VarSymbol f = (VarSymbol) sym; 482 if ((f.flags() & Flags.STATIC) != 0 483 && (f.flags() & Flags.PRIVATE) != 0) { 484 return f; 485 } 486 } 487 } 488 return null; 489 } 490 491 /* 492 * Catalog Serializable method if it exists in current ClassSymbol. 493 * Do not look for method in superclasses. 494 * 495 * Serialization requires these methods to be non-static. 496 * 497 * @param method should be an unqualified Serializable method 498 * name either READOBJECT, WRITEOBJECT, READRESOLVE 499 * or WRITEREPLACE. 500 * @param visibility the visibility flag for the given method. 501 */ 502 private void addMethodIfExist(ClassSymbol def, String methodName) { 503 Names names = def.name.table.names; 504 505 for (Symbol sym : def.members().getSymbolsByName(names.fromString(methodName))) { 506 if (sym.kind == MTH) { 507 MethodSymbol md = (MethodSymbol) sym; 508 if ((md.flags() & Flags.STATIC) == 0) { 509 /* 510 * WARNING: not robust if unqualifiedMethodName is overloaded 511 * method. Signature checking could make more robust. 512 * READOBJECT takes a single parameter, java.io.ObjectInputStream. 513 * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream. 514 */ 515 methods.add(md); 516 } 517 } 518 } 519 } 520 521 /* 522 * Compute default Serializable fields from all members of ClassSymbol. 523 * 524 * must walk over all members of ClassSymbol. 525 */ 526 private void computeDefaultSerializableFields(ClassSymbol te) { 527 for (Symbol sym : te.members().getSymbols(NON_RECURSIVE)) { 528 if (sym != null && sym.kind == VAR) { 529 VarSymbol f = (VarSymbol) sym; 530 if ((f.flags() & Flags.STATIC) == 0 531 && (f.flags() & Flags.TRANSIENT) == 0) { 532 //### No modifier filtering applied here. 533 //### Add to beginning. 534 //### Preserve order used by old 'javadoc'. 535 fields.add(f); 536 } 537 } 538 } 539 } 540 541 /** 542 * Find a method in this class scope. Search order: this class, interfaces, superclasses, 543 * outerclasses. Note that this is not necessarily what the compiler would do! 544 * 545 * @param methodName the unqualified name to search for. 546 * @param paramTypes the array of Strings for method parameter types. 547 * @return the first MethodDocImpl which matches, null if not found. 548 */ 549 public ExecutableElement findMethod(TypeElement te, String methodName, 550 List<String> paramTypes) { 551 List<? extends Element> allMembers = this.elements.getAllMembers(te); 552 loop: 553 for (Element e : allMembers) { 554 if (e.getKind() != METHOD) { 555 continue; 556 } 557 ExecutableElement ee = (ExecutableElement) e; 558 if (!ee.getSimpleName().contentEquals(methodName)) { 559 continue; 560 } 561 List<? extends VariableElement> parameters = ee.getParameters(); 562 if (paramTypes.size() != parameters.size()) { 563 continue; 564 } 565 for (int i = 0; i < parameters.size(); i++) { 566 VariableElement ve = parameters.get(i); 567 if (!ve.asType().toString().equals(paramTypes.get(i))) { 568 break loop; 569 } 570 } 571 return ee; 572 } 573 TypeElement encl = utils.getEnclosingTypeElement(te); 574 if (encl == null) { 575 return null; 576 } 577 return findMethod(encl, methodName, paramTypes); 578 } 579 } 580 581 // TODO: we need to eliminate this, as it is hacky. 582 /** 583 * Returns a representation of the package truncated to two levels. 584 * For instance if the given package represents foo.bar.baz will return 585 * a representation of foo.bar 586 * @param pkg the PackageElement 587 * @return an abbreviated PackageElement 588 */ 589 public PackageElement getAbbreviatedPackageElement(PackageElement pkg) { 590 String parsedPackageName = utils.parsePackageName(pkg); 591 ModuleElement encl = (ModuleElement) pkg.getEnclosingElement(); 592 PackageElement abbrevPkg = encl == null 593 ? utils.elementUtils.getPackageElement(parsedPackageName) 594 : ((JavacElements) utils.elementUtils).getPackageElement(encl, parsedPackageName); 595 return abbrevPkg; 596 } 597 }