/* * 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 8005085 8008762 8008751 8013065 * @summary Type annotations on anonymous and inner class. * Six TYPE_USE annotations are repeated(or not); Four combinations create * four test files, and each results in the test class and 2 anonymous classes. * Each element of these three classes is checked for expected number of the * four annotation Attributes. Expected annotation counts depend on type of * annotation place on type of element (a FIELD&TYPE_USE element on a field * results in 2). Elements with no annotations expect 0. * */ import java.lang.annotation.*; import java.io.*; import java.util.List; import com.sun.tools.classfile.*; import java.lang.annotation.*; import static java.lang.annotation.RetentionPolicy.*; import static java.lang.annotation.ElementType.*; public class TestAnonInnerClasses extends ClassfileTestHelper { // tally errors and test cases int errors = 0; int checks = 0; String[] AnnoAttributes = { Attribute.RuntimeVisibleTypeAnnotations, Attribute.RuntimeInvisibleTypeAnnotations, Attribute.RuntimeVisibleAnnotations, Attribute.RuntimeInvisibleAnnotations }; // Four test files generated based on combinations of repeating annotations. Boolean As= false, Bs=true, Cs=false, Ds=false, TAs=false,TBs=false; Boolean [][] bRepeat = new Boolean[][]{ /* no repeats */ {false, false, false, false, false, false} /* repeat A,C,TA */ //{true, false, true, false, true, false} /* repeat B,D,TB {false, true, false, true, false, true}, */ /* repeat all {true, true, true, true, true, true}*/ }; // Save descriptions of failed test case; does not terminate upon a failure. List failed = new java.util.LinkedList<>(); public static void main(String[] args) throws Exception { new TestAnonInnerClasses().run(); } // Check annotation counts and make reports sufficiently descriptive to // easily diagnose. void check(String testcase, int vtaX, int itaX, int vaX, int iaX, int vtaA, int itaA, int vaA, int iaA) { String descr = " checking " + testcase+" _TYPE_, expected: " + vtaX + ", " + itaX + ", " + vaX + ", " + iaX + "; actual: " + vtaA + ", " + itaA + ", " + vaA + ", " + iaA; String description; description=descr.replace("_TYPE_","RuntimeVisibleTypeAnnotations"); if(vtaX != vtaA) { errors++; failed.add(++checks + " " + testcase + ": (vtaX) " + vtaX + " != " + vtaA + " (vtaA)"); println(checks + " FAIL: " + description); } else println(++checks + " PASS: " + description); description=descr.replace("_TYPE_","RuntimeInvisibleTypeAnnotations"); if(itaX != itaA) { errors++; failed.add(++checks + " " + testcase + ": (itaX) " + itaX + " != " + itaA + " (itaA)"); println(checks + " FAIL: " + description); } else println(++checks + " PASS: " + description); description=descr.replace("_TYPE_","RuntimeVisibleAnnotations"); if(vaX != vaA) { errors++; failed.add(++checks + " " + testcase + ": (vaX) " + vaX + " != " + vaA + " (vaA)"); println(checks + " FAIL: " + description); } else println(++checks + " PASS: " + description); description=descr.replace("_TYPE_","RuntimeInvisibleAnnotations"); if(iaX != iaA) { errors++; failed.add(++checks + " " + testcase + ": (iaX) " + iaX + " != " + iaA + " (iaA)"); println(checks + " FAIL: " + description); } else println(++checks + " PASS: " + description); println(""); } // Print failed cases (if any) and throw exception for fail. void report() { if(errors!=0) { System.err.println("Failed tests: " + errors + "\nfailed test cases:\n"); for(String t: failed) System.err.println(" " + t); throw new RuntimeException("FAIL: There were test failures."); } else System.out.println("PASSED all tests."); } void test(String ttype, ClassFile cf, Method m, Field f, boolean visible) { int vtaActual = 0, itaActual = 0, vaActual = 0, iaActual = 0, vtaExp = 0, itaExp = 0, vaExp = 0, iaExp = 0, index = 0, index2 = 0; String memberName = null, testcase = "undefined", testClassName = null; Attribute attr = null, cattr = null; Code_attribute CAttr = null; // Get counts of 4 annotation Attributes on element being checked. for (String AnnoType : AnnoAttributes) { try { switch(ttype) { case "METHOD": index = m.attributes.getIndex(cf.constant_pool, AnnoType); memberName = m.getName(cf.constant_pool); if(index != -1) attr = m.attributes.get(index); //fetch index annotations from code attribute. index2 = m.attributes.getIndex(cf.constant_pool, Attribute.Code); if(index2 != -1) { cattr = m.attributes.get(index2); assert cattr instanceof Code_attribute; CAttr = (Code_attribute)cattr; index2 = CAttr.attributes.getIndex(cf.constant_pool, AnnoType); if(index2 != -1) cattr = CAttr.attributes.get(index2); } break; case "FIELD": index = f.attributes.getIndex(cf.constant_pool, AnnoType); memberName = f.getName(cf.constant_pool); if(index != -1) attr = f.attributes.get(index); //fetch index annotations from code attribute. index2 = cf.attributes.getIndex(cf.constant_pool, Attribute.Code); if(index2!= -1) { cattr = cf.attributes.get(index2); assert cattr instanceof Code_attribute; CAttr = (Code_attribute)cattr; index2 = CAttr.attributes.getIndex(cf.constant_pool, AnnoType); if(index2!= -1) cattr = CAttr.attributes.get(index2); } break; default: memberName = cf.getName(); index = cf.attributes.getIndex(cf.constant_pool, AnnoType); if(index!= -1) attr = cf.attributes.get(index); break; } } catch(ConstantPoolException cpe) { cpe.printStackTrace(); } try { testClassName=cf.getName(); testcase = ttype + ": " + testClassName + ": " + memberName + ", "; } catch(ConstantPoolException cpe) { cpe.printStackTrace(); } if(index != -1) { switch(AnnoType) { case Attribute.RuntimeVisibleTypeAnnotations: //count RuntimeVisibleTypeAnnotations RuntimeVisibleTypeAnnotations_attribute RVTAa = (RuntimeVisibleTypeAnnotations_attribute)attr; vtaActual += RVTAa.annotations.length; break; case Attribute.RuntimeVisibleAnnotations: //count RuntimeVisibleAnnotations RuntimeVisibleAnnotations_attribute RVAa = (RuntimeVisibleAnnotations_attribute)attr; vaActual += RVAa.annotations.length; break; case Attribute.RuntimeInvisibleTypeAnnotations: //count RuntimeInvisibleTypeAnnotations RuntimeInvisibleTypeAnnotations_attribute RITAa = (RuntimeInvisibleTypeAnnotations_attribute)attr; itaActual += RITAa.annotations.length; break; case Attribute.RuntimeInvisibleAnnotations: //count RuntimeInvisibleAnnotations RuntimeInvisibleAnnotations_attribute RIAa = (RuntimeInvisibleAnnotations_attribute)attr; iaActual += RIAa.annotations.length; break; } } ////////////////////////////////////////////////////////////////// if(index2 != -1) { switch(AnnoType) { case Attribute.RuntimeVisibleTypeAnnotations: //count RuntimeVisibleTypeAnnotations RuntimeVisibleTypeAnnotations_attribute RVTAa = (RuntimeVisibleTypeAnnotations_attribute)cattr; vtaActual += RVTAa.annotations.length; break; case Attribute.RuntimeVisibleAnnotations: //count RuntimeVisibleAnnotations RuntimeVisibleAnnotations_attribute RVAa = (RuntimeVisibleAnnotations_attribute)cattr; vaActual += RVAa.annotations.length; break; case Attribute.RuntimeInvisibleTypeAnnotations: //count RuntimeInvisibleTypeAnnotations RuntimeInvisibleTypeAnnotations_attribute RITAa = (RuntimeInvisibleTypeAnnotations_attribute)cattr; itaActual += RITAa.annotations.length; break; case Attribute.RuntimeInvisibleAnnotations: //count RuntimeInvisibleAnnotations RuntimeInvisibleAnnotations_attribute RIAa = (RuntimeInvisibleAnnotations_attribute)cattr; iaActual += RIAa.annotations.length; break; } } ////////////////////////////////////////////////////////////////// } switch ( memberName ) { //METHODs case "test" : vtaExp=4; itaExp=4; vaExp=0; iaExp=0;//from IN if(testClassName.compareTo("Test4")==0) { println("8008769: workaround " + testClassName + ":test"); vtaExp=2; itaExp=2; vaExp=0; iaExp=0; } break; case "mtest": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "m1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "m2": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "m3": vtaExp=10; itaExp=10; vaExp=1; iaExp=1; break; case "tm": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; //inner class case "i_m1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "i_m2": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "i_um": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; //local class case "l_m1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "l_m2": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "l_um": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; //anon class case "mm_m1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "mm_m2": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "mm_m3": vtaExp=10; itaExp=10; vaExp=1; iaExp=1; break; case "mm_tm": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; //InnerAnon class case "ia_m1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "ia_m2": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "ia_um": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; //FIELDs case "data": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "odata1": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "pdata1": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "tdata": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "sa1": vtaExp = 6; itaExp=6; vaExp=1; iaExp=1; break; //inner class case "i_odata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "i_pdata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "i_udata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "i_sa1": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; case "i_tdata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; //local class case "l_odata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "l_pdata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "l_udata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "l_sa1": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; case "l_tdata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; //anon class case "mm_odata1": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "mm_pdata1": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; case "mm_sa1": vtaExp = 6; itaExp=6; vaExp=1; iaExp=1; break; case "mm_tdata": vtaExp = 2; itaExp=2; vaExp=1; iaExp=1; break; // InnerAnon class case "ia_odata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "ia_pdata1": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "ia_udata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "ia_sa1": vtaExp=6; itaExp=6; vaExp=1; iaExp=1; break; case "ia_tdata": vtaExp=2; itaExp=2; vaExp=1; iaExp=1; break; case "IA": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; case "IN": vtaExp=4; itaExp=4; vaExp=1; iaExp=1; break; default: vtaExp = 0; itaExp=0; vaExp=0; iaExp=0; break; } check(testcase,vtaExp, itaExp, vaExp, iaExp, vtaActual,itaActual,vaActual,iaActual); } public void run() { ClassFile cf = null; InputStream in = null; int testcount = 1; File testFile = null; // Generate source, check methods and fields for each combination. for(Boolean[] bCombo : bRepeat) { As=bCombo[0]; Bs=bCombo[1]; Cs=bCombo[2]; Ds=bCombo[3]; TAs=bCombo[4]; TBs=bCombo[5]; String testname = "Test" + testcount++; println("Combinations: " + As + ", " + Bs + ", " + Cs + ", " + Ds + ", " + TAs + ", " + TBs + "; see " + testname + ".java"); String[] classes = {testname + ".class", testname + "$Inner.class", testname + "$1Local1.class", testname + "$1.class", testname + "$1$1.class", testname + "$1$InnerAnon.class" }; // Create test source and File. String sourceString = getSource(testname, As, Bs, Cs, Ds, TAs, TBs); //(sourceString); try { testFile = writeTestFile(testname+".java", sourceString); } catch( java.io.IOException ioe) { ioe.printStackTrace(); } // Compile test source and read classfile. File classFile = null; try { classFile = compile(testFile); } catch (Error err) { System.err.println("FAILED compile. Source:\n" + sourceString); throw err; } String testloc = classFile.getAbsolutePath().substring( 0,classFile.getAbsolutePath().indexOf(classFile.getPath())); for(String clazz : classes) { try { cf = ClassFile.read(new File(testloc+clazz)); } catch(Exception e) { e.printStackTrace(); } // Test for all methods and fields for (Method m: cf.methods) { test("METHOD", cf, m, null, true); } for (Field f: cf.fields) { test("FIELD", cf, null, f, true); } } } report(); } // test class template String getSource(String testname, Boolean Arepeats, Boolean Brepeats, Boolean Crepeats, Boolean Drepeats, Boolean TArepeats, Boolean TBrepeats ) { String As = Arepeats ? "@A @A":"@A", Bs = Brepeats ? "@B @B":"@B", Cs = Crepeats ? "@C @C":"@C", Ds = Drepeats ? "@D @D":"@D", TAs = TArepeats ? "@TA @TA":"@TA", TBs = TBrepeats ? "@TB @TB":"@TB"; String imports = new String("import java.lang.annotation.*; \n" + "import static java.lang.annotation.RetentionPolicy.*; \n" + "import static java.lang.annotation.ElementType.*; \n" + "import java.util.List; \n\n"); String testsource = "class " + testname + " { // TestN.class\n" + " _As _Bs _Cs _Ds String data = \"test\";\n" + " _As _Bs _Cs _Ds Object mtest( _As _Bs _Cs _Ds " + testname + " t){ return null; }\n" + " Object mmtest( " + testname + " t){ return null; }\n" + "\n" + " class Inner { // TestN$1$InnerAnon.class\n" + " _As _Bs _Cs _Ds String i_odata1 = \"test\";\n" + " _As _Bs _Cs _Ds int i_pdata1 = 0;\n" + " _As _Bs _Cs _Ds U i_udata = null;\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds [] _As _Bs _Cs _Ds [] i_sa1 = null;\n" + " _As _Bs _Cs _Ds T i_tdata = null;\n" + " _As _Bs _Cs _Ds String i_m1(){ return null; };\n" + " _As _Bs _Cs _Ds int i_m2( _As _Bs _Cs _Ds Object o){return 0;}\n" + " _As _Bs _Cs _Ds\n" + " <_TAs _TBs _Cs _Ds U> Object i_um( _As _Bs _Cs _Ds U u) { return null; }\n" + " }\n" + " _As _Bs _Cs _Ds Inner< _As _Bs _Cs _Ds String> IN = new Inner< String>();\n" + "\n" + " public void test() {\n" + " \n" + " class Local1 { // TestN$Local1.class\n" + " _As _Bs _Cs _Ds String l_odata1 = \"test\";\n" + " _As _Bs _Cs _Ds int l_pdata1 = 0;\n" + " _As _Bs _Cs _Ds U l_udata = null;\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] l_sa1 = null;\n" + " _TAs _TBs _Cs _Ds T l_tdata = null;\n" + " _As _Bs _Cs _Ds String l_m1(){ return null; };\n" + " _As _Bs _Cs _Ds int l_m2(_As _Bs _Cs _Ds Object o){return 0;}\n" + " _As _Bs _Cs _Ds \n" + " <_TAs _TBs _Cs _Ds U> Object l_um(_As _Bs _Cs _Ds U u) { return null; }\n" + " }\n" + " // The below, as a local variable, will show up on test()\n" + " _As _Bs _Cs _Ds Local1<_As _Bs _Cs _Ds String> LC = new Local1();\n" + " \n" + " mtest( new " + testname + "() { // TestN$1\n" + " class InnerAnon { // TestN$1$InnerAnon.class\n" + " _As _Bs _Cs _Ds String ia_odata1 = \"test\";\n" + " _As _Bs _Cs _Ds int ia_pdata1 = 0;\n" + " _As _Bs _Cs _Ds U ia_udata = null;\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] ia_sa1 = null;\n" + " _TAs _TBs _Cs _Ds T ia_tdata = null;\n" + " _As _Bs _Cs _Ds String ia_m1(){ return null; };\n" + " _As _Bs _Cs _Ds int ia_m2(_As _Bs _Cs _Ds Object o){return 0;}\n" + " _As _Bs _Cs _Ds \n" + " <_TAs _TBs _Cs _Ds U> Object ia_um(_As _Bs _Cs _Ds U u) { return null; }\n" + " }\n" + " _As _Bs _Cs _Ds InnerAnon<_As _Bs _Cs _Ds String> IA = new InnerAnon< String>();\n" + "\n" + " _As _Bs _Cs _Ds String odata1 = \"test\";\n" + " _As _Bs _Cs _Ds int pdata1 = 0;\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] sa1 = null;\n" + " _As _Bs _Cs _Ds T tdata = null;\n" + "\n" + " _As _Bs _Cs _Ds String m1(){ return null; };\n" + " _As _Bs _Cs _Ds int m2(_As _Bs _Cs _Ds Object o){return 0;}\n" + "\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds [] _As _Bs _Cs _Ds []\n" + " m3(String _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] sa){ return null; }\n" + "\n" + " _As _Bs _Cs _Ds\n" + " <_TAs _TBs _Cs _Ds T> Object tm(_As _Bs _Cs _Ds T t) { return null; }\n" + "\n" + " public void atest( " + testname + " t){\n" + " t.mmtest( new " + testname + "() { // TestN$1$1.class \n" + " _As _Bs _Cs _Ds String mm_odata1 = \"test\";\n" + " _As _Bs _Cs _Ds int mm_pdata1 = 0;\n" + " _As _Bs _Cs _Ds Object _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] mm_sa1 = null;\n" + " _TAs _TBs _Cs _Ds T mm_tdata = null;\n" + "\n" + " _As _Bs _Cs _Ds String mm_m1(){ return null; };\n" + " _As _Bs _Cs _Ds int mm_m2(_As _Bs _Cs _Ds Object o){return 0;}\n" + "\n" + " _As _Bs _Cs _Ds String _As _Bs _Cs _Ds [] _As _Bs _Cs _Ds []\n" + " mm_m3(String _As _Bs _Cs _Ds []_As _Bs _Cs _Ds [] sa){ return null; }\n" + "\n" + " _As _Bs _Cs _Ds\n" + " <_TAs _TBs _Cs _Ds T> Object mm_tm(_As _Bs _Cs _Ds T t) { return null; }\n" + " });\n" + " }\n" + " });\n" + " }\n" + "}\n\n" + "@Retention(RUNTIME) @Target({TYPE_USE,FIELD}) @Repeatable( AC.class ) @interface A { }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,METHOD}) @Repeatable( BC.class ) @interface B { }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,FIELD}) @interface AC { A[] value(); }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,METHOD}) @interface BC { B[] value(); }\n" + "\n" + "@Retention(CLASS) @Target({TYPE_USE,FIELD}) @Repeatable( CC.class ) @interface C { }\n" + "@Retention(CLASS) @Target({TYPE_USE,METHOD}) @Repeatable( DC.class ) @interface D { }\n" + "@Retention(CLASS) @Target({TYPE_USE,FIELD}) @interface CC { C[] value(); }\n" + "@Retention(CLASS) @Target({TYPE_USE,METHOD}) @interface DC { D[] value(); }\n" + "\n" + "@Retention(RUNTIME) @Target({TYPE_USE,TYPE_PARAMETER,FIELD}) @Repeatable( TAC.class ) @interface TA { }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,TYPE_PARAMETER,METHOD}) @Repeatable( TBC.class ) @interface TB { }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,TYPE_PARAMETER,FIELD}) @interface TAC { TA[] value(); }\n" + "@Retention(RUNTIME) @Target({TYPE_USE,TYPE_PARAMETER,METHOD}) @interface TBC { TB[] value(); }\n"; return imports.concat(testsource).replace("_As",As).replace("_Bs",Bs).replace("_Cs",Cs).replace("_Ds",Ds).replace("_TAs",TAs).replace("_TBs",TBs); } }