org.openjdk.tools.javac.code.ModuleFinder Maven / Gradle / Ivy
Show all versions of javac-shaded Show documentation
/*
* 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 org.openjdk.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 org.openjdk.tools.javac.code.Symbol.Completer;
import org.openjdk.tools.javac.code.Symbol.CompletionFailure;
import org.openjdk.tools.javac.code.Symbol.ModuleSymbol;
import org.openjdk.tools.javac.jvm.ModuleNameReader;
import org.openjdk.tools.javac.jvm.ModuleNameReader.BadClassFile;
import org.openjdk.tools.javac.resources.CompilerProperties.Errors;
import org.openjdk.tools.javac.resources.CompilerProperties.Fragments;
import org.openjdk.tools.javac.util.Assert;
import org.openjdk.tools.javac.util.Context;
import org.openjdk.tools.javac.util.JCDiagnostic;
import org.openjdk.tools.javac.util.JCDiagnostic.Fragment;
import org.openjdk.tools.javac.util.List;
import org.openjdk.tools.javac.util.ListBuffer;
import org.openjdk.tools.javac.util.Log;
import org.openjdk.tools.javac.util.Name;
import org.openjdk.tools.javac.util.Names;
import static org.openjdk.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<>();
boolean multiModuleMode = fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH);
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 (fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH) &&
msym.patchLocation == null) {
msym.patchLocation =
fileManager.getLocationForModule(StandardLocation.PATCH_MODULE_PATH,
msym.name.toString());
checkModuleInfoOnLocation(msym.patchLocation, Kind.CLASS, Kind.SOURCE);
if (msym.patchLocation != null &&
multiModuleMode &&
fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
msym.patchOutputLocation =
fileManager.getLocationForModule(StandardLocation.CLASS_OUTPUT,
msym.name.toString());
checkModuleInfoOnLocation(msym.patchOutputLocation, Kind.CLASS);
}
}
if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) {
if (msym.patchLocation == null) {
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 checkModuleInfoOnLocation(Location location, Kind... kinds) throws IOException {
if (location == null)
return ;
for (Kind kind : kinds) {
JavaFileObject file = fileManager.getJavaFileForInput(location,
names.module_info.toString(),
kind);
if (file != null) {
log.error(Errors.LocnModuleInfoNotAllowedOnPatchPath(file));
return;
}
}
}
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);
}
}