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