8025087: Annotation processing api returns default modifier for interface static method

ClassReader must not set Flags.DEFAULT for interface static methods

Reviewed-by: vromero, jjg
This commit is contained in:
Jan Lahoda 2013-10-09 13:09:31 +02:00
parent 7de3ec870d
commit 5ec0ba7c47
10 changed files with 365 additions and 24 deletions
langtools
make
src/share/classes/com/sun/tools/javac
test/tools/javac
defaultMethods
diags
processing/model/element

@ -360,7 +360,7 @@
datafile="${build.coverage.dir}/cobertura.ser"/>
</target>
<target name="diags-examples" depends="build-javac">
<target name="diags-examples" depends="build-javac,build-javap">
<!-- can override the following on the command line if desired. -->
<property name="diags.examples.out" location="${build.dir}/diag-examples/diags-examples.html"/>
<mkdir dir="${build.dir}/diag-examples/classes"/>
@ -370,7 +370,7 @@
destdir="${build.dir}/diag-examples/classes"
includes="ArgTypeCompilerFactory.java,Example.java,FileManager.java,HTMLWriter.java,RunExamples.java,DocCommentProcessor.java"
sourcepath=""
classpath="${dist.lib.dir}/javac.jar"
classpath="${dist.lib.dir}/javac.jar;${dist.lib.dir}/javap.jar"
includeAntRuntime="no"
debug="${javac.debug}"
debuglevel="${javac.debuglevel}">
@ -379,7 +379,7 @@
<java fork="true"
jvm="${target.java.home}/bin/java"
dir="test/tools/javac/diags"
classpath="${build.dir}/diag-examples/classes;${dist.lib.dir}/javac.jar"
classpath="${build.dir}/diag-examples/classes;${dist.lib.dir}/javac.jar;${dist.lib.dir}/javap.jar"
classname="RunExamples">
<jvmarg value="-Dtest.classes=${build.dir}/diag-examples/classes"/>
<arg value="-examples"/>

