6960424: new option -Xpkginfo for better control of when package-info.class is generated

Reviewed-by: mcimadamore
This commit is contained in:
Jonathan Gibbons 2010-08-25 11:40:25 -07:00
parent 0e1661c2f1
commit 37fcc133f6
9 changed files with 255 additions and 32 deletions

View File

@ -295,4 +295,11 @@ public abstract class Attribute implements AnnotationValue {
void visitEnum(Attribute.Enum e);
void visitError(Attribute.Error e);
}
/** A mirror of java.lang.annotation.RetentionPolicy. */
public static enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
}

View File

@ -32,6 +32,7 @@ import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.jvm.ClassReader;
import com.sun.tools.javac.code.Attribute.RetentionPolicy;
import com.sun.tools.javac.comp.Check;
import static com.sun.tools.javac.code.Type.*;
@ -3554,4 +3555,24 @@ public class Types {
public Type visitType(Type t, S s) { return t; }
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Annotation support">
public RetentionPolicy getRetention(Attribute.Compound a) {
RetentionPolicy vis = RetentionPolicy.CLASS; // the default
Attribute.Compound c = a.type.tsym.attribute(syms.retentionType.tsym);
if (c != null) {
Attribute value = c.member(names.value);
if (value != null && value instanceof Attribute.Enum) {
Name levelName = ((Attribute.Enum)value).value.name;
if (levelName == names.SOURCE) vis = RetentionPolicy.SOURCE;
else if (levelName == names.CLASS) vis = RetentionPolicy.CLASS;
else if (levelName == names.RUNTIME) vis = RetentionPolicy.RUNTIME;
else ;// /* fail soft */ throw new AssertionError(levelName);
}
}
return vis;
}
// </editor-fold>
}

View File

