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 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.ClassSymbol; 43 import com.sun.tools.javac.code.Symbol.Completer; 44 import com.sun.tools.javac.code.Symbol.CompletionFailure; 45 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 46 import com.sun.tools.javac.jvm.ModuleNameReader; 47 import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile; 48 import com.sun.tools.javac.resources.CompilerProperties.Errors; 49 import com.sun.tools.javac.resources.CompilerProperties.Fragments; 50 import com.sun.tools.javac.util.Assert; 51 import com.sun.tools.javac.util.Context; 52 import com.sun.tools.javac.util.JCDiagnostic; 53 import com.sun.tools.javac.util.JCDiagnostic.Fragment; 54 import com.sun.tools.javac.util.List; 55 import com.sun.tools.javac.util.ListBuffer; 56 import com.sun.tools.javac.util.Log; 57 import com.sun.tools.javac.util.Name; 58 import com.sun.tools.javac.util.Names; 59 60 import static com.sun.tools.javac.code.Kinds.Kind.*; 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 86 /** Access to files 87 */ 88 private final JavaFileManager fileManager; 89 90 private final JCDiagnostic.Factory diags; 91 92 private ModuleNameReader moduleNameReader; 93 94 public ModuleNameFromSourceReader moduleNameFromSourceReader; 95 96 /** Get the ModuleFinder instance for this invocation. */ 97 public static ModuleFinder instance(Context context) { 98 ModuleFinder instance = context.get(moduleFinderKey); 99 if (instance == null) 100 instance = new ModuleFinder(context); 101 return instance; 102 } 103 104 /** Construct a new module finder. */ 105 protected ModuleFinder(Context context) { 106 context.put(moduleFinderKey, this); 107 names = Names.instance(context); 108 syms = Symtab.instance(context); 109 fileManager = context.get(JavaFileManager.class); 110 log = Log.instance(context); 111 classFinder = ClassFinder.instance(context); 112 113 diags = JCDiagnostic.Factory.instance(context); 114 } 115 116 class ModuleLocationIterator implements Iterator<Set<Location>> { 117 StandardLocation outer; 118 Set<Location> next = null; 119 120 Iterator<StandardLocation> outerIter = Arrays.asList( 121 StandardLocation.MODULE_SOURCE_PATH, 122 StandardLocation.UPGRADE_MODULE_PATH, 123 StandardLocation.SYSTEM_MODULES, 124 StandardLocation.MODULE_PATH 125 ).iterator(); 126 Iterator<Set<Location>> innerIter = null; 127 128 @Override 129 public boolean hasNext() { 130 while (next == null) { 131 while (innerIter == null || !innerIter.hasNext()) { 132 if (outerIter.hasNext()) { 133 outer = outerIter.next(); 134 try { 135 innerIter = fileManager.listLocationsForModules(outer).iterator(); 136 } catch (IOException e) { 137 System.err.println("error listing module locations for " + outer + ": " + e); // FIXME 138 } 139 } else 140 return false; 141 } 142 143 if (innerIter.hasNext()) 144 next = innerIter.next(); 145 } 146 return true; 147 } 148 149 @Override 150 public Set<Location> next() { 151 hasNext(); 152 if (next != null) { 153 Set<Location> result = next; 154 next = null; 155 return result; 156 } 157 throw new NoSuchElementException(); 158 } 159 160 } 161 162 ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator(); 163 164 public ModuleSymbol findModule(Name name) { 165 return findModule(syms.enterModule(name)); 166 } 167 168 public ModuleSymbol findModule(ModuleSymbol msym) { 169 if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) { 170 // fill in location 171 List<ModuleSymbol> list = scanModulePath(msym); 172 if (list.isEmpty()) { 173 msym.kind = ERR; 174 } 175 } 176 if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { 177 // fill in module-info 178 findModuleInfo(msym); 179 } 180 return msym; 181 } 182 183 public List<ModuleSymbol> findAllModules() { 184 List<ModuleSymbol> list = scanModulePath(null); 185 for (ModuleSymbol msym: list) { 186 if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { 187 // fill in module-info 188 findModuleInfo(msym); 189 } 190 } 191 return list; 192 } 193 194 public ModuleSymbol findSingleModule() { 195 try { 196 JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE); 197 JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS); 198 JavaFileObject fo = (src_fo == null) ? class_fo 199 : (class_fo == null) ? src_fo 200 : classFinder.preferredFileObject(src_fo, class_fo); 201 202 ModuleSymbol msym; 203 if (fo == null) { 204 msym = syms.unnamedModule; 205 } else { 206 msym = readModule(fo); 207 } 208 209 if (msym.patchLocation == null) { 210 msym.classLocation = StandardLocation.CLASS_OUTPUT; 211 } else { 212 msym.patchOutputLocation = StandardLocation.CLASS_OUTPUT; 213 } 214 return msym; 215 216 } catch (IOException e) { 217 throw new Error(e); // FIXME 218 } 219 } 220 221 private ModuleSymbol readModule(JavaFileObject fo) throws IOException { 222 Name name; 223 switch (fo.getKind()) { 224 case SOURCE: 225 name = moduleNameFromSourceReader.readModuleName(fo); 226 if (name == null) { 227 JCDiagnostic diag = 228 diags.fragment(Fragments.FileDoesNotContainModule); 229 ClassSymbol errModuleInfo = syms.defineClass(names.module_info, syms.errModule); 230 throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags); 231 } 232 break; 233 case CLASS: 234 try { 235 name = names.fromString(readModuleName(fo)); 236 } catch (BadClassFile | IOException ex) { 237 //fillIn will report proper errors: 238 name = names.error; 239 } 240 break; 241 default: 242 Assert.error(); 243 name = names.error; 244 break; 245 } 246 247 ModuleSymbol msym = syms.enterModule(name); 248 msym.module_info.classfile = fo; 249 if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && name != names.error) { 250 msym.patchLocation = fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, name.toString()); 251 252 if (msym.patchLocation != null) { 253 JavaFileObject patchFO = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS); 254 patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), patchFO); 255 patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), patchFO); 256 257 if (patchFO != null) { 258 msym.module_info.classfile = patchFO; 259 } 260 } 261 } 262 263 msym.completer = Completer.NULL_COMPLETER; 264 classFinder.fillIn(msym.module_info); 265 266 return msym; 267 } 268 269 private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile { 270 if (moduleNameReader == null) 271 moduleNameReader = new ModuleNameReader(); 272 return moduleNameReader.readModuleName(jfo); 273 } 274 275 private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException { 276 if (location == null || !fileManager.hasLocation(location)) 277 return null; 278 279 return fileManager.getJavaFileForInput(location, 280 names.module_info.toString(), 281 kind); 282 } 283 284 private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) { 285 ListBuffer<ModuleSymbol> results = new ListBuffer<>(); 286 Map<Name, Location> namesInSet = new HashMap<>(); 287 boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH); 288 while (moduleLocationIterator.hasNext()) { 289 Set<Location> locns = (moduleLocationIterator.next()); 290 namesInSet.clear(); 291 for (Location l: locns) { 292 try { 293 Name n = names.fromString(fileManager.inferModuleName(l)); 294 if (namesInSet.put(n, l) == null) { 295 ModuleSymbol msym = syms.enterModule(n); 296 if (msym.sourceLocation != null || msym.classLocation != null) { 297 // module has already been found, so ignore this instance 298 continue; 299 } 300 if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && 301 msym.patchLocation == null) { 302 msym.patchLocation = 303 fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, 304 msym.name.toString()); 305 if (msym.patchLocation != null && 306 multiModuleMode && 307 fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 308 msym.patchOutputLocation = 309 fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, 310 msym.name.toString()); 311 } 312 } 313 if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) { 314 msym.sourceLocation = l; 315 if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { 316 msym.classLocation = 317 fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, 318 msym.name.toString()); 319 } 320 } else { 321 msym.classLocation = l; 322 } 323 if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES || 324 moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) { 325 msym.flags_field |= Flags.SYSTEM_MODULE; 326 } 327 if (toFind == null || 328 (toFind == msym && (msym.sourceLocation != null || msym.classLocation != null))) { 329 // Note: cannot return msym directly, because we must finish 330 // processing this set first 331 results.add(msym); 332 } 333 } else { 334 log.error(Errors.DuplicateModuleOnPath( 335 getDescription(moduleLocationIterator.outer), n)); 336 } 337 } catch (IOException e) { 338 // skip location for now? log error? 339 } 340 } 341 if (toFind != null && results.nonEmpty()) 342 return results.toList(); 343 } 344 345 return results.toList(); 346 } 347 348 private void findModuleInfo(ModuleSymbol msym) { 349 try { 350 JavaFileObject fo; 351 352 fo = getModuleInfoFromLocation(msym.patchOutputLocation, Kind.CLASS); 353 fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), fo); 354 fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), fo); 355 356 if (fo == null) { 357 fo = getModuleInfoFromLocation(msym.classLocation, Kind.CLASS); 358 fo = preferredFileObject(getModuleInfoFromLocation(msym.sourceLocation, Kind.SOURCE), fo); 359 } 360 361 if (fo == null) { 362 String moduleName = msym.sourceLocation == null && msym.classLocation != null ? 363 fileManager.inferModuleName(msym.classLocation) : null; 364 if (moduleName != null) { 365 msym.module_info.classfile = null; 366 msym.flags_field |= Flags.AUTOMATIC_MODULE; 367 } else { 368 msym.kind = ERR; 369 } 370 } else { 371 msym.module_info.classfile = fo; 372 msym.module_info.completer = new Symbol.Completer() { 373 @Override 374 public void complete(Symbol sym) throws CompletionFailure { 375 classFinder.fillIn(msym.module_info); 376 } 377 @Override 378 public String toString() { 379 return "ModuleInfoCompleter"; 380 } 381 }; 382 } 383 } catch (IOException e) { 384 msym.kind = ERR; 385 } 386 } 387 388 private JavaFileObject preferredFileObject(JavaFileObject fo1, JavaFileObject fo2) { 389 if (fo1 == null) return fo2; 390 if (fo2 == null) return fo1; 391 return classFinder.preferredFileObject(fo1, fo2); 392 } 393 394 Fragment getDescription(StandardLocation l) { 395 switch (l) { 396 case MODULE_PATH: return Fragments.LocnModule_path; 397 case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path; 398 case SYSTEM_MODULES: return Fragments.LocnSystem_modules; 399 case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path; 400 default: 401 throw new AssertionError(); 402 } 403 } 404 405 public interface ModuleNameFromSourceReader { 406 public Name readModuleName(JavaFileObject file); 407 } 408 409 }