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