@ -1993,11 +1993,15 @@ public class ClassReader {
(flags & ABSTRACT) == 0 && !name.equals(names.clinit)) {
if (majorVersion > Target.JDK1_8.majorVersion ||
(majorVersion == Target.JDK1_8.majorVersion && minorVersion >= Target.JDK1_8.minorVersion)) {
currentOwner.flags_field |= DEFAULT;
flags |= DEFAULT | ABSTRACT;
if ((flags & STATIC) == 0) {
currentOwner.flags_field |= DEFAULT;
flags |= DEFAULT | ABSTRACT;
}
} else {
//protect against ill-formed classfiles
throw new CompletionFailure(currentOwner, "default method found in pre JDK 8 classfile");
throw badClassFile((flags & STATIC) == 0 ? "invalid.default.interface" : "invalid.static.interface",
Integer.toString(majorVersion),
Integer.toString(minorVersion));
}
}
if (name == names.init && currentOwner.hasOuterInstance()) {

@ -1699,6 +1699,7 @@ compiler.err.cant.access=\
cannot access {0}\n\
{1}
# 0: file name, 1: message segment
compiler.misc.bad.class.file.header=\
bad class file: {0}\n\
{1}\n\
@ -1744,6 +1745,14 @@ compiler.misc.class.file.wrong.class=\
compiler.misc.class.file.not.found=\
class file for {0} not found
# 0: classfile major version, 1: classfile minor version
compiler.misc.invalid.default.interface=\
default method found in version {0}.{1} classfile
# 0: classfile major version, 1: classfile minor version
compiler.misc.invalid.static.interface=\
static method found in version {0}.{1} classfile
# 0: name
compiler.misc.file.doesnt.contain.class=\
file does not contain class {0}

@ -0,0 +1,89 @@
/*
* Copyright (c) 2013, 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 8025087
* @summary Verify that pre-JDK8 classfiles with default and/or static methods
* are refused correctly.
* @build BadClassfile
* @run main BadClassfile
*/
import com.sun.tools.classfile.*;
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.jvm.ClassReader.BadClassFile;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.JCDiagnostic;
import java.io.File;
import java.util.Arrays;
import java.util.Objects;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class BadClassfile {
public static void main(String... args) throws Exception {
test("BadClassfile$DefaultMethodTest", "compiler.misc.invalid.default.interface");
test("BadClassfile$StaticMethodTest", "compiler.misc.invalid.static.interface");
}
private static void test(String classname, String expected) throws Exception {
File classfile = new File(System.getProperty("test.classes", "."), classname + ".class");
ClassFile cf = ClassFile.read(classfile);
cf = new ClassFile(cf.magic, Target.JDK1_7.minorVersion,
Target.JDK1_7.majorVersion, cf.constant_pool, cf.access_flags,
cf.this_class, cf.super_class, cf.interfaces, cf.fields,
cf.methods, cf.attributes);
new ClassWriter().write(cf, classfile);
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
JavacTaskImpl task = (JavacTaskImpl) c.getTask(null, null, null, Arrays.asList("-classpath", System.getProperty("test.classes", ".")), null, null);
try {
Symbol clazz = com.sun.tools.javac.main.JavaCompiler.instance(task.getContext()).resolveIdent(classname);
clazz.complete();
} catch (BadClassFile f) {
JCDiagnostic embeddedDiag = (JCDiagnostic) f.diag.getArgs()[1];
assertEquals(expected, embeddedDiag.getCode());
assertEquals(Integer.toString(Target.JDK1_7.majorVersion), embeddedDiag.getArgs()[0]);
assertEquals(Integer.toString(Target.JDK1_7.minorVersion), embeddedDiag.getArgs()[1]);
}
}
private static void assertEquals(Object expected, Object actual) {
Assert.check(Objects.equals(expected, actual),
"expected: " + expected + ", but was: " + actual);
}
interface DefaultMethodTest {
default void test() { }
}
interface StaticMethodTest {
static void test() { }
}
}

@ -40,7 +40,6 @@ compiler.err.type.var.more.than.once # UNUSED
compiler.err.type.var.more.than.once.in.result # UNUSED
compiler.err.unexpected.type
compiler.err.unsupported.cross.fp.lit # Scanner: host system dependent
compiler.misc.bad.class.file.header # bad class file
compiler.misc.bad.class.signature # bad class file
compiler.misc.bad.const.pool.tag # bad class file
compiler.misc.bad.const.pool.tag.at # bad class file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2013, 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.
*/
// key: compiler.misc.invalid.default.interface
// key: compiler.misc.bad.class.file.header
// key: compiler.err.cant.access
// options: -processor CreateBadClassFile
/* The annotation processor will create an invalid classfile with version 51.0
* and a non-abstract method in an interface. Loading the classfile will produce
* the diagnostic.
*/
class InvalidDefaultInterface { }

@ -0,0 +1,85 @@
/*
* Copyright (c) 2013, 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.
*/
import com.sun.tools.classfile.*;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
import com.sun.tools.classfile.ConstantPool.CPInfo;
import java.io.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.*;
/* Create an invalid classfile with version 51.0 and a non-abstract method in an interface.*/
@SupportedAnnotationTypes("*")
public class CreateBadClassFile extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv) {
if (++round == 1) {
ConstantPool cp = new ConstantPool(new CPInfo[] {
new CONSTANT_Utf8_info(""), //0
new CONSTANT_Utf8_info("Test"), //1
new CONSTANT_Class_info(null, 1), //2
new CONSTANT_Utf8_info("java/lang/Object"), //3
new CONSTANT_Class_info(null, 3), //4
new CONSTANT_Utf8_info("test"), //5
new CONSTANT_Utf8_info("()V"), //6
});
ClassFile cf = new ClassFile(0xCAFEBABE,
0,
51,
cp,
new AccessFlags(AccessFlags.ACC_ABSTRACT |
AccessFlags.ACC_INTERFACE |
AccessFlags.ACC_PUBLIC),
2,
4,
new int[0],
new Field[0],
new Method[] {
//creating non-abstract method in 51.0 classfile:
new Method(new AccessFlags(AccessFlags.ACC_PUBLIC),
5,
new Descriptor(6),
new Attributes(cp, new Attribute[0]))
},
new Attributes(cp, new Attribute[0]));
try {
JavaFileObject clazz = processingEnv.getFiler().createClassFile("Test");
try (OutputStream out = clazz.openOutputStream()) {
new ClassWriter().write(cf, out);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return false;
}
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
int round = 0;
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 2013, 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.
*/
// key: compiler.misc.invalid.static.interface
// key: compiler.misc.bad.class.file.header
// key: compiler.err.cant.access
// options: -processor CreateBadClassFile
/* The annotation processor will create an invalid classfile with version 51.0
* and a static method in an interface. Loading the classfile will produce
* the diagnostic.
*/
class InvalidDefaultInterface { }

@ -0,0 +1,86 @@
/*
* Copyright (c) 2013, 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.
*/
import com.sun.tools.classfile.*;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
import com.sun.tools.classfile.ConstantPool.CPInfo;
import java.io.*;
import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.tools.*;
/* Create an invalid classfile with version 51.0 and a static method in an interface.*/
@SupportedAnnotationTypes("*")
public class CreateBadClassFile extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> elems, RoundEnvironment renv) {
if (++round == 1) {
ConstantPool cp = new ConstantPool(new CPInfo[] {
new CONSTANT_Utf8_info(""), //0
new CONSTANT_Utf8_info("Test"), //1
new CONSTANT_Class_info(null, 1), //2
new CONSTANT_Utf8_info("java/lang/Object"), //3
new CONSTANT_Class_info(null, 3), //4
new CONSTANT_Utf8_info("test"), //5
new CONSTANT_Utf8_info("()V"), //6
});
ClassFile cf = new ClassFile(0xCAFEBABE,
0,
51,
cp,
new AccessFlags(AccessFlags.ACC_ABSTRACT |
AccessFlags.ACC_INTERFACE |
AccessFlags.ACC_PUBLIC),
2,
4,
new int[0],
new Field[0],
new Method[] {
//creating static method in 51.0 classfile:
new Method(new AccessFlags(AccessFlags.ACC_PUBLIC |
AccessFlags.ACC_STATIC),
5,
new Descriptor(6),
new Attributes(cp, new Attribute[0]))
},
new Attributes(cp, new Attribute[0]));
try {
JavaFileObject clazz = processingEnv.getFiler().createClassFile("Test");
try (OutputStream out = clazz.openOutputStream()) {
new ClassWriter().write(cf, out);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return false;
}
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
int round = 0;
}

@ -23,54 +23,57 @@
/*
* @test
* @bug 8005046 8011052
* @summary Test basic properties of javax.lang.element.Element
* @bug 8005046 8011052 8025087
* @summary Test basic properties of javax.lang.element.ExecutableElement
* @author Joseph D. Darcy
* @library /tools/javac/lib
* @build JavacTestingAbstractProcessor TestExecutableElement
* @compile -processor TestExecutableElement -proc:only TestExecutableElement.java
* @compile -processor TestExecutableElement -proc:only -AexpectedMethodCount=7 TestExecutableElement.java
* @compile/process -processor TestExecutableElement -proc:only -AexpectedMethodCount=3 ProviderOfDefault
*/
import java.lang.annotation.*;
import java.util.Formatter;
import java.util.Set;
import java.util.Objects;
import java.util.regex.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import static javax.lang.model.SourceVersion.*;
import javax.lang.model.element.*;
import javax.lang.model.util.*;
import static javax.lang.model.util.ElementFilter.*;
import static javax.tools.Diagnostic.Kind.*;
import static javax.tools.StandardLocation.*;
/**
* Test some basic workings of javax.lang.element.ExecutableElement
*/
@SupportedOptions("expectedMethodCount")
public class TestExecutableElement extends JavacTestingAbstractProcessor implements ProviderOfDefault {
private int seenMethods = 0;
@IsDefault(false)
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
int errors = 0;
if (!roundEnv.processingOver()) {
boolean hasRun = false;
for (Element element : roundEnv.getRootElements()) {
for (ExecutableElement method : methodsIn(element.getEnclosedElements())) {
hasRun = true;
errors += checkIsDefault(method);
checkIsDefault(method);
seenMethods++;
}
}
} else {
String expectedMethodCountStr = processingEnv.getOptions().get("expectedMethodCount");
if (expectedMethodCountStr == null) {
messager.printMessage(ERROR, "No expected method count specified.");
} else {
int expectedMethodCount = Integer.parseInt(expectedMethodCountStr);
if (!hasRun) {
messager.printMessage(ERROR, "No test cases run; test fails.");
if (seenMethods != expectedMethodCount) {
messager.printMessage(ERROR, "Wrong number of seen methods: " + seenMethods);
}
}
}
return true;
}
@IsDefault(false)
int checkIsDefault(ExecutableElement method) {
void checkIsDefault(ExecutableElement method) {
System.out.println("Testing " + method);
IsDefault expectedIsDefault = method.getAnnotation(IsDefault.class);
@ -116,9 +119,7 @@ public class TestExecutableElement extends JavacTestingAbstractProcessor impleme
expectedDefault,
methodIsDefault).toString(),
method);
return 1;
}
return 0;
}
}
@ -142,4 +143,6 @@ interface ProviderOfDefault {
@IsDefault(value=true, expectedTextRegex="\\s*@IsDefault\\(.*\\)\\s*default strictfp void quux\\(\\);\\s*$")
default strictfp void quux() {};
@IsDefault(false)
static void statik() {}
}