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 boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); 271 while (moduleLocationIterator.hasNext()) { 272 Set<Location> locns = (moduleLocationIterator.next()); 273 namesInSet.clear(); 274 for (Location l: locns) { 275 try { 276 Name n = names.fromString(fileManager.inferModuleName(l)); 277 if (namesInSet.put(n, l) == null) { 278 ModuleSymbol msym = syms.enterModule(n); 279 if (msym.sourceLocation != null || msym.classLocation != null) { 280 // module has already been found, so ignore this instance 281 continue; 282 } 283 if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && 284 msym.patchLocation == null) { 285 msym.patchLocation = 286 fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, 287 msym.name.toString()); 288 checkModuleInfoOnLocation(msym.patchLocation, Kind.CLASS, Kind.SOURCE); 289 if (msym.patchLocation != null && 290 multiModuleMode && 291 fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 292 msym.patchOutputLocation = 293 fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, 294 msym.name.toString()); 295 checkModuleInfoOnLocation(msym.patchOutputLocation, Kind.CLASS); 296 } 297 } 298 if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) { 299 if (msym.patchLocation == null) { 300 msym.sourceLocation = l; 301 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 302 msym.classLocation = 303 fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, 304 msym.name.toString()); 305 } 306 } 307 } else { 308 msym.classLocation = l; 309 } 310 if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES || 311 moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) { 312 msym.flags_field |= Flags.SYSTEM_MODULE; 313 } 314 if (toFind == null || 315 (toFind == msym && (msym.sourceLocation != null || msym.classLocation != null))) { 316 // Note: cannot return msym directly, because we must finish 317 // processing this set first 318 results.add(msym); 319 } 320 } else { 321 log.error(Errors.DuplicateModuleOnPath( 322 getDescription(moduleLocationIterator.outer), n)); 323 } 324 } catch (IOException e) { 325 // skip location for now? log error? 326 } 327 } 328 if (toFind != null && results.nonEmpty()) 329 return results.toList(); 330 } 331 332 return results.toList(); 333 } 334 335 private void checkModuleInfoOnLocation(Location location, Kind... kinds) throws IOException { 336 if (location == null) 337 return ; 338 339 for (Kind kind : kinds) { 340 JavaFileObject file = fileManager.getJavaFileForInput(location, 341 names.module_info.toString(), 342 kind); 343 if (file != null) { 344 log.error(Errors.LocnModuleInfoNotAllowedOnPatchPath(file)); 345 return ; 346 } 347 } 348 } 349 350 private void findModuleInfo(ModuleSymbol msym) { 351 try { 352 JavaFileObject src_fo = (msym.sourceLocation == null) ? null 353 : fileManager.getJavaFileForInput(msym.sourceLocation, 354 names.module_info.toString(), Kind.SOURCE); 355 356 JavaFileObject class_fo = (msym.classLocation == null) ? null 357 : fileManager.getJavaFileForInput(msym.classLocation, 358 names.module_info.toString(), Kind.CLASS); 359 360 JavaFileObject fo = (src_fo == null) ? class_fo : 361 (class_fo == null) ? src_fo : 362 classFinder.preferredFileObject(src_fo, class_fo); 363 364 if (fo == null) { 365 String moduleName = msym.sourceLocation == null && msym.classLocation != null ? 366 fileManager.inferModuleName(msym.classLocation) : null; 367 if (moduleName != null) { 368 msym.module_info.classfile = null; 369 msym.flags_field |= Flags.AUTOMATIC_MODULE; 370 } else { 371 msym.kind = ERR; 372 } 373 } else { 374 msym.module_info.classfile = fo; 375 msym.module_info.completer = new Symbol.Completer() { 376 @Override 377 public void complete(Symbol sym) throws CompletionFailure { 378 classFinder.fillIn(msym.module_info); 379 } 380 @Override 381 public String toString() { 382 return "ModuleInfoCompleter"; 383 } 384 }; 385 } 386 } catch (IOException e) { 387 msym.kind = ERR; 388 } 389 } 390 391 Fragment getDescription(StandardLocation l) { 392 switch (l) { 393 case MODULE_PATH: return Fragments.LocnModule_path; 394 case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path; 395 case SYSTEM_MODULES: return Fragments.LocnSystem_modules; 396 case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path; 397 default: 398 throw new AssertionError(); 399 } 400 } 401 402 public interface ModuleInfoSourceFileCompleter { 403 public ModuleSymbol complete(JavaFileObject file); 404 } 405 406 }