6999210: javac should be able to warn of anomalous conditions in classfiles
Reviewed-by: mcimadamore, darcy
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java Tue Dec 07 14:13:25 2010 -0800
@@ -134,6 +134,11 @@
CAST("cast"),
/**
+ * Warn about issues related to classfile contents
+ */
+ CLASSFILE("classfile"),
+
+ /**
* Warn about use of deprecated items.
*/
DEPRECATION("deprecation"),
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Tue Dec 07 14:13:25 2010 -0800
@@ -32,6 +32,7 @@
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.lang.model.SourceVersion;
@@ -44,11 +45,13 @@
import com.sun.tools.javac.comp.Annotate;
import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Lint.LintCategory;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.file.BaseFileObject;
import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Kinds.*;
@@ -102,6 +105,10 @@
*/
boolean allowAnnotations;
+ /** Lint option: warn about classfile issues
+ */
+ boolean lintClassfile;
+
/** Switch: preserve parameter names from the variable table.
*/
public boolean saveParameterNames;
@@ -207,6 +214,11 @@
*/
boolean haveParameterNameIndices;
+ /**
+ * The set of attribute names for which warnings have been generated for the current class
+ */
+ Set<Name> warnedAttrs = new HashSet<Name>();
+
/** Get the ClassReader instance for this invocation. */
public static ClassReader instance(Context context) {
ClassReader instance = context.get(classReaderKey);
@@ -279,6 +291,8 @@
typevars = new Scope(syms.noSymbol);
debugJSR308 = options.isSet("TA:reader");
+ lintClassfile = Lint.instance(context).isEnabled(LintCategory.CLASSFILE);
+
initAttributeReaders();
}
@@ -870,7 +884,22 @@
}
boolean accepts(AttributeKind kind) {
- return kinds.contains(kind) && majorVersion >= version.major;
+ if (kinds.contains(kind)) {
+ if (majorVersion > version.major || (majorVersion == version.major && minorVersion >= version.minor))
+ return true;
+
+ if (lintClassfile && !warnedAttrs.contains(name)) {
+ JavaFileObject prev = log.useSource(currentClassFile);
+ try {
+ log.warning(LintCategory.CLASSFILE, (DiagnosticPosition) null, "future.attr",
+ name, version.major, version.minor, majorVersion, minorVersion);
+ } finally {
+ log.useSource(prev);
+ }
+ warnedAttrs.add(name);
+ }
+ }
+ return false;
}
abstract void read(Symbol sym, int attrLen);
@@ -889,7 +918,7 @@
protected Map<Name, AttributeReader> attributeReaders = new HashMap<Name, AttributeReader>();
- protected void initAttributeReaders() {
+ private void initAttributeReaders() {
AttributeReader[] readers = {
// v45.3 attributes
@@ -1561,7 +1590,7 @@
public void accept(Visitor v) { ((ProxyVisitor)v).visitCompoundAnnotationProxy(this); }
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
buf.append("@");
buf.append(type.tsym.getQualifiedName());
buf.append("/*proxy*/{");
@@ -2286,6 +2315,7 @@
throw new CompletionFailure(c, "user-selected completion failure by class name");
}
currentOwner = c;
+ warnedAttrs.clear();
JavaFileObject classfile = c.classfile;
if (classfile != null) {
JavaFileObject previousClassFile = currentClassFile;
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Dec 07 14:13:25 2010 -0800
@@ -767,6 +767,9 @@
compiler.warn.source.no.bootclasspath=\
bootstrap class path not set in conjunction with -source {0}
+compiler.warn.future.attr=\
+ {0} attribute introduced in version {1}.{2} class files is ignored in version {3}.{4} class files
+
# Warnings related to annotation processing
compiler.warn.proc.package.does.not.exist=\
package {0} does not exist
--- a/langtools/src/share/classes/com/sun/tools/javac/util/RawDiagnosticFormatter.java Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/RawDiagnosticFormatter.java Tue Dec 07 14:13:25 2010 -0800
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.EnumSet;
import java.util.Locale;
+import javax.tools.JavaFileObject;
import com.sun.tools.javac.api.DiagnosticFormatter.Configuration.*;
import com.sun.tools.javac.api.Formattable;
@@ -62,7 +63,7 @@
//provide common default formats
public String formatDiagnostic(JCDiagnostic d, Locale l) {
try {
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder();
if (d.getPosition() != Position.NOPOS) {
buf.append(formatSource(d, false, null));
buf.append(':');
@@ -71,16 +72,22 @@
buf.append(formatPosition(d, COLUMN, null));
buf.append(':');
}
+ else if (d.getSource() != null && d.getSource().getKind() == JavaFileObject.Kind.CLASS) {
+ buf.append(formatSource(d, false, null));
+ buf.append(":-:-:");
+ }
else
buf.append('-');
buf.append(' ');
buf.append(formatMessage(d, null));
- if (displaySource(d))
- buf.append("\n" + formatSourceLine(d, 0));
+ if (displaySource(d)) {
+ buf.append("\n");
+ buf.append(formatSourceLine(d, 0));
+ }
return buf.toString();
}
catch (Exception e) {
- e.printStackTrace();
+ //e.printStackTrace();
return null;
}
}
@@ -96,7 +103,9 @@
buf.append(",{");
for (String sub : formatSubdiagnostics(d, null)) {
buf.append(sep);
- buf.append("(" + sub + ")");
+ buf.append("(");
+ buf.append(sub);
+ buf.append(")");
sep = ",";
}
buf.append('}');
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T6999210.java Tue Dec 07 14:13:25 2010 -0800
@@ -0,0 +1,124 @@
+/*
+ * 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 6999210
+ * @summary javac should be able to warn of anomalous conditions in classfiles
+ */
+
+import java.io.*;
+import java.util.*;
+
+public class T6999210 {
+ public static void main(String... args) throws Exception {
+ new T6999210().run();
+ }
+
+ void run() throws Exception {
+ File srcDir = new File("src");
+ File classesDir = new File("classes");
+ classesDir.mkdirs();
+
+ File c_java = writeFile(srcDir, "C.java", "class C<T> { }");
+ compile("-d", classesDir.getPath(), c_java.getPath());
+ File c_class = new File(classesDir, "C.class");
+ setMajorVersion(c_class, 48);
+ File d_java = writeFile(srcDir, "D.java", "class D { C c; }");
+
+ // verify no warning if -Xlint:classfile not enabled
+ String out1 = compile(
+ "-d", classesDir.getPath(),
+ "-classpath", classesDir.getPath(),
+ d_java.getPath());
+ if (out1.length() > 0)
+ error("unexpected output from javac");
+
+ // sanity check of warning when -XDrawDiagnostics not used
+ String out2 = compile(
+ "-d", classesDir.getPath(),
+ "-classpath", classesDir.getPath(),
+ "-Xlint:classfile",
+ d_java.getPath());
+ if (!out2.contains("[classfile]"))
+ error("expected output \"[classfile]\" not found");
+
+ // check specific details, using -XDrawDiagnostics
+ String out3 = compile(
+ "-d", classesDir.getPath(),
+ "-classpath", classesDir.getPath(),
+ "-Xlint:classfile", "-XDrawDiagnostics",
+ d_java.getPath());
+ String expect = "C.class:-:-: compiler.warn.future.attr: Signature, 49, 0, 48, 0";
+ if (!out3.contains(expect))
+ error("expected output \"" + expect + "\" not found");
+
+ if (errors > 0)
+ throw new Exception(errors + " errors occurred");
+ }
+
+ String compile(String... args) throws Exception {
+ System.err.println("compile: " + Arrays.asList(args));
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ int rc = com.sun.tools.javac.Main.compile(args, pw);
+ pw.close();
+ String out = sw.toString();
+ if (out.length() > 0)
+ System.err.println(out);
+ if (rc != 0)
+ throw new Exception("compilation failed, rc=" + rc);
+ return out;
+ }
+
+ void setMajorVersion(File f, int major) throws IOException {
+ int len = (int) f.length();
+ byte[] data = new byte[len];
+ try (DataInputStream in = new DataInputStream(new FileInputStream(f))) {
+ in.readFully(data);
+ }
+ // u4 magic
+ // u2 minor
+ data[6] = (byte) (major >> 8);
+ data[7] = (byte) (major & 0xff);
+ try (FileOutputStream out = new FileOutputStream(f)) {
+ out.write(data);
+ }
+ }
+
+ File writeFile(File dir, String path, String body) throws IOException {
+ File f = new File(dir, path);
+ f.getParentFile().mkdirs();
+ try (FileWriter out = new FileWriter(f)) {
+ out.write(body);
+ }
+ return f;
+ }
+
+ void error(String msg) {
+ System.err.println("Error: " + msg);
+ errors++;
+ }
+
+ int errors;
+}
--- a/langtools/test/tools/javac/annotations/6214965/T6214965.out Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/test/tools/javac/annotations/6214965/T6214965.out Tue Dec 07 14:13:25 2010 -0800
@@ -1,2 +1,2 @@
-- compiler.warn.annotation.method.not.found: CompilerAnnotationTest2, name2
+CompilerAnnotationTest.class:-:-: compiler.warn.annotation.method.not.found: CompilerAnnotationTest2, name2
1 warning
--- a/langtools/test/tools/javac/annotations/6365854/test1.out Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/test/tools/javac/annotations/6365854/test1.out Tue Dec 07 14:13:25 2010 -0800
@@ -1,2 +1,2 @@
-- compiler.warn.annotation.method.not.found.reason: test.annotation.TestAnnotation, test, (compiler.misc.class.file.not.found: test.annotation.TestAnnotation)
+TestCore.class:-:-: compiler.warn.annotation.method.not.found.reason: test.annotation.TestAnnotation, test, (compiler.misc.class.file.not.found: test.annotation.TestAnnotation)
1 warning
--- a/langtools/test/tools/javac/annotations/6365854/test2.out Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/test/tools/javac/annotations/6365854/test2.out Tue Dec 07 14:13:25 2010 -0800
@@ -1,2 +1,2 @@
-- compiler.warn.annotation.method.not.found: test.annotation.TestAnnotation, test
+TestCore.class:-:-: compiler.warn.annotation.method.not.found: test.annotation.TestAnnotation, test
1 warning
--- a/langtools/test/tools/javac/diags/examples.not-yet.txt Mon Dec 06 11:51:02 2010 +0000
+++ b/langtools/test/tools/javac/diags/examples.not-yet.txt Tue Dec 07 14:13:25 2010 -0800
@@ -104,6 +104,7 @@
compiler.warn.annotation.method.not.found # ClassReader
compiler.warn.annotation.method.not.found.reason # ClassReader
compiler.warn.big.major.version # ClassReader
+compiler.warn.future.attr # ClassReader
compiler.warn.illegal.char.for.encoding
compiler.warn.invalid.archive.file
compiler.warn.override.bridge