blob: 1dc6fee34e6b64c18344bfa28916bb943c84d078 [file] [log] [blame]
/*
* Copyright (c) 2015, 2017, 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.ClassSymbol;
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.
*
* <p><b>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.</b>
*/
public class ModuleFinder {
/** The context key for the module finder. */
protected static final Context.Key<ModuleFinder> 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 final DeferredCompletionFailureHandler dcfh;
private ModuleNameReader moduleNameReader;
public ModuleNameFromSourceReader moduleNameFromSourceReader;
/** 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);
dcfh = DeferredCompletionFailureHandler.instance(context);
}
class ModuleLocationIterator implements Iterator<Set<Location>> {
StandardLocation outer;
Set<Location> next = null;
Iterator<StandardLocation> outerIter = Arrays.asList(
StandardLocation.MODULE_SOURCE_PATH,
StandardLocation.UPGRADE_MODULE_PATH,
StandardLocation.SYSTEM_MODULES,
StandardLocation.MODULE_PATH
).iterator();
Iterator<Set<Location>> 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<Location> next() {
hasNext();
if (next != null) {
Set<Location> 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<ModuleSymbol> 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<ModuleSymbol> findAllModules() {
List<ModuleSymbol> 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;
}
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 {
msym = readModule(fo);
}
if (msym.patchLocation == null) {
msym.classLocation = StandardLocation.CLASS_OUTPUT;
} else {
msym.patchOutputLocation = StandardLocation.CLASS_OUTPUT;
}
return msym;
} catch (IOException e) {
throw new Error(e); // FIXME
}
}
private ModuleSymbol readModule(JavaFileObject fo) throws IOException {
Name name;
switch (fo.getKind()) {
case SOURCE:
name = moduleNameFromSourceReader.readModuleName(fo);
if (name == null) {
JCDiagnostic diag =
diags.fragment(Fragments.FileDoesNotContainModule);
ClassSymbol errModuleInfo = syms.defineClass(names.module_info, syms.errModule);
throw new ClassFinder.BadClassFile(errModuleInfo, fo, diag, diags, dcfh);
}
break;
case CLASS:
try {
name = names.fromString(readModuleName(fo));
} catch (BadClassFile | IOException ex) {
//fillIn will report proper errors:
name = names.error;
}
break;
default:
Assert.error();
name = names.error;
break;
}
ModuleSymbol msym = syms.enterModule(name);
msym.module_info.classfile = fo;
if (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) && name != names.error) {
msym.patchLocation = fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH, name.toString());
if (msym.patchLocation != null) {
JavaFileObject patchFO = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), patchFO);
patchFO = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), patchFO);
if (patchFO != null) {
msym.module_info.classfile = patchFO;
}
}
}
msym.completer = Completer.NULL_COMPLETER;
classFinder.fillIn(msym.module_info);
return msym;
}
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 (location == null || !fileManager.hasLocation(location))
return null;
return fileManager.getJavaFileForInput(location,
names.module_info.toString(),
kind);
}
private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) {
ListBuffer<ModuleSymbol> results = new ListBuffer<>();
Map<Name, Location> namesInSet = new HashMap<>();
boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
while (moduleLocationIterator.hasNext()) {
Set<Location> 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 (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) &&
msym.patchLocation == null) {
msym.patchLocation =
fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH,
msym.name.toString());
if (msym.patchLocation != null &&
multiModuleMode &&
fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
msym.patchOutputLocation =
fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
msym.name.toString());
}
}
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 (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES ||
moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) {
msym.flags_field |= Flags.SYSTEM_MODULE;
}
if (toFind == null ||
(toFind == msym && (msym.sourceLocation != null || msym.classLocation != 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 fo;
fo = getModuleInfoFromLocation(msym.patchOutputLocation, Kind.CLASS);
fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.CLASS), fo);
fo = preferredFileObject(getModuleInfoFromLocation(msym.patchLocation, Kind.SOURCE), fo);
if (fo == null) {
fo = getModuleInfoFromLocation(msym.classLocation, Kind.CLASS);
fo = preferredFileObject(getModuleInfoFromLocation(msym.sourceLocation, Kind.SOURCE), 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;
}
}
private JavaFileObject preferredFileObject(JavaFileObject fo1, JavaFileObject fo2) {
if (fo1 == null) return fo2;
if (fo2 == null) return fo1;
return classFinder.preferredFileObject(fo1, fo2);
}
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 ModuleNameFromSourceReader {
public Name readModuleName(JavaFileObject file);
}
}