langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/ClassFinder.java
changeset 25874 83c19f00452c
parent 25844 48eab270456c
child 27224 228abfa87080
equal deleted inserted replaced
25873:024ed9c9ed13 25874:83c19f00452c
       
     1 /*
       
     2  * Copyright (c) 1999, 2014, 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 com.sun.tools.javac.code;
       
    27 
       
    28 import java.io.*;
       
    29 import java.util.EnumSet;
       
    30 import java.util.Set;
       
    31 import javax.lang.model.SourceVersion;
       
    32 import javax.tools.JavaFileObject;
       
    33 import javax.tools.JavaFileManager;
       
    34 import javax.tools.JavaFileManager.Location;
       
    35 import javax.tools.StandardJavaFileManager;
       
    36 
       
    37 import static javax.tools.StandardLocation.*;
       
    38 
       
    39 import com.sun.tools.javac.comp.Annotate;
       
    40 import com.sun.tools.javac.code.Scope.WriteableScope;
       
    41 import com.sun.tools.javac.code.Symbol.*;
       
    42 import com.sun.tools.javac.jvm.ClassReader;
       
    43 import com.sun.tools.javac.util.*;
       
    44 
       
    45 import static com.sun.tools.javac.code.Flags.*;
       
    46 import static com.sun.tools.javac.code.Kinds.*;
       
    47 
       
    48 import static com.sun.tools.javac.main.Option.*;
       
    49 
       
    50 /**
       
    51  *  This class provides operations to locate class definitions
       
    52  *  from the source and class files on the paths provided to javac.
       
    53  *
       
    54  *  <p><b>This is NOT part of any supported API.
       
    55  *  If you write code that depends on this, you do so at your own risk.
       
    56  *  This code and its internal interfaces are subject to change or
       
    57  *  deletion without notice.</b>
       
    58  */
       
    59 public class ClassFinder {
       
    60     /** The context key for the class finder. */
       
    61     protected static final Context.Key<ClassFinder> classFinderKey = new Context.Key<>();
       
    62 
       
    63     ClassReader reader;
       
    64 
       
    65     Annotate annotate;
       
    66 
       
    67     /** Switch: verbose output.
       
    68      */
       
    69     boolean verbose;
       
    70 
       
    71     /**
       
    72      * Switch: cache completion failures unless -XDdev is used
       
    73      */
       
    74     private boolean cacheCompletionFailure;
       
    75 
       
    76     /**
       
    77      * Switch: prefer source files instead of newer when both source
       
    78      * and class are available
       
    79      **/
       
    80     protected boolean preferSource;
       
    81 
       
    82     /**
       
    83      * Switch: Search classpath and sourcepath for classes before the
       
    84      * bootclasspath
       
    85      */
       
    86     protected boolean userPathsFirst;
       
    87 
       
    88     /** The log to use for verbose output
       
    89      */
       
    90     final Log log;
       
    91 
       
    92     /** The symbol table. */
       
    93     Symtab syms;
       
    94 
       
    95     /** The name table. */
       
    96     final Names names;
       
    97 
       
    98     /** Force a completion failure on this name
       
    99      */
       
   100     final Name completionFailureName;
       
   101 
       
   102     /** Access to files
       
   103      */
       
   104     private final JavaFileManager fileManager;
       
   105 
       
   106     /** Dependency tracker
       
   107      */
       
   108     private final Dependencies dependencies;
       
   109 
       
   110     /** Factory for diagnostics
       
   111      */
       
   112     JCDiagnostic.Factory diagFactory;
       
   113 
       
   114     /** Can be reassigned from outside:
       
   115      *  the completer to be used for ".java" files. If this remains unassigned
       
   116      *  ".java" files will not be loaded.
       
   117      */
       
   118     public Completer sourceCompleter = null;
       
   119 
       
   120     /** The path name of the class file currently being read.
       
   121      */
       
   122     protected JavaFileObject currentClassFile = null;
       
   123 
       
   124     /** The class or method currently being read.
       
   125      */
       
   126     protected Symbol currentOwner = null;
       
   127 
       
   128     /**
       
   129      * Completer that delegates to the complete-method of this class.
       
   130      */
       
   131     private final Completer thisCompleter = new Completer() {
       
   132         @Override
       
   133         public void complete(Symbol sym) throws CompletionFailure {
       
   134             ClassFinder.this.complete(sym);
       
   135         }
       
   136     };
       
   137 
       
   138     public Completer getCompleter() {
       
   139         return thisCompleter;
       
   140     }
       
   141 
       
   142     /** Get the ClassFinder instance for this invocation. */
       
   143     public static ClassFinder instance(Context context) {
       
   144         ClassFinder instance = context.get(classFinderKey);
       
   145         if (instance == null)
       
   146             instance = new ClassFinder(context);
       
   147         return instance;
       
   148     }
       
   149 
       
   150     /** Construct a new class reader. */
       
   151     protected ClassFinder(Context context) {
       
   152         context.put(classFinderKey, this);
       
   153         reader = ClassReader.instance(context);
       
   154         names = Names.instance(context);
       
   155         syms = Symtab.instance(context);
       
   156         fileManager = context.get(JavaFileManager.class);
       
   157         dependencies = Dependencies.instance(context);
       
   158         if (fileManager == null)
       
   159             throw new AssertionError("FileManager initialization error");
       
   160         diagFactory = JCDiagnostic.Factory.instance(context);
       
   161 
       
   162         log = Log.instance(context);
       
   163         annotate = Annotate.instance(context);
       
   164 
       
   165         Options options = Options.instance(context);
       
   166         verbose = options.isSet(VERBOSE);
       
   167         cacheCompletionFailure = options.isUnset("dev");
       
   168         preferSource = "source".equals(options.get("-Xprefer"));
       
   169         userPathsFirst = options.isSet(XXUSERPATHSFIRST);
       
   170 
       
   171 
       
   172         completionFailureName =
       
   173             options.isSet("failcomplete")
       
   174             ? names.fromString(options.get("failcomplete"))
       
   175             : null;
       
   176     }
       
   177 
       
   178 /************************************************************************
       
   179  * Loading Classes
       
   180  ***********************************************************************/
       
   181 
       
   182     /** Completion for classes to be loaded. Before a class is loaded
       
   183      *  we make sure its enclosing class (if any) is loaded.
       
   184      */
       
   185     private void complete(Symbol sym) throws CompletionFailure {
       
   186         if (sym.kind == TYP) {
       
   187             try {
       
   188                 ClassSymbol c = (ClassSymbol) sym;
       
   189                 dependencies.push(c);
       
   190                 c.members_field = new Scope.ErrorScope(c); // make sure it's always defined
       
   191                 annotate.enterStart();
       
   192                 try {
       
   193                     completeOwners(c.owner);
       
   194                     completeEnclosing(c);
       
   195                 } finally {
       
   196                     // The flush needs to happen only after annotations
       
   197                     // are filled in.
       
   198                     annotate.enterDoneWithoutFlush();
       
   199                 }
       
   200                 fillIn(c);
       
   201             } finally {
       
   202                 dependencies.pop();
       
   203             }
       
   204         } else if (sym.kind == PCK) {
       
   205             PackageSymbol p = (PackageSymbol)sym;
       
   206             try {
       
   207                 fillIn(p);
       
   208             } catch (IOException ex) {
       
   209                 throw new CompletionFailure(sym, ex.getLocalizedMessage()).initCause(ex);
       
   210             }
       
   211         }
       
   212         if (!reader.filling)
       
   213             annotate.flush(); // finish attaching annotations
       
   214     }
       
   215 
       
   216     /** complete up through the enclosing package. */
       
   217     private void completeOwners(Symbol o) {
       
   218         if (o.kind != PCK) completeOwners(o.owner);
       
   219         o.complete();
       
   220     }
       
   221 
       
   222     /**
       
   223      * Tries to complete lexically enclosing classes if c looks like a
       
   224      * nested class.  This is similar to completeOwners but handles
       
   225      * the situation when a nested class is accessed directly as it is
       
   226      * possible with the Tree API or javax.lang.model.*.
       
   227      */
       
   228     private void completeEnclosing(ClassSymbol c) {
       
   229         if (c.owner.kind == PCK) {
       
   230             Symbol owner = c.owner;
       
   231             for (Name name : Convert.enclosingCandidates(Convert.shortName(c.name))) {
       
   232                 Symbol encl = owner.members().findFirst(name);
       
   233                 if (encl == null)
       
   234                     encl = syms.classes.get(TypeSymbol.formFlatName(name, owner));
       
   235                 if (encl != null)
       
   236                     encl.complete();
       
   237             }
       
   238         }
       
   239     }
       
   240 
       
   241     /** Fill in definition of class `c' from corresponding class or
       
   242      *  source file.
       
   243      */
       
   244     private void fillIn(ClassSymbol c) {
       
   245         if (completionFailureName == c.fullname) {
       
   246             throw new CompletionFailure(c, "user-selected completion failure by class name");
       
   247         }
       
   248         currentOwner = c;
       
   249         JavaFileObject classfile = c.classfile;
       
   250         if (classfile != null) {
       
   251             JavaFileObject previousClassFile = currentClassFile;
       
   252             try {
       
   253                 if (reader.filling) {
       
   254                     Assert.error("Filling " + classfile.toUri() + " during " + previousClassFile);
       
   255                 }
       
   256                 currentClassFile = classfile;
       
   257                 if (verbose) {
       
   258                     log.printVerbose("loading", currentClassFile.toString());
       
   259                 }
       
   260                 if (classfile.getKind() == JavaFileObject.Kind.CLASS) {
       
   261                     reader.readClassFile(c);
       
   262                 } else {
       
   263                     if (sourceCompleter != null) {
       
   264                         sourceCompleter.complete(c);
       
   265                     } else {
       
   266                         throw new IllegalStateException("Source completer required to read "
       
   267                                                         + classfile.toUri());
       
   268                     }
       
   269                 }
       
   270             } finally {
       
   271                 currentClassFile = previousClassFile;
       
   272             }
       
   273         } else {
       
   274             JCDiagnostic diag =
       
   275                 diagFactory.fragment("class.file.not.found", c.flatname);
       
   276             throw
       
   277                 newCompletionFailure(c, diag);
       
   278         }
       
   279     }
       
   280     // where
       
   281         /** Static factory for CompletionFailure objects.
       
   282          *  In practice, only one can be used at a time, so we share one
       
   283          *  to reduce the expense of allocating new exception objects.
       
   284          */
       
   285         private CompletionFailure newCompletionFailure(TypeSymbol c,
       
   286                                                        JCDiagnostic diag) {
       
   287             if (!cacheCompletionFailure) {
       
   288                 // log.warning("proc.messager",
       
   289                 //             Log.getLocalizedString("class.file.not.found", c.flatname));
       
   290                 // c.debug.printStackTrace();
       
   291                 return new CompletionFailure(c, diag);
       
   292             } else {
       
   293                 CompletionFailure result = cachedCompletionFailure;
       
   294                 result.sym = c;
       
   295                 result.diag = diag;
       
   296                 return result;
       
   297             }
       
   298         }
       
   299         private CompletionFailure cachedCompletionFailure =
       
   300             new CompletionFailure(null, (JCDiagnostic) null);
       
   301         {
       
   302             cachedCompletionFailure.setStackTrace(new StackTraceElement[0]);
       
   303         }
       
   304 
       
   305 
       
   306     /** Load a toplevel class with given fully qualified name
       
   307      *  The class is entered into `classes' only if load was successful.
       
   308      */
       
   309     public ClassSymbol loadClass(Name flatname) throws CompletionFailure {
       
   310         boolean absent = syms.classes.get(flatname) == null;
       
   311         ClassSymbol c = syms.enterClass(flatname);
       
   312         if (c.members_field == null && c.completer != null) {
       
   313             try {
       
   314                 c.complete();
       
   315             } catch (CompletionFailure ex) {
       
   316                 if (absent) syms.classes.remove(flatname);
       
   317                 throw ex;
       
   318             }
       
   319         }
       
   320         return c;
       
   321     }
       
   322 
       
   323 /************************************************************************
       
   324  * Loading Packages
       
   325  ***********************************************************************/
       
   326 
       
   327     /** Include class corresponding to given class file in package,
       
   328      *  unless (1) we already have one the same kind (.class or .java), or
       
   329      *         (2) we have one of the other kind, and the given class file
       
   330      *             is older.
       
   331      */
       
   332     protected void includeClassFile(PackageSymbol p, JavaFileObject file) {
       
   333         if ((p.flags_field & EXISTS) == 0)
       
   334             for (Symbol q = p; q != null && q.kind == PCK; q = q.owner)
       
   335                 q.flags_field |= EXISTS;
       
   336         JavaFileObject.Kind kind = file.getKind();
       
   337         int seen;
       
   338         if (kind == JavaFileObject.Kind.CLASS)
       
   339             seen = CLASS_SEEN;
       
   340         else
       
   341             seen = SOURCE_SEEN;
       
   342         String binaryName = fileManager.inferBinaryName(currentLoc, file);
       
   343         int lastDot = binaryName.lastIndexOf(".");
       
   344         Name classname = names.fromString(binaryName.substring(lastDot + 1));
       
   345         boolean isPkgInfo = classname == names.package_info;
       
   346         ClassSymbol c = isPkgInfo
       
   347             ? p.package_info
       
   348             : (ClassSymbol) p.members_field.findFirst(classname);
       
   349         if (c == null) {
       
   350             c = syms.enterClass(classname, p);
       
   351             if (c.classfile == null) // only update the file if's it's newly created
       
   352                 c.classfile = file;
       
   353             if (isPkgInfo) {
       
   354                 p.package_info = c;
       
   355             } else {
       
   356                 if (c.owner == p)  // it might be an inner class
       
   357                     p.members_field.enter(c);
       
   358             }
       
   359         } else if (!preferCurrent && c.classfile != null && (c.flags_field & seen) == 0) {
       
   360             // if c.classfile == null, we are currently compiling this class
       
   361             // and no further action is necessary.
       
   362             // if (c.flags_field & seen) != 0, we have already encountered
       
   363             // a file of the same kind; again no further action is necessary.
       
   364             if ((c.flags_field & (CLASS_SEEN | SOURCE_SEEN)) != 0)
       
   365                 c.classfile = preferredFileObject(file, c.classfile);
       
   366         }
       
   367         c.flags_field |= seen;
       
   368     }
       
   369 
       
   370     /** Implement policy to choose to derive information from a source
       
   371      *  file or a class file when both are present.  May be overridden
       
   372      *  by subclasses.
       
   373      */
       
   374     protected JavaFileObject preferredFileObject(JavaFileObject a,
       
   375                                            JavaFileObject b) {
       
   376 
       
   377         if (preferSource)
       
   378             return (a.getKind() == JavaFileObject.Kind.SOURCE) ? a : b;
       
   379         else {
       
   380             long adate = a.getLastModified();
       
   381             long bdate = b.getLastModified();
       
   382             // 6449326: policy for bad lastModifiedTime in ClassReader
       
   383             //assert adate >= 0 && bdate >= 0;
       
   384             return (adate > bdate) ? a : b;
       
   385         }
       
   386     }
       
   387 
       
   388     /**
       
   389      * specifies types of files to be read when filling in a package symbol
       
   390      */
       
   391     protected EnumSet<JavaFileObject.Kind> getPackageFileKinds() {
       
   392         return EnumSet.of(JavaFileObject.Kind.CLASS, JavaFileObject.Kind.SOURCE);
       
   393     }
       
   394 
       
   395     /**
       
   396      * this is used to support javadoc
       
   397      */
       
   398     protected void extraFileActions(PackageSymbol pack, JavaFileObject fe) {
       
   399     }
       
   400 
       
   401     protected Location currentLoc; // FIXME
       
   402 
       
   403     private boolean verbosePath = true;
       
   404 
       
   405     // Set to true when the currently selected file should be kept
       
   406     private boolean preferCurrent;
       
   407 
       
   408     /** Load directory of package into members scope.
       
   409      */
       
   410     private void fillIn(PackageSymbol p) throws IOException {
       
   411         if (p.members_field == null)
       
   412             p.members_field = WriteableScope.create(p);
       
   413 
       
   414         preferCurrent = false;
       
   415         if (userPathsFirst) {
       
   416             scanUserPaths(p);
       
   417             preferCurrent = true;
       
   418             scanPlatformPath(p);
       
   419         } else {
       
   420             scanPlatformPath(p);
       
   421             scanUserPaths(p);
       
   422         }
       
   423         verbosePath = false;
       
   424     }
       
   425 
       
   426     /**
       
   427      * Scans class path and source path for files in given package.
       
   428      */
       
   429     private void scanUserPaths(PackageSymbol p) throws IOException {
       
   430         Set<JavaFileObject.Kind> kinds = getPackageFileKinds();
       
   431 
       
   432         Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds);
       
   433         classKinds.remove(JavaFileObject.Kind.SOURCE);
       
   434         boolean wantClassFiles = !classKinds.isEmpty();
       
   435 
       
   436         Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds);
       
   437         sourceKinds.remove(JavaFileObject.Kind.CLASS);
       
   438         boolean wantSourceFiles = !sourceKinds.isEmpty();
       
   439 
       
   440         boolean haveSourcePath = fileManager.hasLocation(SOURCE_PATH);
       
   441 
       
   442         if (verbose && verbosePath) {
       
   443             if (fileManager instanceof StandardJavaFileManager) {
       
   444                 StandardJavaFileManager fm = (StandardJavaFileManager)fileManager;
       
   445                 if (haveSourcePath && wantSourceFiles) {
       
   446                     List<File> path = List.nil();
       
   447                     for (File file : fm.getLocation(SOURCE_PATH)) {
       
   448                         path = path.prepend(file);
       
   449                     }
       
   450                     log.printVerbose("sourcepath", path.reverse().toString());
       
   451                 } else if (wantSourceFiles) {
       
   452                     List<File> path = List.nil();
       
   453                     for (File file : fm.getLocation(CLASS_PATH)) {
       
   454                         path = path.prepend(file);
       
   455                     }
       
   456                     log.printVerbose("sourcepath", path.reverse().toString());
       
   457                 }
       
   458                 if (wantClassFiles) {
       
   459                     List<File> path = List.nil();
       
   460                     for (File file : fm.getLocation(PLATFORM_CLASS_PATH)) {
       
   461                         path = path.prepend(file);
       
   462                     }
       
   463                     for (File file : fm.getLocation(CLASS_PATH)) {
       
   464                         path = path.prepend(file);
       
   465                     }
       
   466                     log.printVerbose("classpath",  path.reverse().toString());
       
   467                 }
       
   468             }
       
   469         }
       
   470 
       
   471         String packageName = p.fullname.toString();
       
   472         if (wantSourceFiles && !haveSourcePath) {
       
   473             fillIn(p, CLASS_PATH,
       
   474                    fileManager.list(CLASS_PATH,
       
   475                                     packageName,
       
   476                                     kinds,
       
   477                                     false));
       
   478         } else {
       
   479             if (wantClassFiles)
       
   480                 fillIn(p, CLASS_PATH,
       
   481                        fileManager.list(CLASS_PATH,
       
   482                                         packageName,
       
   483                                         classKinds,
       
   484                                         false));
       
   485             if (wantSourceFiles)
       
   486                 fillIn(p, SOURCE_PATH,
       
   487                        fileManager.list(SOURCE_PATH,
       
   488                                         packageName,
       
   489                                         sourceKinds,
       
   490                                         false));
       
   491         }
       
   492     }
       
   493 
       
   494     /**
       
   495      * Scans platform class path for files in given package.
       
   496      */
       
   497     private void scanPlatformPath(PackageSymbol p) throws IOException {
       
   498         fillIn(p, PLATFORM_CLASS_PATH,
       
   499                fileManager.list(PLATFORM_CLASS_PATH,
       
   500                                 p.fullname.toString(),
       
   501                                 EnumSet.of(JavaFileObject.Kind.CLASS),
       
   502                                 false));
       
   503     }
       
   504     // where
       
   505         private void fillIn(PackageSymbol p,
       
   506                             Location location,
       
   507                             Iterable<JavaFileObject> files)
       
   508         {
       
   509             currentLoc = location;
       
   510             for (JavaFileObject fo : files) {
       
   511                 switch (fo.getKind()) {
       
   512                 case CLASS:
       
   513                 case SOURCE: {
       
   514                     // TODO pass binaryName to includeClassFile
       
   515                     String binaryName = fileManager.inferBinaryName(currentLoc, fo);
       
   516                     String simpleName = binaryName.substring(binaryName.lastIndexOf(".") + 1);
       
   517                     if (SourceVersion.isIdentifier(simpleName) ||
       
   518                         simpleName.equals("package-info"))
       
   519                         includeClassFile(p, fo);
       
   520                     break;
       
   521                 }
       
   522                 default:
       
   523                     extraFileActions(p, fo);
       
   524                 }
       
   525             }
       
   526         }
       
   527 
       
   528     /**
       
   529      * Used for bad class definition files, such as bad .class files or
       
   530      * for .java files with unexpected package or class names.
       
   531      */
       
   532     public static class BadClassFile extends CompletionFailure {
       
   533         private static final long serialVersionUID = 0;
       
   534 
       
   535         public BadClassFile(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag,
       
   536                 JCDiagnostic.Factory diagFactory) {
       
   537             super(sym, createBadClassFileDiagnostic(file, diag, diagFactory));
       
   538         }
       
   539         // where
       
   540         private static JCDiagnostic createBadClassFileDiagnostic(
       
   541                 JavaFileObject file, JCDiagnostic diag, JCDiagnostic.Factory diagFactory) {
       
   542             String key = (file.getKind() == JavaFileObject.Kind.SOURCE
       
   543                         ? "bad.source.file.header" : "bad.class.file.header");
       
   544             return diagFactory.fragment(key, file, diag);
       
   545         }
       
   546     }
       
   547 }