mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
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:
parent
96074068b8
commit
0545e4b847
6 changed files with 257 additions and 145 deletions
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue