8059349: Public API scanning should be implemented in the form of a TaskListener

Replaces JavaCompilerWithDeps with a TaskListener.

Reviewed-by: jfranck
This commit is contained in:
Andreas Lundblad 2014-10-07 21:21:42 +02:00
parent 96074068b8
commit 0545e4b847
6 changed files with 257 additions and 145 deletions

View file

@ -1228,12 +1228,6 @@ public class JavaCompiler {
attr.postAttr(env.tree);
}
compileStates.put(env, CompileState.ATTR);
if (rootClasses != null && rootClasses.contains(env.enclClass)) {
// This was a class that was explicitly supplied for compilation.
// If we want to capture the public api of this class,
// then now is a good time to do it.
reportPublicApi(env.enclClass.sym);
}
}
finally {
log.useSource(prev);
@ -1242,14 +1236,6 @@ public class JavaCompiler {
return env;
}
/** Report the public api of a class that was supplied explicitly for compilation,
* for example on the command line to javac.
* @param sym The symbol of the class.
*/
public void reportPublicApi(ClassSymbol sym) {
// Override to collect the reported public api.
}
/**
* Perform dataflow checks on attributed parse trees.
* These include checks for definite assignment and unreachable statements.

View file

@ -1,107 +0,0 @@
/*
* Copyright (c) 2012, 2014, 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.sjavac.comp;
import java.util.StringTokenizer;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
/** Subclass to Resolve that overrides collect.
*
* <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 JavaCompilerWithDeps extends JavaCompiler {
/** The dependency database
*/
protected Dependencies deps;
protected SjavacErrorHandler errorHandler;
public JavaCompilerWithDeps(Context context, SjavacErrorHandler eh) {
super(context);
deps = Dependencies.instance(context);
errorHandler = eh;
needRootClasses = true;
}
public static void preRegister(Context context, final SjavacErrorHandler eh) {
context.put(compilerKey, new Context.Factory<JavaCompiler>() {
public JavaCompiler make(Context c) {
JavaCompiler instance = new JavaCompilerWithDeps(c, eh);
c.put(JavaCompiler.class, instance);
return instance;
}
});
}
/** Collect the public apis of classes supplied explicitly for compilation.
* @param sym The class to visit.
*/
@Override
public void reportPublicApi(ClassSymbol sym) {
// The next test will catch when source files are located in the wrong directory!
// This ought to be moved into javac as a new warning, or perhaps as part
// of the auxiliary class warning.
// For example if sun.swing.BeanInfoUtils
// is in fact stored in: /mybuild/jdk/gensrc/javax/swing/beaninfo/BeanInfoUtils.java
// We do not need to test that BeanInfoUtils is stored in a file named BeanInfoUtils
// since this is checked earlier.
if (sym.sourcefile != null) {
// Rewrite sun.swing.BeanInfoUtils into /sun/swing/
StringBuilder pathb = new StringBuilder();
StringTokenizer qn = new StringTokenizer(sym.packge().toString(), ".");
boolean first = true;
while (qn.hasMoreTokens()) {
String o = qn.nextToken();
pathb.append("/");
pathb.append(o);
first = false;
}
pathb.append("/");
String path = pathb.toString();
// Now cut the uri to be: file:///mybuild/jdk/gensrc/javax/swing/beaninfo/
String p = sym.sourcefile.toUri().getPath();
// Do not use File.separatorChar here, a URI always uses slashes /.
int i = p.lastIndexOf("/");
String pp = p.substring(0,i+1);
// Now check if the truncated uri ends with the path. (It does not == failure!)
if (path.length() > 0 && !path.equals("/unnamed package/") && !pp.endsWith(path)) {
errorHandler.logError("Error: The source file "+sym.sourcefile.getName()+
" is located in the wrong package directory, because it contains the class "+
sym.getQualifiedName());
}
}
deps.visitPubapi(sym);
}
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2014, 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.sjavac.comp;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.tools.JavaFileObject;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.Name;
public class PathAndPackageVerifier implements TaskListener {
// Stores the set of compilation units whose source file path does not
// match the package declaration.
Set<CompilationUnitTree> misplacedCompilationUnits = new HashSet<>();
@Override
@DefinedBy(Api.COMPILER_TREE)
public void started(TaskEvent e) {
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public void finished(TaskEvent e) {
if (e.getKind() != TaskEvent.Kind.ANALYZE)
return;
CompilationUnitTree cu = e.getCompilationUnit();
if (cu == null)
return;
JavaFileObject jfo = cu.getSourceFile();
if (jfo == null)
return; // No source file -> package doesn't matter
JCTree pkg = (JCTree) cu.getPackageName();
if (pkg == null)
return; // Default package. See JDK-8048144.
Path dir = Paths.get(jfo.toUri()).normalize().getParent();
if (!checkPathAndPackage(dir, pkg))
misplacedCompilationUnits.add(cu);
}
/* Returns true if dir matches pkgName.
*
* Examples:
* (a/b/c, a.b.c) gives true
* (i/j/k, i.x.k) gives false
*
* Currently (x/a/b/c, a.b.c) also gives true. See JDK-8059598.
*/
private boolean checkPathAndPackage(Path dir, JCTree pkgName) {
Iterator<String> pathIter = new ParentIterator(dir);
Iterator<String> pkgIter = new EnclosingPkgIterator(pkgName);
while (pathIter.hasNext() && pkgIter.hasNext()) {
if (!pathIter.next().equals(pkgIter.next()))
return false;
}
return !pkgIter.hasNext(); /*&& !pathIter.hasNext() See JDK-8059598 */
}
public Set<CompilationUnitTree> getMisplacedCompilationUnits() {
return misplacedCompilationUnits;
}
/* Iterates over the names of the parents of the given path:
* Example: dir1/dir2/dir3 results in dir3 -> dir2 -> dir1
*/
private static class ParentIterator implements Iterator<String> {
Path next;
ParentIterator(Path initial) {
next = initial;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public String next() {
String tmp = next.getFileName().toString();
next = next.getParent();
return tmp;
}
}
/* Iterates over the names of the enclosing packages:
* Example: pkg1.pkg2.pkg3 results in pkg3 -> pkg2 -> pkg1
*/
private static class EnclosingPkgIterator implements Iterator<String> {
JCTree next;
EnclosingPkgIterator(JCTree initial) {
next = initial;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public String next() {
Name name;
if (next instanceof JCIdent) {
name = ((JCIdent) next).name;
next = null;
} else {
JCFieldAccess fa = (JCFieldAccess) next;
name = fa.name;
next = fa.selected;
}
return name.toString();
}
}
}

View file

@ -28,22 +28,25 @@ import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.api.JavacTool;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.PackageSymbol;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Options;
import com.sun.tools.sjavac.Util;
import com.sun.tools.sjavac.comp.dependencies.DependencyCollector;
import com.sun.tools.sjavac.comp.dependencies.PublicApiCollector;
import com.sun.tools.sjavac.server.CompilationResult;
import com.sun.tools.sjavac.server.Sjavac;
import com.sun.tools.sjavac.server.SysInfo;
@ -72,18 +75,10 @@ public class SjavacImpl implements Sjavac {
List<File> explicitSources,
Set<URI> sourcesToCompile,
Set<URI> visibleSources) {
final AtomicBoolean forcedExit = new AtomicBoolean();
JavacTool compiler = JavacTool.create();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
SmartFileManager smartFileManager = new SmartFileManager(fileManager);
Context context = new Context();
JavaCompilerWithDeps.preRegister(context, new SjavacErrorHandler() {
@Override
public void logError(String msg) {
forcedExit.set(true);
}
});
// Now setup the actual compilation....
CompilationResult compilationResult = new CompilationResult(0);
@ -101,8 +96,6 @@ public class SjavacImpl implements Sjavac {
for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
compilationUnits.append(i);
}
forcedExit.set(false);
// Create a new logger.
StringWriter stdoutLog = new StringWriter();
@ -111,6 +104,8 @@ public class SjavacImpl implements Sjavac {
PrintWriter stderr = new PrintWriter(stderrLog);
com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
DependencyCollector depsCollector = new DependencyCollector();
PublicApiCollector pubApiCollector = new PublicApiCollector();
PathAndPackageVerifier papVerifier = new PathAndPackageVerifier();
try {
if (compilationUnits.size() > 0) {
smartFileManager.setVisibleSources(visibleSources);
@ -128,12 +123,14 @@ public class SjavacImpl implements Sjavac {
context);
smartFileManager.setSymbolFileEnabled(!Options.instance(context).isSet("ignore.symbol.file"));
task.addTaskListener(depsCollector);
task.addTaskListener(pubApiCollector);
task.addTaskListener(papVerifier);
rc = task.doCall();
smartFileManager.flush();
}
} catch (Exception e) {
stderrLog.append(Util.getStackTrace(e));
forcedExit.set(true);
rc = com.sun.tools.javac.main.Main.Result.ERROR;
}
compilationResult.packageArtifacts = smartFileManager.getPackageArtifacts();
@ -144,13 +141,23 @@ public class SjavacImpl implements Sjavac {
deps.collect(from.fullname, to.fullname);
}
for (ClassSymbol cs : pubApiCollector.getClassSymbols())
deps.visitPubapi(cs);
if (papVerifier.getMisplacedCompilationUnits().size() > 0) {
for (CompilationUnitTree cu : papVerifier.getMisplacedCompilationUnits()) {
System.err.println("Misplaced compilation unit.");
System.err.println(" Directory: " + Paths.get(cu.getSourceFile().toUri()).getParent());
System.err.println(" Package: " + cu.getPackageName());
}
rc = com.sun.tools.javac.main.Main.Result.ERROR;
}
compilationResult.packageDependencies = deps.getDependencies();
compilationResult.packagePubapis = deps.getPubapis();
compilationResult.stdout = stdoutLog.toString();
compilationResult.stderr = stderrLog.toString();
compilationResult.returnCode = rc.exitCode == 0 && forcedExit.get() ? -1 : rc.exitCode;
compilationResult.returnCode = rc.exitCode;
return compilationResult;
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2014, 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.sjavac.comp.dependencies;
import java.util.HashSet;
import java.util.Set;
import com.sun.source.tree.Tree;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
public class PublicApiCollector implements TaskListener {
final Set<ClassSymbol> classSymbols = new HashSet<>();
@Override
@DefinedBy(Api.COMPILER_TREE)
public void started(TaskEvent e) {
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ANALYZE) {
for (Tree t : e.getCompilationUnit().getTypeDecls()) {
if (t instanceof JCClassDecl) // Can also be a JCSkip
classSymbols.add(((JCClassDecl) t).sym);
}
}
}
public Set<ClassSymbol> getClassSymbols() {
return classSymbols;
}
}

View file

@ -23,14 +23,32 @@
* questions.
*/
package com.sun.tools.sjavac.comp;
/**
* <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>
/*
* @test
* @bug 8059349
* @summary This test makes sure file paths matches package declarations
* @library /tools/lib
* @build Wrapper ToolBox
* @run main Wrapper PackagePathMismatch
*/
public interface SjavacErrorHandler {
void logError(String msg);
import java.nio.file.Path;
import java.nio.file.Paths;
public class PackagePathMismatch extends SjavacBase {
public static void main(String... args) throws Exception {
Path root = Paths.get(PackagePathMismatch.class.getSimpleName() + "Test");
Path src = root.resolve("src");
Path classes = root.resolve("classes");
toolbox.writeFile(src.resolve("a/x/c/Test.java"),
"package a.b.c; class Test { }");
// Compile should fail since package a.b.c does not match path a/x/c.
String server = "--server:portfile=testserver,background=false";
int rc1 = compile(server, "-d", classes, src);
if (rc1 == 0)
throw new AssertionError("Compilation succeeded unexpectedly");
}
}