@ -38,6 +38,7 @@ import com.sun.tools.javac.util.List;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.main.RecognizedOptions.PkgInfo;
import com.sun.tools.javac.tree.JCTree.*;
import static com.sun.tools.javac.code.Flags.*;
@ -102,6 +103,7 @@ public class Enter extends JCTree.Visitor {
Lint lint;
Names names;
JavaFileManager fileManager;
PkgInfo pkginfoOpt;
private final Todo todo;
@ -132,6 +134,9 @@ public class Enter extends JCTree.Visitor {
predefClassDef.sym = syms.predefClass;
todo = Todo.instance(context);
fileManager = context.get(JavaFileManager.class);
Options options = Options.instance(context);
pkginfoOpt = PkgInfo.get(options);
}
/** A hashtable mapping classes and packages to the environments current
@ -278,7 +283,7 @@ public class Enter extends JCTree.Visitor {
JavaFileObject.Kind.SOURCE);
if (tree.pid != null) {
tree.packge = reader.enterPackage(TreeInfo.fullName(tree.pid));
if (tree.packageAnnotations.nonEmpty()) {
if (tree.packageAnnotations.nonEmpty() || pkginfoOpt == PkgInfo.ALWAYS) {
if (isPkgInfo) {
addEnv = true;
} else {

View File

@ -29,6 +29,7 @@ import java.util.*;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.main.RecognizedOptions.PkgInfo;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@ -82,6 +83,7 @@ public class Lower extends TreeTranslator {
private final Name classDollar;
private Types types;
private boolean debugLower;
private PkgInfo pkginfoOpt;
protected Lower(Context context) {
context.put(lowerKey, this);
@ -106,6 +108,7 @@ public class Lower extends TreeTranslator {
types = Types.instance(context);
Options options = Options.instance(context);
debugLower = options.get("debuglower") != null;
pkginfoOpt = PkgInfo.get(options);
}
/** The currently enclosing class.
@ -2161,7 +2164,7 @@ public class Lower extends TreeTranslator {
}
public void visitTopLevel(JCCompilationUnit tree) {
if (tree.packageAnnotations.nonEmpty()) {
if (needPackageInfoClass(tree)) {
Name name = names.package_info;
long flags = Flags.ABSTRACT | Flags.INTERFACE;
if (target.isPackageInfoSynthetic())
@ -2183,6 +2186,23 @@ public class Lower extends TreeTranslator {
translated.append(packageAnnotationsClass);
}
}
// where
private boolean needPackageInfoClass(JCCompilationUnit tree) {
switch (pkginfoOpt) {
case ALWAYS:
return true;
case LEGACY:
return tree.packageAnnotations.nonEmpty();
case NONEMPTY:
for (Attribute.Compound a: tree.packge.attributes_field) {
Attribute.RetentionPolicy p = types.getRetention(a);
if (p != Attribute.RetentionPolicy.SOURCE)
return true;
}
return false;
}
throw new AssertionError();
}
public void visitClassDef(JCClassDecl tree) {
ClassSymbol currentClassPrev = currentClass;

View File

@ -34,6 +34,7 @@ import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Attribute.RetentionPolicy;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.file.BaseFileObject;
@ -692,7 +693,7 @@ public class ClassWriter extends ClassFile {
boolean hasInvisible = false;
if (m.params != null) for (VarSymbol s : m.params) {
for (Attribute.Compound a : s.getAnnotationMirrors()) {
switch (getRetention(a.type.tsym)) {
switch (types.getRetention(a)) {
case SOURCE: break;
case CLASS: hasInvisible = true; break;
case RUNTIME: hasVisible = true; break;
@ -708,7 +709,7 @@ public class ClassWriter extends ClassFile {
for (VarSymbol s : m.params) {
ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>();
for (Attribute.Compound a : s.getAnnotationMirrors())
if (getRetention(a.type.tsym) == RetentionPolicy.RUNTIME)
if (types.getRetention(a) == RetentionPolicy.RUNTIME)
buf.append(a);
databuf.appendChar(buf.length());
for (Attribute.Compound a : buf)
@ -723,7 +724,7 @@ public class ClassWriter extends ClassFile {
for (VarSymbol s : m.params) {
ListBuffer<Attribute.Compound> buf = new ListBuffer<Attribute.Compound>();
for (Attribute.Compound a : s.getAnnotationMirrors())
if (getRetention(a.type.tsym) == RetentionPolicy.CLASS)
if (types.getRetention(a) == RetentionPolicy.CLASS)
buf.append(a);
databuf.appendChar(buf.length());
for (Attribute.Compound a : buf)
@ -747,7 +748,7 @@ public class ClassWriter extends ClassFile {
ListBuffer<Attribute.Compound> visibles = new ListBuffer<Attribute.Compound>();
ListBuffer<Attribute.Compound> invisibles = new ListBuffer<Attribute.Compound>();
for (Attribute.Compound a : attrs) {
switch (getRetention(a.type.tsym)) {
switch (types.getRetention(a)) {
case SOURCE: break;
case CLASS: invisibles.append(a); break;
case RUNTIME: visibles.append(a); break;
@ -785,7 +786,7 @@ public class ClassWriter extends ClassFile {
if (tc.position.type == TargetType.UNKNOWN
|| !tc.position.emitToClassfile())
continue;
switch (getRetention(tc.type.tsym)) {
switch (types.getRetention(tc)) {
case SOURCE: break;
case CLASS: invisibles.append(tc); break;
case RUNTIME: visibles.append(tc); break;
@ -815,29 +816,6 @@ public class ClassWriter extends ClassFile {
return attrCount;
}
/** A mirror of java.lang.annotation.RetentionPolicy. */
enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
RetentionPolicy getRetention(TypeSymbol annotationType) {
RetentionPolicy vis = RetentionPolicy.CLASS; // the default
Attribute.Compound c = annotationType.attribute(syms.retentionType.tsym);
if (c != null) {
Attribute value = c.member(names.value);
if (value != null && value instanceof Attribute.Enum) {
Name levelName = ((Attribute.Enum)value).value.name;
if (levelName == names.SOURCE) vis = RetentionPolicy.SOURCE;
else if (levelName == names.CLASS) vis = RetentionPolicy.CLASS;
else if (levelName == names.RUNTIME) vis = RetentionPolicy.RUNTIME;
else ;// /* fail soft */ throw new AssertionError(levelName);
}
}
return vis;
}
/** A visitor to write an attribute including its leading
* single-character marker.
*/

View File

@ -80,6 +80,7 @@ public enum OptionName {
XMAXERRS("-Xmaxerrs"),
XMAXWARNS("-Xmaxwarns"),
XSTDOUT("-Xstdout"),
XPKGINFO("-Xpkginfo:"),
XPRINT("-Xprint"),
XPRINTROUNDS("-XprintRounds"),
XPRINTPROCESSORINFO("-XprintProcessorInfo"),

View File

@ -160,6 +160,7 @@ public class RecognizedOptions {
XMAXERRS,
XMAXWARNS,
XSTDOUT,
XPKGINFO,
XPRINT,
XPRINTROUNDS,
XPRINTPROCESSORINFO,
@ -217,6 +218,7 @@ public class RecognizedOptions {
XMAXERRS,
XMAXWARNS,
// XSTDOUT,
XPKGINFO,
XPRINT,
XPRINTROUNDS,
XPRINTPROCESSORINFO,
@ -532,6 +534,9 @@ public class RecognizedOptions {
new XOption(XPREFER, "opt.prefer",
Option.ChoiceKind.ONEOF, "source", "newer"),
new XOption(XPKGINFO, "opt.pkginfo",
Option.ChoiceKind.ONEOF, "always", "legacy", "nonempty"),
/* -O is a no-op, accepted for backward compatibility. */
new HiddenOption(O),
@ -598,6 +603,16 @@ public class RecognizedOptions {
};
}
public enum PkgInfo {
ALWAYS, LEGACY, NONEMPTY;
public static PkgInfo get(Options options) {
String v = options.get(XPKGINFO);
return (v == null
? PkgInfo.LEGACY
: PkgInfo.valueOf(v.toUpperCase()));
}
}
private static Map<String,Boolean> getXLintChoices() {
Map<String,Boolean> choices = new LinkedHashMap<String,Boolean>();
choices.put("all", false);

View File

@ -74,7 +74,9 @@ javac.opt.Werror=\
javac.opt.A=\
Options to pass to annotation processors
javac.opt.implicit=\
Specify whether or not to generate class files for implicitly referenced files
Specify whether or not to generate class files for implicitly referenced files
javac.opt.pkginfo=\
Specify handling of package-info files
javac.opt.arg.class=\
<class>
javac.opt.arg.class.list=\
@ -189,7 +191,7 @@ javac.msg.usage=\
javac.msg.usage.nonstandard.footer=\
These options are non-standard and subject to change without notice.
javac.msg.bug=\
An exception has occurred in the compiler ({0}). \
Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport) \

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2010, 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.
*
* 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.
*/
/*
* @test
* @bug 6960424
* @summary new option -Xpkginfo for better control of when package-info.class is generated
*/
import java.io.*;
import java.util.*;
public class TestPkgInfo {
enum OptKind {
NONE(null),
ALWAYS("-Xpkginfo:always"),
NONEMPTY("-Xpkginfo:nonempty"),
LEGACY("-Xpkginfo:legacy");
OptKind(String opt) { this.opt = opt; }
final String opt;
};
public static void main(String... args) throws Exception {
new TestPkgInfo().run(args);
}
public void run(String... args) throws Exception {
boolean[] booleanValues = { false, true };
for (OptKind ok: OptKind.values()) {
for (boolean sr: booleanValues) {
for (boolean cr: booleanValues) {
for (boolean rr: booleanValues) {
try {
test(ok, sr, cr, rr);
} catch (Exception e) {
error("Exception: " + e);
}
if (errors > 0) throw new AssertionError();
}
}
}
}
if (errors > 0)
throw new Exception(errors + " errors occurred");
}
void test(OptKind ok, boolean sr, boolean cr, boolean rr) throws Exception {
count++;
System.err.println("Test " + count + ": ok:" + ok + " sr:" + sr + " cr:" + cr + " rr:" + rr);
StringBuilder sb = new StringBuilder();
// create annotated package statement with all combinations of retention policy
if (sr) sb.append("@SR\n");
if (cr) sb.append("@CR\n");
if (rr) sb.append("@RR\n");
sb.append("package p;\n");
sb.append("\n");
sb.append("import java.lang.annotation.*;\n");
sb.append("@Retention(RetentionPolicy.SOURCE) @interface SR { }\n");
sb.append("@Retention(RetentionPolicy.CLASS) @interface CR { }\n");
sb.append("@Retention(RetentionPolicy.RUNTIME) @interface RR { }\n");
// test specific tmp directory
File tmpDir = new File("tmp.test" + count);
File classesDir = new File(tmpDir, "classes");
classesDir.mkdirs();
File pkginfo_java = new File(new File(tmpDir, "src"), "package-info.java");
writeFile(pkginfo_java, sb.toString());
// build up list of options and files to be compiled
List<String> opts = new ArrayList<String>();
List<File> files = new ArrayList<File>();
opts.add("-d");
opts.add(classesDir.getPath());
if (ok.opt != null)
opts.add(ok.opt);
//opts.add("-verbose");
files.add(pkginfo_java);
compile(opts, files);
File pkginfo_class = new File(new File(classesDir, "p"), "package-info.class");
boolean exists = pkginfo_class.exists();
boolean expected;
switch (ok) {
case ALWAYS:
expected = true;
break;
case LEGACY:
case NONE:
expected = (sr || cr || rr ); // any annotation
break;
case NONEMPTY:
expected = (cr || rr ); // any annotation in class file
break;
default:
throw new IllegalStateException();
}
if (exists && !expected)
error("package-info.class found but not expected");
if (!exists && expected)
error("package-info.class expected but not found");
}
/** Compile files with options provided. */
void compile(List<String> opts, List<File> files) throws Exception {
System.err.println("javac: " + opts + " " + files);
List<String> args = new ArrayList<String>();
args.addAll(opts);
for (File f: files)
args.add(f.getPath());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
pw.flush();
if (sw.getBuffer().length() > 0)
System.err.println(sw.toString());
if (rc != 0)
throw new Exception("compilation failed: rc=" + rc);
}
/** Write a file with a given body. */
void writeFile(File f, String body) throws Exception {
if (f.getParentFile() != null)
f.getParentFile().mkdirs();
Writer out = new FileWriter(f);
try {
out.write(body);
} finally {
out.close();
}
}
/** Report an error. */
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
/** Test case counter. */
int count;
/** Number of errors found. */
int errors;
}