1 /* 2 * Copyright (c) 2015, 2018, 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.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.SortedSet; 35 import java.util.TreeSet; 36 37 import javax.lang.model.element.AnnotationMirror; 38 import javax.lang.model.element.Element; 39 import javax.lang.model.element.ExecutableElement; 40 import javax.lang.model.element.ModuleElement; 41 import javax.lang.model.element.PackageElement; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.element.VariableElement; 44 import javax.lang.model.type.TypeMirror; 45 import javax.lang.model.util.Elements; 46 import javax.tools.FileObject; 47 import javax.tools.JavaFileManager.Location; 48 49 import com.sun.source.tree.CompilationUnitTree; 50 import com.sun.source.util.JavacTask; 51 import com.sun.source.util.TreePath; 52 import com.sun.tools.doclint.DocLint; 53 import com.sun.tools.javac.api.BasicJavacTask; 54 import com.sun.tools.javac.code.Attribute; 55 import com.sun.tools.javac.code.Flags; 56 import com.sun.tools.javac.code.Scope; 57 import com.sun.tools.javac.code.Source.Feature; 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) { 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 + "html5"); 141 142 JavacTask t = BasicJavacTask.instance(toolEnv.context); 143 doclint = new DocLint(); 144 doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false); 145 } 146 147 // TODO: fix this up correctly 148 public boolean haveDocLint() { 149 return (doclint == null); 150 } 151 152 /* 153 * TODO: This method exists because of a bug in javac which does not 154 * handle "@deprecated tag in package-info.java", when this issue 155 * is fixed this method and its uses must be jettisoned. 156 */ 157 public boolean isDeprecated0(Element e) { 158 if (!utils.getDeprecatedTrees(e).isEmpty()) { 159 return true; 160 } 161 JavacTypes jctypes = ((DocEnvImpl)configuration.docEnv).toolEnv.typeutils; 162 TypeMirror deprecatedType = utils.getDeprecatedType(); 163 for (AnnotationMirror anno : e.getAnnotationMirrors()) { 164 if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), deprecatedType)) 165 return true; 166 } 167 return false; 168 } 169 170 // TODO: fix jx.l.m add this method. 171 public boolean isSynthesized(AnnotationMirror aDesc) { 172 return ((Attribute)aDesc).isSynthesized(); 173 } 174 175 // TODO: fix the caller 176 public Object getConstValue(VariableElement ve) { 177 return ((VarSymbol)ve).getConstValue(); 178 } 179 180 // TODO: DocTrees: Trees.getPath(Element e) is slow a factor 4-5 times. 181 public Map<Element, TreePath> getElementToTreePath() { 182 return toolEnv.elementToTreePath; 183 } 184 185 // TODO: we need ElementUtils.getPackage to cope with input strings 186 // to return the proper unnamedPackage for all supported releases. 187 PackageElement getUnnamedPackage() { 188 return (Feature.MODULES.allowedInSource(toolEnv.source)) 189 ? toolEnv.syms.unnamedModule.unnamedPackage 190 : toolEnv.syms.noModule.unnamedPackage; 191 } 192 193 // TODO: implement in either jx.l.m API (preferred) or DocletEnvironment. 194 FileObject getJavaFileObject(PackageElement packageElement) { 195 return ((PackageSymbol)packageElement).sourcefile; 196 } 197 198 // TODO: needs to ported to jx.l.m. 199 public TypeElement searchClass(TypeElement klass, String className) { 200 TypeElement te; 201 202 // search by qualified name in current module first 203 ModuleElement me = utils.containingModule(klass); 204 if (me != null) { 205 te = configuration.docEnv.getElementUtils().getTypeElement(me, className); 206 if (te != null) { 207 return te; 208 } 209 } 210 211 // search inner classes 212 for (TypeElement ite : utils.getClasses(klass)) { 213 TypeElement innerClass = searchClass(ite, className); 214 if (innerClass != null) { 215 return innerClass; 216 } 217 } 218 219 // check in this package 220 te = utils.findClassInPackageElement(utils.containingPackage(klass), className); 221 if (te != null) { 222 return te; 223 } 224 225 ClassSymbol tsym = (ClassSymbol)klass; 226 // make sure that this symbol has been completed 227 // TODO: do we need this anymore ? 228 if (tsym.completer != null) { 229 tsym.complete(); 230 } 231 232 // search imports 233 if (tsym.sourcefile != null) { 234 235 //### This information is available only for source classes. 236 Env<AttrContext> compenv = toolEnv.getEnv(tsym); 237 if (compenv == null) { 238 return null; 239 } 240 Names names = tsym.name.table.names; 241 Scope s = compenv.toplevel.namedImportScope; 242 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 243 if (sym.kind == TYP) { 244 return (TypeElement)sym; 245 } 246 } 247 248 s = compenv.toplevel.starImportScope; 249 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 250 if (sym.kind == TYP) { 251 return (TypeElement)sym; 252 } 253 } 254 } 255 256 // finally, search by qualified name in all modules 257 te = configuration.docEnv.getElementUtils().getTypeElement(className); 258 if (te != null) { 259 return te; 260 } 261 262 return null; // not found 263 } 264 265 // TODO: need to re-implement this using j.l.m. correctly!, this has 266 // implications on testInterface, the note here is that javac's supertype 267 // does the right thing returning Parameters in scope. 268 /** 269 * Return the type containing the method that this method overrides. 270 * It may be a <code>TypeElement</code> or a <code>TypeParameterElement</code>. 271 * @param method target 272 * @return a type 273 */ 274 public TypeMirror overriddenType(ExecutableElement method) { 275 if (utils.isStatic(method)) { 276 return null; 277 } 278 MethodSymbol sym = (MethodSymbol)method; 279 ClassSymbol origin = (ClassSymbol) sym.owner; 280 for (com.sun.tools.javac.code.Type t = toolEnv.getTypes().supertype(origin.type); 281 t.hasTag(TypeTag.CLASS); 282 t = toolEnv.getTypes().supertype(t)) { 283 ClassSymbol c = (ClassSymbol) t.tsym; 284 for (com.sun.tools.javac.code.Symbol sym2 : c.members().getSymbolsByName(sym.name)) { 285 if (sym.overrides(sym2, origin, toolEnv.getTypes(), true)) { 286 // Ignore those methods that may be a simple override 287 // and allow the real API method to be found. 288 if (sym2.type.hasTag(TypeTag.METHOD) && 289 utils.isSimpleOverride((MethodSymbol)sym2)) { 290 continue; 291 } 292 return t; 293 } 294 } 295 } 296 return null; 297 } 298 299 // TODO: the method jx.l.m.Elements::overrides does not check 300 // the return type, see JDK-8174840 until that is resolved, 301 // use a copy of the same method, with a return type check. 302 303 // Note: the rider.overrides call in this method *must* be consistent 304 // with the call in overrideType(....), the method above. 305 public boolean overrides(ExecutableElement e1, ExecutableElement e2, TypeElement cls) { 306 MethodSymbol rider = (MethodSymbol)e1; 307 MethodSymbol ridee = (MethodSymbol)e2; 308 ClassSymbol origin = (ClassSymbol)cls; 309 310 return rider.name == ridee.name && 311 312 // not reflexive as per JLS 313 rider != ridee && 314 315 // we don't care if ridee is static, though that wouldn't 316 // compile 317 !rider.isStatic() && 318 319 // Symbol.overrides assumes the following 320 ridee.isMemberOf(origin, toolEnv.getTypes()) && 321 322 // check access, signatures and check return types 323 rider.overrides(ridee, origin, toolEnv.getTypes(), true); 324 } 325 326 // TODO: jx.l.m ? 327 public Location getLocationForModule(ModuleElement mdle) { 328 ModuleSymbol msym = (ModuleSymbol)mdle; 329 return msym.sourceLocation != null 330 ? msym.sourceLocation 331 : msym.classLocation; 332 } 333 334 //------------------Start of Serializable Implementation---------------------// 335 private final static Map<TypeElement, NewSerializedForm> serializedForms = new HashMap<>(); 336 337 public SortedSet<VariableElement> getSerializableFields(Utils utils, TypeElement klass) { 338 NewSerializedForm sf = serializedForms.get(klass); 339 if (sf == null) { 340 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 341 serializedForms.put(klass, sf); 342 } 343 return sf.fields; 344 } 345 346 public SortedSet<ExecutableElement> getSerializationMethods(Utils utils, TypeElement klass) { 347 NewSerializedForm sf = serializedForms.get(klass); 348 if (sf == null) { 349 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 350 serializedForms.put(klass, sf); 351 } 352 return sf.methods; 353 } 354 355 public boolean definesSerializableFields(Utils utils, TypeElement klass) { 356 if (!utils.isSerializable(klass) || utils.isExternalizable(klass)) { 357 return false; 358 } else { 359 NewSerializedForm sf = serializedForms.get(klass); 360 if (sf == null) { 361 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 362 serializedForms.put(klass, sf); 363 } 364 return sf.definesSerializableFields; 365 } 366 } 367 368 /* TODO we need a clean port to jx.l.m 369 * The serialized form is the specification of a class' serialization state. 370 * <p> 371 * 372 * It consists of the following information: 373 * <p> 374 * 375 * <pre> 376 * 1. Whether class is Serializable or Externalizable. 377 * 2. Javadoc for serialization methods. 378 * a. For Serializable, the optional readObject, writeObject, 379 * readResolve and writeReplace. 380 * serialData tag describes, in prose, the sequence and type 381 * of optional data written by writeObject. 382 * b. For Externalizable, writeExternal and readExternal. 383 * serialData tag describes, in prose, the sequence and type 384 * of optional data written by writeExternal. 385 * 3. Javadoc for serialization data layout. 386 * a. For Serializable, the name,type and description 387 * of each Serializable fields. 388 * b. For Externalizable, data layout is described by 2(b). 389 * </pre> 390 * 391 */ 392 static class NewSerializedForm { 393 394 final Utils utils; 395 final Elements elements; 396 397 final SortedSet<ExecutableElement> methods; 398 399 /* List of FieldDocImpl - Serializable fields. 400 * Singleton list if class defines Serializable fields explicitly. 401 * Otherwise, list of default serializable fields. 402 * 0 length list for Externalizable. 403 */ 404 final SortedSet<VariableElement> fields; 405 406 /* True if class specifies serializable fields explicitly. 407 * using special static member, serialPersistentFields. 408 */ 409 boolean definesSerializableFields = false; 410 411 // Specially treated field/method names defined by Serialization. 412 private static final String SERIALIZABLE_FIELDS = "serialPersistentFields"; 413 private static final String READOBJECT = "readObject"; 414 private static final String WRITEOBJECT = "writeObject"; 415 private static final String READRESOLVE = "readResolve"; 416 private static final String WRITEREPLACE = "writeReplace"; 417 private static final String READOBJECTNODATA = "readObjectNoData"; 418 419 NewSerializedForm(Utils utils, Elements elements, TypeElement te) { 420 this.utils = utils; 421 this.elements = elements; 422 methods = new TreeSet<>(utils.makeGeneralPurposeComparator()); 423 fields = new TreeSet<>(utils.makeGeneralPurposeComparator()); 424 if (utils.isExternalizable(te)) { 425 /* look up required public accessible methods, 426 * writeExternal and readExternal. 427 */ 428 String[] readExternalParamArr = {"java.io.ObjectInput"}; 429 String[] writeExternalParamArr = {"java.io.ObjectOutput"}; 430 431 ExecutableElement md = findMethod(te, "readExternal", Arrays.asList(readExternalParamArr)); 432 if (md != null) { 433 methods.add(md); 434 } 435 md = findMethod((ClassSymbol) te, "writeExternal", Arrays.asList(writeExternalParamArr)); 436 if (md != null) { 437 methods.add(md); 438 } 439 } else if (utils.isSerializable(te)) { 440 VarSymbol dsf = getDefinedSerializableFields((ClassSymbol) te); 441 if (dsf != null) { 442 /* Define serializable fields with array of ObjectStreamField. 443 * Each ObjectStreamField should be documented by a 444 * serialField tag. 445 */ 446 definesSerializableFields = true; 447 fields.add((VariableElement) dsf); 448 } else { 449 450 /* Calculate default Serializable fields as all 451 * non-transient, non-static fields. 452 * Fields should be documented by serial tag. 453 */ 454 computeDefaultSerializableFields((ClassSymbol) te); 455 } 456 457 /* Check for optional customized readObject, writeObject, 458 * readResolve and writeReplace, which can all contain 459 * the serialData tag. */ 460 addMethodIfExist((ClassSymbol) te, READOBJECT); 461 addMethodIfExist((ClassSymbol) te, WRITEOBJECT); 462 addMethodIfExist((ClassSymbol) te, READRESOLVE); 463 addMethodIfExist((ClassSymbol) te, WRITEREPLACE); 464 addMethodIfExist((ClassSymbol) te, READOBJECTNODATA); 465 } 466 } 467 468 private VarSymbol getDefinedSerializableFields(ClassSymbol def) { 469 Names names = def.name.table.names; 470 471 /* SERIALIZABLE_FIELDS can be private, 472 */ 473 for (Symbol sym : def.members().getSymbolsByName(names.fromString(SERIALIZABLE_FIELDS))) { 474 if (sym.kind == VAR) { 475 VarSymbol f = (VarSymbol) sym; 476 if ((f.flags() & Flags.STATIC) != 0 477 && (f.flags() & Flags.PRIVATE) != 0) { 478 return f; 479 } 480 } 481 } 482 return null; 483 } 484 485 /* 486 * Catalog Serializable method if it exists in current ClassSymbol. 487 * Do not look for method in superclasses. 488 * 489 * Serialization requires these methods to be non-static. 490 * 491 * @param method should be an unqualified Serializable method 492 * name either READOBJECT, WRITEOBJECT, READRESOLVE 493 * or WRITEREPLACE. 494 * @param visibility the visibility flag for the given method. 495 */ 496 private void addMethodIfExist(ClassSymbol def, String methodName) { 497 Names names = def.name.table.names; 498 499 for (Symbol sym : def.members().getSymbolsByName(names.fromString(methodName))) { 500 if (sym.kind == MTH) { 501 MethodSymbol md = (MethodSymbol) sym; 502 if ((md.flags() & Flags.STATIC) == 0) { 503 /* 504 * WARNING: not robust if unqualifiedMethodName is overloaded 505 * method. Signature checking could make more robust. 506 * READOBJECT takes a single parameter, java.io.ObjectInputStream. 507 * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream. 508 */ 509 methods.add(md); 510 } 511 } 512 } 513 } 514 515 /* 516 * Compute default Serializable fields from all members of ClassSymbol. 517 * 518 * must walk over all members of ClassSymbol. 519 */ 520 private void computeDefaultSerializableFields(ClassSymbol te) { 521 for (Symbol sym : te.members().getSymbols(NON_RECURSIVE)) { 522 if (sym != null && sym.kind == VAR) { 523 VarSymbol f = (VarSymbol) sym; 524 if ((f.flags() & Flags.STATIC) == 0 525 && (f.flags() & Flags.TRANSIENT) == 0) { 526 //### No modifier filtering applied here. 527 //### Add to beginning. 528 //### Preserve order used by old 'javadoc'. 529 fields.add(f); 530 } 531 } 532 } 533 } 534 535 /** 536 * Find a method in this class scope. Search order: this class, interfaces, superclasses, 537 * outerclasses. Note that this is not necessarily what the compiler would do! 538 * 539 * @param methodName the unqualified name to search for. 540 * @param paramTypes the array of Strings for method parameter types. 541 * @return the first MethodDocImpl which matches, null if not found. 542 */ 543 public ExecutableElement findMethod(TypeElement te, String methodName, 544 List<String> paramTypes) { 545 List<? extends Element> allMembers = this.elements.getAllMembers(te); 546 loop: 547 for (Element e : allMembers) { 548 if (e.getKind() != METHOD) { 549 continue; 550 } 551 ExecutableElement ee = (ExecutableElement) e; 552 if (!ee.getSimpleName().contentEquals(methodName)) { 553 continue; 554 } 555 List<? extends VariableElement> parameters = ee.getParameters(); 556 if (paramTypes.size() != parameters.size()) { 557 continue; 558 } 559 for (int i = 0; i < parameters.size(); i++) { 560 VariableElement ve = parameters.get(i); 561 if (!ve.asType().toString().equals(paramTypes.get(i))) { 562 break loop; 563 } 564 } 565 return ee; 566 } 567 TypeElement encl = utils.getEnclosingTypeElement(te); 568 if (encl == null) { 569 return null; 570 } 571 return findMethod(encl, methodName, paramTypes); 572 } 573 } 574 575 // TODO: we need to eliminate this, as it is hacky. 576 /** 577 * Returns a representation of the package truncated to two levels. 578 * For instance if the given package represents foo.bar.baz will return 579 * a representation of foo.bar 580 * @param pkg the PackageElement 581 * @return an abbreviated PackageElement 582 */ 583 public PackageElement getAbbreviatedPackageElement(PackageElement pkg) { 584 String parsedPackageName = utils.parsePackageName(pkg); 585 ModuleElement encl = (ModuleElement) pkg.getEnclosingElement(); 586 PackageElement abbrevPkg = encl == null 587 ? utils.elementUtils.getPackageElement(parsedPackageName) 588 : ((JavacElements) utils.elementUtils).getPackageElement(encl, parsedPackageName); 589 return abbrevPkg; 590 } 591 }