/* * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.code; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.function.Function; import javax.tools.JavaFileManager; import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.StandardLocation; import com.sun.tools.javac.code.Symbol.Completer; import com.sun.tools.javac.code.Symbol.CompletionFailure; import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.jvm.ModuleNameReader; import com.sun.tools.javac.jvm.ModuleNameReader.BadClassFile; import com.sun.tools.javac.resources.CompilerProperties.Errors; import com.sun.tools.javac.resources.CompilerProperties.Fragments; import com.sun.tools.javac.util.Assert; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.Fragment; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import static com.sun.tools.javac.code.Kinds.Kind.*; /** * This class provides operations to locate module definitions * from the source and class files on the paths provided to javac. * *

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice. */ public class ModuleFinder { /** The context key for the module finder. */ protected static final Context.Key moduleFinderKey = new Context.Key<>(); /** The log to use for verbose output. */ private final Log log; /** The symbol table. */ private final Symtab syms; /** The name table. */ private final Names names; private final ClassFinder classFinder; /** Access to files */ private final JavaFileManager fileManager; private final JCDiagnostic.Factory diags; private ModuleNameReader moduleNameReader; public ModuleInfoSourceFileCompleter sourceFileCompleter; /** Get the ModuleFinder instance for this invocation. */ public static ModuleFinder instance(Context context) { ModuleFinder instance = context.get(moduleFinderKey); if (instance == null) instance = new ModuleFinder(context); return instance; } /** Construct a new module finder. */ protected ModuleFinder(Context context) { context.put(moduleFinderKey, this); names = Names.instance(context); syms = Symtab.instance(context); fileManager = context.get(JavaFileManager.class); log = Log.instance(context); classFinder = ClassFinder.instance(context); diags = JCDiagnostic.Factory.instance(context); } class ModuleLocationIterator implements Iterator> { StandardLocation outer; Set next = null; Iterator outerIter = Arrays.asList( StandardLocation.MODULE_SOURCE_PATH, StandardLocation.UPGRADE_MODULE_PATH, StandardLocation.SYSTEM_MODULES, StandardLocation.MODULE_PATH ).iterator(); Iterator> innerIter = null; @Override public boolean hasNext() { while (next == null) { while (innerIter == null || !innerIter.hasNext()) { if (outerIter.hasNext()) { outer = outerIter.next(); try { innerIter = fileManager.listLocationsForModules(outer).iterator(); } catch (IOException e) { System.err.println("error listing module locations for " + outer + ": " + e); // FIXME } } else return false; } if (innerIter.hasNext()) next = innerIter.next(); } return true; } @Override public Set next() { hasNext(); if (next != null) { Set result = next; next = null; return result; } throw new NoSuchElementException(); } } ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator(); public ModuleSymbol findModule(Name name) { return findModule(syms.enterModule(name)); } public ModuleSymbol findModule(ModuleSymbol msym) { if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) { // fill in location List list = scanModulePath(msym); if (list.isEmpty()) { msym.kind = ERR; } } if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { // fill in module-info findModuleInfo(msym); } return msym; } public List findAllModules() { List list = scanModulePath(null); for (ModuleSymbol msym: list) { if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) { // fill in module-info findModuleInfo(msym); } } return list; } private boolean inFindSingleModule; public ModuleSymbol findSingleModule() { try { JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE); JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS); JavaFileObject fo = (src_fo == null) ? class_fo : (class_fo == null) ? src_fo : classFinder.preferredFileObject(src_fo, class_fo); ModuleSymbol msym; if (fo == null) { msym = syms.unnamedModule; } else { switch (fo.getKind()) { case SOURCE: if (!inFindSingleModule) { try { inFindSingleModule = true; // Note: the following will trigger a re-entrant call to Modules.enter msym = sourceFileCompleter.complete(fo); msym.module_info.classfile = fo; } finally { inFindSingleModule = false; } } else { //the module-info.java does not contain a module declaration, //avoid infinite recursion: msym = syms.unnamedModule; } break; case CLASS: Name name; try { name = names.fromString(readModuleName(fo)); } catch (BadClassFile | IOException ex) { //fillIn will report proper errors: name = names.error; } msym = syms.enterModule(name); msym.module_info.classfile = fo; msym.completer = Completer.NULL_COMPLETER; classFinder.fillIn(msym.module_info); break; default: Assert.error(); msym = syms.unnamedModule; break; } } msym.classLocation = StandardLocation.CLASS_OUTPUT; return msym; } catch (IOException e) { throw new Error(e); // FIXME } } private String readModuleName(JavaFileObject jfo) throws IOException, ModuleNameReader.BadClassFile { if (moduleNameReader == null) moduleNameReader = new ModuleNameReader(); return moduleNameReader.readModuleName(jfo); } private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException { if (!fileManager.hasLocation(location)) return null; return fileManager.getJavaFileForInput(location, names.module_info.toString(), kind); } private List scanModulePath(ModuleSymbol toFind) { ListBuffer results = new ListBuffer<>(); Map namesInSet = new HashMap<>(); while (moduleLocationIterator.hasNext()) { Set locns = (moduleLocationIterator.next()); namesInSet.clear(); for (Location l: locns) { try { Name n = names.fromString(fileManager.inferModuleName(l)); if (namesInSet.put(n, l) == null) { ModuleSymbol msym = syms.enterModule(n); if (msym.sourceLocation != null || msym.classLocation != null) { // module has already been found, so ignore this instance continue; } if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) { msym.sourceLocation = l; if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) { msym.classLocation = fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT, msym.name.toString()); } } else { msym.classLocation = l; } if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) { msym.patchLocation = fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, msym.name.toString()); } if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES || moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) { msym.flags_field |= Flags.SYSTEM_MODULE; } if (toFind == msym || toFind == null) { // Note: cannot return msym directly, because we must finish // processing this set first results.add(msym); } } else { log.error(Errors.DuplicateModuleOnPath( getDescription(moduleLocationIterator.outer), n)); } } catch (IOException e) { // skip location for now? log error? } } if (toFind != null && results.nonEmpty()) return results.toList(); } return results.toList(); } private void findModuleInfo(ModuleSymbol msym) { try { JavaFileObject src_fo = (msym.sourceLocation == null) ? null : fileManager.getJavaFileForInput(msym.sourceLocation, names.module_info.toString(), Kind.SOURCE); JavaFileObject class_fo = (msym.classLocation == null) ? null : fileManager.getJavaFileForInput(msym.classLocation, names.module_info.toString(), Kind.CLASS); JavaFileObject fo = (src_fo == null) ? class_fo : (class_fo == null) ? src_fo : classFinder.preferredFileObject(src_fo, class_fo); if (fo == null) { String moduleName = msym.sourceLocation == null && msym.classLocation != null ? fileManager.inferModuleName(msym.classLocation) : null; if (moduleName != null) { msym.module_info.classfile = null; msym.flags_field |= Flags.AUTOMATIC_MODULE; } else { msym.kind = ERR; } } else { msym.module_info.classfile = fo; msym.module_info.completer = new Symbol.Completer() { @Override public void complete(Symbol sym) throws CompletionFailure { classFinder.fillIn(msym.module_info); } @Override public String toString() { return "ModuleInfoCompleter"; } }; } } catch (IOException e) { msym.kind = ERR; } } Fragment getDescription(StandardLocation l) { switch (l) { case MODULE_PATH: return Fragments.LocnModule_path; case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path; case SYSTEM_MODULES: return Fragments.LocnSystem_modules; case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path; default: throw new AssertionError(); } } public interface ModuleInfoSourceFileCompleter { public ModuleSymbol complete(JavaFileObject file); } }