# HG changeset patch # User aeremeev # Date 1429523141 -10800 # Node ID 319ded3e820ab74aa97f33f64427cd080e11a95b # Parent ac3f5a39d4ff14d70c365e12cf5ec8f2abd52a04 8042947: Implement classfile tests for AnnotationDefault attribute Reviewed-by: jjg, shurailine, anazarov diff -r ac3f5a39d4ff -r 319ded3e820a langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefault.java.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefault.java.template Mon Apr 20 12:45:41 2015 +0300 @@ -0,0 +1,94 @@ +/* + * 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. + * + * 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 java.lang.annotation.*; + +%REPEATABLE% +@Retention(RetentionPolicy.%POLICY%) +public @interface AnnotationDefault { + @ExpectedValues(tag = 'Z', name = "booleanDefault", values = "1") + boolean booleanDefault() default true; + + @ExpectedValues(tag = 'C', name = "charDefault", values = "1") + char charDefault() default 1; + + @ExpectedValues(tag = 'B', name = "byteDefault", values = "1") + byte byteDefault() default 1; + + @ExpectedValues(tag = 'S', name = "shortDefault", values = "1") + short shortDefault() default 1; + + @ExpectedValues(tag = 'I', name = "intDefault", values = "1") + int intDefault() default 1; + + @ExpectedValues(tag = 'J', name = "longDefault", values = "1") + long longDefault() default 1; + + @ExpectedValues(tag = 'F', name = "floatDefault", values = "1.0") + float floatDefault() default 1.0f; + + @ExpectedValues(tag = 'D', name = "doubleDefault", values = "1.0") + double doubleDefault() default 1.0; + + @ExpectedValues(tag = 's', name = "stringDefault", values = "DEFAULT_VALUE") + String stringDefault() default "DEFAULT_VALUE"; + + @ExpectedValues(tag = 'e', name = "enumDefault", values = {"LAnnotationDefault$DefaultValues;", "VALUE1"}) + DefaultValues enumDefault() default DefaultValues.VALUE1; + + @ExpectedValues(tag = 'c', name = "clazzDefault1", values = "V") + Class clazzDefault1() default void.class; + + @ExpectedValues(tag = 'c', name = "clazzDefault2", values = "Ljava/lang/Void;") + Class clazzDefault2() default Void.class; + + @ExpectedValues(tag = '[', name = "arrayDefault1", values = {"1", "2", "3"}) + int[] arrayDefault1() default {1, 2, 3}; + + @ExpectedValues(tag = '[', name = "arrayDefault2", values = {"DEFAULT_VALUE_1", "DEFAULT_VALUE_2", "DEFAULT_VALUE_3"}) + String[] arrayDefault2() default {"DEFAULT_VALUE_1", "DEFAULT_VALUE_2", "DEFAULT_VALUE_3"}; + + @ExpectedValues(tag = '[', name = "arrayOfEnums", values = {"LAnnotationDefault$DefaultValues;", "VALUE2", + "LAnnotationDefault$DefaultValues;", "VALUE3"}) + DefaultValues[] arrayOfEnums() default {DefaultValues.VALUE2, DefaultValues.VALUE3}; + + @ExpectedValues(tag = '[', name = "arrayOfAnno", values = {"LAnnotationDefault$DefaultAnnotation;", "value", "DEFAULT_VALUE1", + "LAnnotationDefault$DefaultAnnotation;", "value", "DEFAULT_VALUE2"}) + DefaultAnnotation[] arrayOfAnno() default {@DefaultAnnotation(value = "DEFAULT_VALUE1"), @DefaultAnnotation(value = "DEFAULT_VALUE2")}; + + @ExpectedValues(tag = '@', name = "annoDefault", values = {"LAnnotationDefault$DefaultAnnotation;", "value", "DEFAULT_VALUE"}) + DefaultAnnotation annoDefault() default @DefaultAnnotation(value = "DEFAULT_VALUE"); + + @interface DefaultAnnotation { + String value() default "NOT_DEFAULT_VALUE"; + } + + enum DefaultValues { + VALUE1, VALUE2, VALUE3 + } +} + +@Retention(RetentionPolicy.%POLICY%) +@interface Container { + AnnotationDefault[] value(); +} \ No newline at end of file diff -r ac3f5a39d4ff -r 319ded3e820a langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultTest.java Mon Apr 20 12:45:41 2015 +0300 @@ -0,0 +1,163 @@ +/* + * 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. + * + * 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 8042947 + * @summary Checking AnnotationDefault attribute. + * @library /tools/lib /tools/javac/lib ../lib + * @build AnnotationDefaultTest TestBase TestResult InMemoryFileManager ToolBox AnnotationDefaultVerifier + * @run main AnnotationDefaultTest + */ + +import com.sun.tools.classfile.*; + +import java.io.File; +import java.io.IOException; +import java.lang.annotation.RetentionPolicy; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class AnnotationDefaultTest extends TestResult { + + private final static String templateFileName = "AnnotationDefault.java.template"; + + private final AnnotationDefaultVerifier verifier; + + public AnnotationDefaultTest() { + verifier = new AnnotationDefaultVerifier(); + } + + private void test(String template, Map replacements, boolean hasDefault) { + String source = replace(template, replacements); + addTestCase(source); + try { + printf("Testing source:\n%s\n", source); + String className = "AnnotationDefault"; + InMemoryFileManager fileManager = compile(source); + + // Map + Map expectedValues = + getExpectedValues(forName(className, fileManager)); + ClassFile classFile = readClassFile(fileManager.getClasses().get(className)); + + for (Method method : classFile.methods) { + String methodName = method.getName(classFile.constant_pool); + printf("Testing method : %s\n", methodName); + AnnotationDefault_attribute attr = + (AnnotationDefault_attribute) method.attributes + .get(Attribute.AnnotationDefault); + + if (hasDefault && !checkNotNull(attr, "Attribute is not null") + || !hasDefault && checkNull(attr, "Attribute is null")) { + // stop checking, attr is null + continue; + } + + checkEquals(countNumberOfAttributes(method.attributes.attrs), + 1l, + "Number of AnnotationDefault attribute"); + checkEquals(classFile.constant_pool + .getUTF8Value(attr.attribute_name_index), + "AnnotationDefault", "attribute_name_index"); + + ExpectedValues expectedValue = expectedValues.get(methodName); + checkEquals((char) attr.default_value.tag, expectedValue.tag(), + String.format("check tag : %c %s", expectedValue.tag(), expectedValue.name())); + verifier.testElementValue(attr.default_value.tag, + this, classFile, attr.default_value, + expectedValue.values()); + verifier.testLength(attr.default_value.tag, this, attr); + } + } catch (Exception e) { + addFailure(e); + } + } + + private Class forName(String className, InMemoryFileManager fileManager) throws ClassNotFoundException { + return fileManager.getClassLoader(null).loadClass(className); + } + + private Map getExpectedValues(Class clazz) { + return Stream.of(clazz.getMethods()) + .map(method -> method.getAnnotation(ExpectedValues.class)) + .filter(Objects::nonNull) + .collect(Collectors.toMap( + ExpectedValues::name, + Function.identity())); + } + + private String replace(String template, Map replacements) { + String ans = template; + for (Map.Entry replace : replacements.entrySet()) { + ans = ans.replaceAll(replace.getKey(), replace.getValue()); + } + return ans; + } + + private long countNumberOfAttributes(Attribute[] attrs) { + return Stream.of(attrs) + .filter(x -> x instanceof AnnotationDefault_attribute) + .count(); + } + + public String getSource(File templateFileName) throws IOException { + return Files.lines(templateFileName.toPath()) + .filter(str -> !str.startsWith("/*") && !str.startsWith(" *")) + .collect(Collectors.joining("\n")); + } + + public void test() throws TestFailedException { + try { + String template = getSource(getSourceFile(templateFileName)); + for (int i = 0; i < 2; ++i) { + for (String repeatable : new String[] {"", "@Repeatable(Container.class)"}) { + for (RetentionPolicy policy : RetentionPolicy.values()) { + final int finalI = i; + Map replacements = new HashMap(){{ + put("%POLICY%", policy.toString()); + if (finalI != 0) { + put("default.*\n", ";\n"); + } + put("%REPEATABLE%", repeatable); + }}; + test(template, replacements, i == 0); + } + } + } + } catch (Throwable e) { + addFailure(e); + } finally { + checkStatus(); + } + } + + public static void main(String[] args) throws TestFailedException { + new AnnotationDefaultTest().test(); + } +} diff -r ac3f5a39d4ff -r 319ded3e820a langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/AnnotationDefaultVerifier.java Mon Apr 20 12:45:41 2015 +0300 @@ -0,0 +1,287 @@ +/* + * 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. + * + * 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.Annotation; +import com.sun.tools.classfile.AnnotationDefault_attribute; +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.ConstantPool; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class AnnotationDefaultVerifier { + + private final Map verifiers; + + public AnnotationDefaultVerifier() { + this.verifiers = new HashMap<>(); + verifiers.put((int) 'B', new TestIntegerElementValue()); + verifiers.put((int) 'C', new TestIntegerElementValue()); + verifiers.put((int) 'D', new TestDoubleElementValue()); + verifiers.put((int) 'F', new TestFloatElementValue()); + verifiers.put((int) 'I', new TestIntegerElementValue()); + verifiers.put((int) 'J', new TestLongElementValue()); + verifiers.put((int) 'S', new TestIntegerElementValue()); + verifiers.put((int) 'Z', new TestIntegerElementValue()); + verifiers.put((int) 's', new TestStringElementValue()); + verifiers.put((int) 'e', new TestEnumElementValue()); + verifiers.put((int) 'c', new TestClassElementValue()); + verifiers.put((int) '[', new TestArrayElementValue()); + verifiers.put((int) '@', new TestAnnotationElementValue()); + } + + public void testLength(int tag, TestResult testResult, AnnotationDefault_attribute attr) { + verifiers.get(tag).testLength(testResult, attr); + } + + public void testElementValue(int tag, TestResult testResult, ClassFile classFile, + Annotation.element_value element_value, String[] values) + throws ConstantPool.UnexpectedEntry, ConstantPool.InvalidIndex { + get(tag).testElementValue(testResult, classFile, element_value, values); + } + + private TestElementValue get(int tag) { + TestElementValue ev = verifiers.get(tag); + if (ev == null) { + throw new IllegalArgumentException("Unknown tag : " + (char) tag); + } + return ev; + } + + private abstract class TestElementValue { + public void testLength(TestResult testCase, AnnotationDefault_attribute attr) { + testCase.checkEquals(attr.attribute_length, 1 + attr.default_value.length(), + "attribute_length"); + } + + public String[] getValues(String[] values, int index, int length) { + return Arrays.copyOfRange(values, index, index + length); + } + + public int getLength() { + return 1; + } + + public abstract void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) + throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry; + } + + private class TestIntegerElementValue extends TestElementValue { + + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) throws ConstantPool.InvalidIndex { + Annotation.Primitive_element_value ev = + (Annotation.Primitive_element_value) element_value; + ConstantPool.CONSTANT_Integer_info info = + (ConstantPool.CONSTANT_Integer_info) + classFile.constant_pool.get(ev.const_value_index); + testCase.checkEquals(info.value, Integer.parseInt(values[0]), "const_value_index"); + } + } + + private class TestLongElementValue extends TestElementValue { + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) throws ConstantPool.InvalidIndex { + Annotation.Primitive_element_value ev = + (Annotation.Primitive_element_value) element_value; + ConstantPool.CONSTANT_Long_info info = + (ConstantPool.CONSTANT_Long_info) + classFile.constant_pool.get(ev.const_value_index); + testCase.checkEquals(info.value, Long.parseLong(values[0]), "const_value_index"); + } + } + + private class TestFloatElementValue extends TestElementValue { + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) throws ConstantPool.InvalidIndex { + Annotation.Primitive_element_value ev = + (Annotation.Primitive_element_value) element_value; + ConstantPool.CONSTANT_Float_info info = + (ConstantPool.CONSTANT_Float_info) + classFile.constant_pool.get(ev.const_value_index); + testCase.checkEquals(info.value, Float.parseFloat(values[0]), "const_value_index"); + } + } + + private class TestDoubleElementValue extends TestElementValue { + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) throws ConstantPool.InvalidIndex { + Annotation.Primitive_element_value ev = + (Annotation.Primitive_element_value) element_value; + ConstantPool.CONSTANT_Double_info info = + (ConstantPool.CONSTANT_Double_info) + classFile.constant_pool.get(ev.const_value_index); + testCase.checkEquals(info.value, Double.parseDouble(values[0]), "const_value_index"); + } + } + + private class TestStringElementValue extends TestElementValue { + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) throws ConstantPool.InvalidIndex { + Annotation.Primitive_element_value ev = + (Annotation.Primitive_element_value) element_value; + ConstantPool.CONSTANT_Utf8_info info = + (ConstantPool.CONSTANT_Utf8_info) + classFile.constant_pool.get(ev.const_value_index); + testCase.checkEquals(info.value, values[0], "const_value_index"); + } + } + + private class TestEnumElementValue extends TestElementValue { + + @Override + public int getLength() { + return 2; + } + + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) + throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry { + Annotation.Enum_element_value ev = (Annotation.Enum_element_value) element_value; + testCase.checkEquals(classFile.constant_pool.getUTF8Info(ev.type_name_index).value, + values[0], "type_name_index"); + testCase.checkEquals(classFile.constant_pool.getUTF8Info(ev.const_name_index).value, + values[1], "const_name_index"); + } + } + + private class TestClassElementValue extends TestElementValue { + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) + throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry { + Annotation.Class_element_value ev = (Annotation.Class_element_value) element_value; + testCase.checkEquals( + classFile.constant_pool.getUTF8Info(ev.class_info_index).value, + values[0], "class_info_index"); + } + } + + private class TestAnnotationElementValue extends TestElementValue { + @Override + public void testLength(TestResult testCase, AnnotationDefault_attribute attr) { + // Suppress, since it is hard to test the length of this kind of element values. + } + + @Override + public int getLength() { + // Expected that the test uses DefaultAnnotation + // tag (1 byte) + annotation_value (2 bytes) which contains const_value + return 3; + } + + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) + throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry { + Annotation ev = ((Annotation.Annotation_element_value) element_value) + .annotation_value; + testCase.checkEquals( + classFile.constant_pool.getUTF8Info(ev.type_index).value, + values[0], + "type_index"); + for (int i = 0; i < ev.num_element_value_pairs; ++i) { + Annotation.element_value_pair pair = ev.element_value_pairs[i]; + testCase.checkEquals( + classFile.constant_pool.getUTF8Info(pair.element_name_index).value, + values[2 * i + 1], + "element_name_index"); + TestElementValue testElementValue = verifiers.get(pair.value.tag); + testElementValue.testElementValue( + testCase, + classFile, + pair.value, + new String[]{values[2 * i + 2]}); + } + } + } + + private class TestArrayElementValue extends TestElementValue { + @Override + public void testLength(TestResult testCase, AnnotationDefault_attribute attr) { + Annotation.Array_element_value ev = + (Annotation.Array_element_value) attr.default_value; + int sizeOfTag = ev.values[0].tag == 'e' ? 0 : 1; + // tag (1 byte) + array header (2 byte) + length of entries + testCase.checkEquals(attr.attribute_length, 1 + 2 + + (sizeOfTag + ev.length() / ev.num_values) * ev.num_values, "attribute_length"); + } + + @Override + public void testElementValue( + TestResult testCase, + ClassFile classFile, + Annotation.element_value element_value, + String[] values) + throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry { + Annotation.Array_element_value ev = + (Annotation.Array_element_value) element_value; + int index = 0; + for (int i = 0; i < ev.num_values; ++i) { + TestElementValue testElementValue = verifiers.get(ev.values[i].tag); + int length = testElementValue.getLength(); + testElementValue.testElementValue( + testCase, + classFile, + ev.values[i], + testElementValue.getValues(values, index, length)); + index += length; + } + } + } +} diff -r ac3f5a39d4ff -r 319ded3e820a langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/classfiles/attributes/AnnotationDefault/ExpectedValues.java Mon Apr 20 12:45:41 2015 +0300 @@ -0,0 +1,32 @@ +/* + * 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. + * + * 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 java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface ExpectedValues { + char tag(); + String name(); + String[] values(); +}