src/jdk.compiler/share/classes/com/sun/tools/javac/code/ModuleFinder.java
author Dusan Balek <dbalek@netbeans.org>
Mon, 31 Jul 2017 11:07:41 +0200
changeset 5955 f54cccaf6e6c
parent 5855 0fb5201da354
permissions -rw-r--r--
Mergin jlahoda's fix of #8182450: javac aborts when generating ct.sym intermittently - Initialize the module system model even in presence of missing/broken module-infos; BadClassFiles should not immediatelly abort compilation anymore, but should be handled as if the classfile did not exist.
     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 package com.sun.tools.javac.code;
    26 
    27 import java.io.IOException;
    28 import java.util.Arrays;
    29 import java.util.HashMap;
    30 import java.util.Iterator;
    31 import java.util.Map;
    32 import java.util.NoSuchElementException;
    33 import java.util.Set;
    34 
    35 import javax.tools.JavaFileManager;
    36 import javax.tools.JavaFileManager.Location;
    37 import javax.tools.JavaFileObject;
    38 import javax.tools.JavaFileObject.Kind;
    39 import javax.tools.StandardLocation;
    40 
    41 import com.sun.tools.javac.code.Symbol.ClassSymbol;
    42 import com.sun.tools.javac.code.Symbol.Completer;
    43 import com.sun.tools.javac.code.Symbol.CompletionFailure;
    44 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
    45 import com.sun.tools.javac.jvm.ModuleNameReader;
    46 import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile;
    47 import com.sun.tools.javac.resources.CompilerProperties.Errors;
    48 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
    49 import com.sun.tools.javac.util.Assert;
    50 import com.sun.tools.javac.util.Context;
    51 import com.sun.tools.javac.util.JCDiagnostic;
    52 import com.sun.tools.javac.util.JCDiagnostic.Fragment;
    53 import com.sun.tools.javac.util.List;
    54 import com.sun.tools.javac.util.ListBuffer;
    55 import com.sun.tools.javac.util.Log;
    56 import com.sun.tools.javac.util.Name;
    57 import com.sun.tools.javac.util.Names;
    58 
    59 import static com.sun.tools.javac.code.Kinds.Kind.*;
    60 import com.sun.tools.javac.comp.Check;
    61 
    62 /**
    63  *  This class provides operations to locate module definitions
    64  *  from the source and class files on the paths provided to javac.
    65  *
    66  *  <p><b>This is NOT part of any supported API.
    67  *  If you write code that depends on this, you do so at your own risk.
    68  *  This code and its internal interfaces are subject to change or
    69  *  deletion without notice.</b>
    70  */
    71 public class ModuleFinder {
    72     /** The context key for the module finder. */
    73     protected static final Context.Key<ModuleFinder> moduleFinderKey = new Context.Key<>();
    74 
    75     /** The log to use for verbose output. */
    76     private final Log log;
    77 
    78     /** The symbol table. */
    79     private final Symtab syms;
    80 
    81     /** The name table. */
    82     private final Names names;
    83 
    84     private final ClassFinder classFinder;
    85     private final Check chk;
    86 
    87     /** Access to files
    88      */
    89     private final JavaFileManager fileManager;
    90 
    91     private final JCDiagnostic.Factory diags;
    92 
    93     private ModuleNameReader moduleNameReader;
    94 
    95     public ModuleNameFromSourceReader moduleNameFromSourceReader;
    96 
    97     /** Get the ModuleFinder instance for this invocation. */
    98     public static ModuleFinder instance(Context context) {
    99         ModuleFinder instance = context.get(moduleFinderKey);
   100         if (instance == null)
   101             instance = new ModuleFinder(context);
   102         return instance;
   103     }
   104 
   105     /** Construct a new module finder. */
   106     protected ModuleFinder(Context context) {
   107         context.put(moduleFinderKey, this);
   108         names = Names.instance(context);
   109         syms = Symtab.instance(context);
   110         fileManager = context.get(JavaFileManager.class);
   111         log = Log.instance(context);
   112         classFinder = ClassFinder.instance(context);
   113         chk = Check.instance(context);
   114 
   115         diags = JCDiagnostic.Factory.instance(context);
   116     }
   117 
   118     class ModuleLocationIterator implements Iterator<Set<Location>> {
   119         StandardLocation outer;
   120         Set<Location> next = null;
   121 
   122         Iterator<StandardLocation> outerIter = Arrays.asList(
   123                 StandardLocation.MODULE_SOURCE_PATH,
   124                 StandardLocation.UPGRADE_MODULE_PATH,
   125                 StandardLocation.SYSTEM_MODULES,
   126                 StandardLocation.MODULE_PATH
   127         ).iterator();
   128         Iterator<Set<Location>> innerIter = null;
   129 
   130         @Override
   131         public boolean hasNext() {
   132             while (next == null) {
   133                 while (innerIter == null || !innerIter.hasNext()) {
   134                     if (outerIter.hasNext()) {
   135                         outer = outerIter.next();
   136                         try {
   137                             innerIter = fileManager.listLocationsForModules(outer).iterator();
   138                         } catch (IOException e) {
   139                             System.err.println("error listing module locations for " + outer + ": " + e);  // FIXME
   140                         }
   141                     } else
   142                         return false;
   143                 }
   144 
   145                 if (innerIter.hasNext())
   146                     next = innerIter.next();
   147             }
   148             return true;
   149         }
   150 
   151         @Override
   152         public Set<Location> next() {
   153             hasNext();
   154             if (next != null) {
   155                 Set<Location> result = next;
   156                 next = null;
   157                 return result;
   158             }
   159             throw new NoSuchElementException();
   160         }
   161 
   162     }
   163 
   164     ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator();
   165 
   166     public ModuleSymbol findModule(Name name) {
   167         return findModule(syms.enterModule(name));
   168     }
   169 
   170     public ModuleSymbol findModule(ModuleSymbol msym) {
   171         if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) {
   172             // fill in location
   173             List<ModuleSymbol> list = scanModulePath(msym);
   174             if (list.isEmpty()) {
   175                 msym.kind = ERR;
   176             }
   177         }
   178         if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
   179             // fill in module-info
   180             findModuleInfo(msym);
   181         }
   182         return msym;
   183     }
   184 
   185     public List<ModuleSymbol> findAllModules() {
   186         List<ModuleSymbol> list = scanModulePath(null);
   187         for (ModuleSymbol msym: list) {
   188             if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
   189                 // fill in module-info
   190                 findModuleInfo(msym);
   191             }
   192         }
   193         return list;
   194     }
   195 
   196     public ModuleSymbol findSingleModule() {
   197         try {
   198             JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE);
   199             JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
   200             JavaFileObject fo = (src_fo == null) ? class_fo
   201                     : (class_fo == null) ? src_fo
   202                             : classFinder.preferredFileObject(src_fo, class_fo);
   203 
   204             ModuleSymbol msym;
   205             if (fo == null) {
   206                 msym = syms.unnamedModule;
   207             } else {
   208                 try {
   209                     msym = readModule(fo);
   210                 } catch (CompletionFailure ex) {
   211                     chk.completionError(null, ex);
   212                     msym = syms.unnamedModule;
   213                 }
   214                     
   215             }
   216 
   217             if (msym.patchLocation == null) {
   218                 msym.classLocation = StandardLocation.CLASS_OUTPUT;
   219             } else {
   220                 msym.patchOutputLocation = StandardLocation.CLASS_OUTPUT;
   221             }
   222             return msym;
   223 
   224         } catch (IOException e) {
   225             throw new Error(e); // FIXME
   226         }
   227     }
   228 
   229     private ModuleSymbol readModule(JavaFileObject fo) throws IOException {
   230         Name name;
   231         switch (fo.getKind()) {
   232             case SOURCE:
   233                 name = moduleNameFromSourceReader.readModuleName(fo);
   234                 if (name == null) {
   235                     JCDiagnostic diag =
   236                         diags.fragment("file.does.not.contain.module");
   237                     ClassSymbol errModuleInfo = syms.defineClass(names.module_info, syms.errModule);
   238                     throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags);
   239                 }
   240                 break;
   241             case CLASS:
   242                 try {
   243                     name = names.fromString(readModuleName(fo));
   244                 } catch (BadClassFile | IOException ex) {
   245                     //fillIn will report proper errors:
   246                     name = names.error;
   247                 }
   248                 break;
   249             default:
   250                 Assert.error();
   251                 name = names.error;
   252                 break;
   253         }
   254 
   255         ModuleSymbol msym = syms.enterModule(name);
   256         msym.module_info.classfile = fo;
   257         if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && name != names.error) {
   258             msym.patchLocation = fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, name.toString());
   259 
   260             if (msym.patchLocation != null) {
   261                 JavaFileObject patchFO = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
   262                 patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), patchFO);
   263                 patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), patchFO);
   264 
   265                 if (patchFO != null) {
   266                     msym.module_info.classfile = patchFO;
   267                 }
   268             }
   269         }
   270 
   271         msym.completer = Completer.NULL_COMPLETER;
   272         classFinder.fillIn(msym.module_info);
   273 
   274         return msym;
   275     }
   276 
   277     private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile {
   278         if (moduleNameReader == null)
   279             moduleNameReader = new ModuleNameReader();
   280         return moduleNameReader.readModuleName(jfo);
   281     }
   282 
   283     private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException {
   284         if (location == null || !fileManager.hasLocation(location))
   285             return null;
   286 
   287         return fileManager.getJavaFileForInput(location,
   288                                                names.module_info.toString(),
   289                                                kind);
   290     }
   291 
   292     private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) {
   293         ListBuffer<ModuleSymbol> results = new ListBuffer<>();
   294         Map<Name, Location> namesInSet = new HashMap<>();
   295         boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
   296         while (moduleLocationIterator.hasNext()) {
   297             Set<Location> locns = (moduleLocationIterator.next());
   298             namesInSet.clear();
   299             for (Location l: locns) {
   300                 try {
   301                     Name n = names.fromString(fileManager.inferModuleName(l));
   302                     if (namesInSet.put(n, l) == null) {
   303                         ModuleSymbol msym = syms.enterModule(n);
   304                         if (msym.sourceLocation != null || msym.classLocation != null) {
   305                             // module has already been found, so ignore this instance
   306                             continue;
   307                         }
   308                         if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) &&
   309                             msym.patchLocation == null) {
   310                             msym.patchLocation =
   311                                     fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH,
   312                                                                      msym.name.toString());
   313                             if (msym.patchLocation != null &&
   314                                 multiModuleMode &&
   315                                 fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
   316                                 msym.patchOutputLocation =
   317                                         fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
   318                                                                          msym.name.toString());
   319                             }
   320                         }
   321                         if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) {
   322                             msym.sourceLocation = l;
   323                             if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
   324                                 msym.classLocation =
   325                                         fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
   326                                                                          msym.name.toString());
   327                             }
   328                         } else {
   329                             msym.classLocation = l;
   330                         }
   331                         if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES ||
   332                             moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) {
   333                             msym.flags_field |= Flags.SYSTEM_MODULE;
   334                         }
   335                         if (toFind == null ||
   336                             (toFind == msym && (msym.sourceLocation != null || msym.classLocation != null))) {
   337                             // Note: cannot return msym directly, because we must finish
   338                             // processing this set first
   339                             results.add(msym);
   340                         }
   341                     } else {
   342                         log.error(Errors.DuplicateModuleOnPath(
   343                                 getDescription(moduleLocationIterator.outer), n));
   344                     }
   345                 } catch (IOException e) {
   346                     // skip location for now?  log error?
   347                 }
   348             }
   349             if (toFind != null && results.nonEmpty())
   350                 return results.toList();
   351         }
   352 
   353         return results.toList();
   354     }
   355 
   356     private void findModuleInfo(ModuleSymbol msym) {
   357         try {
   358             JavaFileObject fo;
   359 
   360             fo = getModuleInfoFromLocation(msym.patchOutputLocation, Kind.CLASS);
   361             fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), fo);
   362             fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), fo);
   363 
   364             if (fo == null) {
   365                 fo = getModuleInfoFromLocation(msym.classLocation, Kind.CLASS);
   366                 fo = preferredFileObject(getModuleInfoFromLocation(msym.sourceLocation, Kind.SOURCE), fo);
   367             }
   368 
   369             if (fo == null) {
   370                 String moduleName = msym.sourceLocation == null && msym.classLocation != null ?
   371                     fileManager.inferModuleName(msym.classLocation) : null;
   372                 if (moduleName != null) {
   373                     msym.module_info.classfile = null;
   374                     msym.flags_field |= Flags.AUTOMATIC_MODULE;
   375                 } else {
   376                     msym.kind = ERR;
   377                 }
   378             } else {
   379                 msym.module_info.classfile = fo;
   380                 msym.module_info.completer = new Symbol.Completer() {
   381                     @Override
   382                     public void complete(Symbol sym) throws CompletionFailure {
   383                         try {
   384                             classFinder.fillIn(msym.module_info);
   385                         } catch (Exception ex) {
   386                             msym.kind = ERR;
   387                             //make sure the module is initialized:
   388                             msym.directives = List.nil();
   389                             msym.exports = List.nil();
   390                             msym.provides = List.nil();
   391                             msym.requires = List.nil();
   392                             msym.uses = List.nil();
   393                         }
   394                     }
   395                     @Override
   396                     public String toString() {
   397                         return "ModuleInfoCompleter";
   398                     }
   399                 };
   400             }
   401         } catch (IOException e) {
   402             msym.kind = ERR;
   403         }
   404     }
   405 
   406     private JavaFileObject preferredFileObject(JavaFileObject fo1, JavaFileObject fo2) {
   407         if (fo1 == null) return fo2;
   408         if (fo2 == null) return fo1;
   409         return classFinder.preferredFileObject(fo1, fo2);
   410     }
   411 
   412     Fragment getDescription(StandardLocation l) {
   413         switch (l) {
   414             case MODULE_PATH: return Fragments.LocnModule_path;
   415             case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path;
   416             case SYSTEM_MODULES: return Fragments.LocnSystem_modules;
   417             case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path;
   418             default:
   419                 throw new AssertionError();
   420         }
   421     }
   422 
   423     public interface ModuleNameFromSourceReader {
   424         public Name readModuleName(JavaFileObject file);
   425     }
   426 
   427 }