1 /* 2 * Copyright (c) 2015, 2016, 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 import java.util.function.Function; 35 36 import javax.tools.JavaFileManager; 37 import javax.tools.JavaFileManager.Location; 38 import javax.tools.JavaFileObject; 39 import javax.tools.JavaFileObject.Kind; 40 import javax.tools.StandardLocation; 41 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 61 /** 62 * This class provides operations to locate module definitions 63 * from the source and class files on the paths provided to javac. 64 * 65 * <p><b>This is NOT part of any supported API. 66 * If you write code that depends on this, you do so at your own risk. 67 * This code and its internal interfaces are subject to change or 68 * deletion without notice.</b> 69 */ 70 public class ModuleFinder { 71 /** The context key for the module finder. */ 72 protected static final Context.Key<ModuleFinder> moduleFinderKey = new Context.Key<>(); 73 74 /** The log to use for verbose output. */ 75 private final Log log; 76 77 /** The symbol table. */ 78 private final Symtab syms; 79 80 /** The name table. */ 81 private final Names names; 82 83 private final ClassFinder classFinder; 84 85 /** Access to files 86 */ 87 private final JavaFileManager fileManager; 88 89 private final JCDiagnostic.Factory diags; 90 91 private ModuleNameReader moduleNameReader; 92 93 public ModuleInfoSourceFileCompleter sourceFileCompleter; 94 95 /** Get the ModuleFinder instance for this invocation. */ 96 public static ModuleFinder instance(Context context) { 97 ModuleFinder instance = context.get(moduleFinderKey); 98 if (instance == null) 99 instance = new ModuleFinder(context); 100 return instance; 101 } 102 103 /** Construct a new module finder. */ 104 protected ModuleFinder(Context context) { 105 context.put(moduleFinderKey, this); 106 names = Names.instance(context); 107 syms = Symtab.instance(context); 108 fileManager = context.get(JavaFileManager.class); 109 log = Log.instance(context); 110 classFinder = ClassFinder.instance(context); 111 112 diags = JCDiagnostic.Factory.instance(context); 113 } 114 115 class ModuleLocationIterator implements Iterator<Set<Location>> { 116 StandardLocation outer; 117 Set<Location> next = null; 118 119 Iterator<StandardLocation> outerIter = Arrays.asList( 120 StandardLocation.MODULE_SOURCE_PATH, 121 StandardLocation.UPGRADE_MODULE_PATH, 122 StandardLocation.SYSTEM_MODULES, 123 StandardLocation.MODULE_PATH 124 ).iterator(); 125 Iterator<Set<Location>> innerIter = null; 126 127 @Override 128 public boolean hasNext() { 129 while (next == null) { 130 while (innerIter == null || !innerIter.hasNext()) { 131 if (outerIter.hasNext()) { 132 outer = outerIter.next(); 133 try { 134 innerIter = fileManager.listLocationsForModules(outer).iterator(); 135 } catch (IOException e) { 136 System.err.println("error listing module locations for " + outer + ": " + e); // FIXME 137 } 138 } else 139 return false; 140 } 141 142 if (innerIter.hasNext()) 143 next = innerIter.next(); 144 } 145 return true; 146 } 147 148 @Override 149 public Set<Location> next() { 150 hasNext(); 151 if (next != null) { 152 Set<Location> result = next; 153 next = null; 154 return result; 155 } 156 throw new NoSuchElementException(); 157 } 158 159 } 160 161 ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator(); 162 163 public ModuleSymbol findModule(Name name) { 164 return findModule(syms.enterModule(name)); 165 } 166 167 public ModuleSymbol findModule(ModuleSymbol msym) { 168 if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) { 169 // fill in location 170 List<ModuleSymbol> list = scanModulePath(msym); 171 if (list.isEmpty()) { 172 msym.kind = ERR; 173 } 174 } 175 if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { 176 // fill in module-info 177 findModuleInfo(msym); 178 } 179 return msym; 180 } 181 182 public List<ModuleSymbol> findAllModules() { 183 List<ModuleSymbol> list = scanModulePath(null); 184 for (ModuleSymbol msym: list) { 185 if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { 186 // fill in module-info 187 findModuleInfo(msym); 188 } 189 } 190 return list; 191 } 192 193 private boolean inFindSingleModule; 194 195 public ModuleSymbol findSingleModule() { 196 try { 197 JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE); 198 JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS); 199 JavaFileObject fo = (src_fo == null) ? class_fo 200 : (class_fo == null) ? src_fo 201 : classFinder.preferredFileObject(src_fo, class_fo); 202 203 ModuleSymbol msym; 204 if (fo == null) { 205 msym = syms.unnamedModule; 206 } else { 207 switch (fo.getKind()) { 208 case SOURCE: 209 if (!inFindSingleModule) { 210 try { 211 inFindSingleModule = true; 212 // Note: the following will trigger a re-entrant call to Modules.enter 213 msym = sourceFileCompleter.complete(fo); 214 msym.module_info.classfile = fo; 215 } finally { 216 inFindSingleModule = false; 217 } 218 } else { 219 //the module-info.java does not contain a module declaration, 220 //avoid infinite recursion: 221 msym = syms.unnamedModule; 222 } 223 break; 224 case CLASS: 225 Name name; 226 try { 227 name = names.fromString(readModuleName(fo)); 228 } catch (BadClassFile | IOException ex) { 229 //fillIn will report proper errors: 230 name = names.error; 231 } 232 msym = syms.enterModule(name); 233 msym.module_info.classfile = fo; 234 msym.completer = Completer.NULL_COMPLETER; 235 classFinder.fillIn(msym.module_info); 236 break; 237 default: 238 Assert.error(); 239 msym = syms.unnamedModule; 240 break; 241 } 242 } 243 244 msym.classLocation = StandardLocation.CLASS_OUTPUT; 245 return msym; 246 247 } catch (IOException e) { 248 throw new Error(e); // FIXME 249 } 250 } 251 252 private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile { 253 if (moduleNameReader == null) 254 moduleNameReader = new ModuleNameReader(); 255 return moduleNameReader.readModuleName(jfo); 256 } 257 258 private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException { 259 if (!fileManager.hasLocation(location)) 260 return null; 261 262 return fileManager.getJavaFileForInput(location, 263 names.module_info.toString(), 264 kind); 265 } 266 267 private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) { 268 ListBuffer<ModuleSymbol> results = new ListBuffer<>(); 269 Map<Name, Location> namesInSet = new HashMap<>(); 270 while (moduleLocationIterator.hasNext()) { 271 Set<Location> locns = (moduleLocationIterator.next()); 272 namesInSet.clear(); 273 for (Location l: locns) { 274 try { 275 Name n = names.fromString(fileManager.inferModuleName(l)); 276 if (namesInSet.put(n, l) == null) { 277 ModuleSymbol msym = syms.enterModule(n); 278 if (msym.sourceLocation != null || msym.classLocation != null) { 279 // module has already been found, so ignore this instance 280 continue; 281 } 282 if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) { 283 msym.sourceLocation = l; 284 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 285 msym.classLocation = fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, msym.name.toString()); 286 } 287 } else { 288 msym.classLocation = l; 289 } 290 if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES || 291 moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) { 292 msym.flags_field |= Flags.SYSTEM_MODULE; 293 } 294 if (toFind == msym || toFind == null) { 295 // Note: cannot return msym directly, because we must finish 296 // processing this set first 297 results.add(msym); 298 } 299 } else { 300 log.error(Errors.DuplicateModuleOnPath( 301 getDescription(moduleLocationIterator.outer), n)); 302 } 303 } catch (IOException e) { 304 // skip location for now? log error? 305 } 306 } 307 if (toFind != null && results.nonEmpty()) 308 return results.toList(); 309 } 310 311 return results.toList(); 312 } 313 314 private void findModuleInfo(ModuleSymbol msym) { 315 try { 316 JavaFileObject src_fo = (msym.sourceLocation == null) ? null 317 : fileManager.getJavaFileForInput(msym.sourceLocation, 318 names.module_info.toString(), Kind.SOURCE); 319 320 JavaFileObject class_fo = (msym.classLocation == null) ? null 321 : fileManager.getJavaFileForInput(msym.classLocation, 322 names.module_info.toString(), Kind.CLASS); 323 324 JavaFileObject fo = (src_fo == null) ? class_fo : 325 (class_fo == null) ? src_fo : 326 classFinder.preferredFileObject(src_fo, class_fo); 327 328 if (fo == null) { 329 String moduleName = msym.sourceLocation == null && msym.classLocation != null ? 330 fileManager.inferModuleName(msym.classLocation) : null; 331 if (moduleName != null) { 332 msym.module_info.classfile = null; 333 msym.flags_field |= Flags.AUTOMATIC_MODULE; 334 } else { 335 msym.kind = ERR; 336 } 337 } else { 338 msym.module_info.classfile = fo; 339 msym.module_info.completer = new Symbol.Completer() { 340 @Override 341 public void complete(Symbol sym) throws CompletionFailure { 342 classFinder.fillIn(msym.module_info); 343 } 344 @Override 345 public String toString() { 346 return "ModuleInfoCompleter"; 347 } 348 }; 349 } 350 } catch (IOException e) { 351 msym.kind = ERR; 352 } 353 } 354 355 Fragment getDescription(StandardLocation l) { 356 switch (l) { 357 case MODULE_PATH: return Fragments.LocnModule_path; 358 case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path; 359 case SYSTEM_MODULES: return Fragments.LocnSystem_modules; 360 case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path; 361 default: 362 throw new AssertionError(); 363 } 364 } 365 366 public interface ModuleInfoSourceFileCompleter { 367 public ModuleSymbol complete(JavaFileObject file); 368 } 369 370 }