1 /*
   2  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8005085 8005877 8004829 8005681 8006734 8006775 8006507
  27  * @summary Combinations of Target ElementTypes on (repeated)type annotations.
  28  * @modules jdk.jdeps/com.sun.tools.classfile
  29  */
  30 
  31 import com.sun.tools.classfile.*;
  32 import java.io.File;
  33 
  34 public class CombinationsTargetTest2 extends ClassfileTestHelper {
  35 
  36     // Test count helps identify test case in event of failure.
  37     int testcount = 0;
  38 
  39     // Base test case template descriptions;true==annotations in code attribute.
  40     enum srce  {
  41         src1("(repeating) type annotations on on field in method body",true),
  42         src2("(repeating) type annotations on type parameters, bounds and  type arguments", true),
  43         src3("(repeating) type annotations on type parameters of class, method return value in method", true),
  44         src4("(repeating) type annotations on field in anonymous class", false),
  45         src5("(repeating) type annotations on field in anonymous class", false),
  46         src6("(repeating) type annotations on void method declaration", false),
  47         src7("(repeating) type annotations in use of instanceof", true),
  48         src8("(repeating) type annotations in use of instanceof in method", true);
  49 
  50         String description;
  51         Boolean local;
  52 
  53         srce(String desc, Boolean b) {
  54             this.description = this + ": " +desc;
  55             this.local = b;
  56         }
  57     }
  58 
  59 
  60     String[] ETypes={"TYPE", "FIELD", "METHOD", "PARAMETER", "CONSTRUCTOR",
  61                      "LOCAL_VARIABLE", "ANNOTATION_TYPE", "PACKAGE"};
  62 
  63     // local class tests will have an inner class.
  64     Boolean hasInnerClass=false;
  65     String innerClassname="";
  66 
  67     public static void main(String[] args) throws Exception {
  68         new CombinationsTargetTest2().run();
  69     }
  70 
  71     void run() throws Exception {
  72         // Determines which repeat and order in source(ABMix).
  73         Boolean As= false, BDs=true, ABMix=false;
  74         int testrun=0;
  75         Boolean [][] bRepeat = new Boolean[][]{{false,false,false},//no repeats
  76                                                {true,false,false}, //repeat @A
  77                                                {false,true,false}, //repeat @B
  78                                                {true,true,false},  //repeat both
  79                                                {false,false,true}  //repeat mix
  80         };
  81 
  82         for(Boolean[] bCombo : bRepeat) {
  83             As=bCombo[0]; BDs=bCombo[1]; ABMix=bCombo[2];
  84             for(String et : ETypes) {
  85                switch(et) {
  86                    case "METHOD":
  87                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src1);
  88                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src1);
  89                        test( 2, 0, 2, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src5);
  90                        test( 0, 2, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src5);
  91                        test( 0, 0, 2, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src6);
  92                        test( 0, 0, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src6);
  93                        test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7);
  94                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7);
  95                        test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8);
  96                        test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8);
  97                        break;
  98                    case "FIELD":
  99                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src1);
 100                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src2);
 101                        test( 6, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src3);
 102                        test( 2, 0, 2, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src4);
 103                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src1);
 104                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src2);
 105                        test( 0, 6, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src3);
 106                        test( 0, 2, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src4);
 107                        break;
 108                    default:/*TYPE,PARAMETER,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE*/
 109                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src4);
 110                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src5);
 111                        break;
 112                }
 113             }
 114         }
 115     }
 116 
 117     public void test(int tinv, int tvis, int inv, int vis, Boolean Arepeats,
 118                      Boolean BDrepeats, Boolean ABmix, String rtn, String et2,
 119                      Integer N, srce source) throws Exception {
 120         ++testcount;
 121         expected_tvisibles = tvis;
 122         expected_tinvisibles = tinv;
 123         expected_visibles = vis;
 124         expected_invisibles = inv;
 125         File testFile = null;
 126         String tname="Test" + N.toString();
 127         hasInnerClass=false;
 128         String testDef = "Test " + testcount + " parameters: tinv=" + tinv +
 129                 ", tvis=" + tvis + ", inv=" + inv + ", vis=" + vis +
 130                 ", Arepeats=" + Arepeats + ", BDrepeats=" + BDrepeats +
 131                 ", ABmix=" + ABmix + ", retention: " + rtn + ", anno2: " +
 132                 et2 + ", src=" + source + "\n    " + source.description;
 133 
 134         println(testDef);
 135         // Create test source and File.
 136         String sourceString = sourceString(tname, rtn, et2, Arepeats,
 137                                            BDrepeats, ABmix, source);
 138         testFile = writeTestFile(tname+".java", sourceString);
 139         // Compile test source and read classfile.
 140         File classFile = null;
 141         try {
 142             classFile = compile(testFile);
 143         } catch (Error err) {
 144             System.err.println("Failed compile. Source:\n" + sourceString);
 145             throw err;
 146         }
 147         //if sourcString() set hasInnerClass it also set innerClassname.
 148         if(hasInnerClass) {
 149             StringBuffer sb = new StringBuffer(classFile.getAbsolutePath());
 150             classFile=new File(sb.insert(sb.lastIndexOf(".class"),innerClassname).toString());
 151             println("classfile: " + classFile.getAbsolutePath());
 152         }
 153         ClassFile cf = ClassFile.read(classFile);
 154 
 155         //Test class,fields and method counts.
 156         test(cf);
 157 
 158         for (Field f : cf.fields) {
 159             if(source.local)
 160                 test(cf, f, true);
 161             else
 162                 test(cf,f);
 163         }
 164         for (Method m: cf.methods) {
 165             if(source.local)
 166                 test(cf, m, true);
 167             else
 168                 test(cf, m);
 169         }
 170         countAnnotations();
 171         if (errors > 0) {
 172             System.err.println( testDef );
 173             System.err.println( "Source:\n" + sourceString );
 174             throw new Exception( errors + " errors found" );
 175         }
 176         println("Pass");
 177     }
 178 
 179     // Source for test cases
 180     String sourceString(String testname, String retentn, String annot2,
 181                         Boolean Arepeats, Boolean BDrepeats, Boolean ABmix,
 182                         srce src) {
 183 
 184         String As = "@A", Bs = "@B", Ds = "@D";
 185         if(Arepeats) As = "@A @A";
 186         if(BDrepeats) {
 187             Bs = "@B @B";
 188             Ds = "@D @D";
 189         }
 190         if(ABmix) { As = "@A @B"; Bs = "@A @B"; Ds = "@D @D"; }
 191 
 192         // Source to check for TYPE_USE and TYPE_PARAMETER annotations.
 193         // Source base (annotations) is same for all test cases.
 194         String source = new String();
 195         String imports = new String("import java.lang.annotation.*; \n" +
 196             "import static java.lang.annotation.RetentionPolicy.*; \n" +
 197             "import static java.lang.annotation.ElementType.*; \n" +
 198             "import java.util.List; \n" +
 199             "import java.util.HashMap; \n" +
 200             "import java.util.Map; \n\n");
 201 
 202             String sourceBase = new String("@Retention("+retentn+")\n" +
 203             "@Target({TYPE_USE,_OTHER_})\n" +
 204             "@Repeatable( AC.class )\n" +
 205             "@interface A { }\n\n" +
 206 
 207             "@Retention("+retentn+")\n" +
 208             "@Target({TYPE_USE,_OTHER_})\n" +
 209             "@interface AC { A[] value(); }\n\n" +
 210 
 211             "@Retention("+retentn+")\n" +
 212             "@Target({TYPE_USE,_OTHER_})\n" +
 213             "@Repeatable( BC.class )\n" +
 214             "@interface B { }\n\n" +
 215 
 216             "@Retention("+retentn+")\n" +
 217             "@Target({TYPE_USE,_OTHER_})\n" +
 218             "@interface BC { B[] value(); } \n\n" +
 219 
 220             "@Retention("+retentn+")\n" +
 221             "@Target({TYPE_USE,TYPE_PARAMETER,_OTHER_})\n" +
 222             "@Repeatable(DC.class)\n" +
 223             "@interface D { }\n\n" +
 224 
 225             "@Retention("+retentn+")\n" +
 226             "@Target({TYPE_USE,TYPE_PARAMETER,_OTHER_})\n" +
 227             "@interface DC { D[] value(); }\n\n");
 228 
 229         // Test case sources with sample generated source
 230         switch(src) {
 231             case src1: // (repeating) type annotations on field in method body
 232                     /*
 233                      * class Test1 {
 234                      * Test1(){}
 235                      * // type usage in method body
 236                      * String test(Test1 this, String param, String ... vararg) {
 237                      *     @A @B
 238                      *     Object o = new @A @B  String @A @B  [3];
 239                      *         return (@A @B  String) null;
 240                      * }}
 241                       */
 242                 source = new String(
 243                     "// " + src.description + "\n" +
 244                     "class " + testname + " {\n" +
 245                     "" + testname +"(){} \n" +
 246                     "// type usage in method body \n" +
 247                     "String test("+testname+" this, " +
 248                        "String param, String ... vararg) { \n" +
 249                     "    _As_ _Bs_\n    Object o = new _As_ _Bs_  String _As_ _Bs_  [3]; \n" +
 250                     "        return (_As_ _Bs_  String) null; \n" +
 251                     "} \n" +
 252                     "} \n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 253                     "\n\n";
 254                     break;
 255             case src2: // (repeating) annotations on type parameters, bounds and  type arguments in new statement.
 256                     /*
 257                      * class Test2<T extends Object> {
 258                      *     Map<List<String>, Integer> map =
 259                      *         new HashMap<@A @B List<@A @B String>, @A @B Integer>();
 260                      *     Map<List<String>, Integer> map2 = new @A @B HashMap<>();
 261                      *     String test(Test2<T> this) { return null;}
 262                      *     <T> String genericMethod(T t) { return null; }
 263                      * }
 264                      */
 265                 source = new String( source +
 266                     "// " + src.description + "\n" +
 267                     "class " + testname + "<T extends Object> {\n" +
 268                     "    Map<List<String>, Integer> map =\n" +
 269                     "        new HashMap<_As_ _Bs_ List<_As_ _Bs_ String>, _As_ _Bs_ Integer>();\n" +
 270                     "    Map<List<String>, Integer> map2 = new _As_ _Bs_ HashMap<>();\n" +
 271                     "    String test(" + testname + "<T> this) { return null;}\n" +
 272                     "    <T> String genericMethod(T t) { return null; }\n" +
 273                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 274                     "\n\n";
 275                 break;
 276             case src3: // (repeating)annotations on type parameters of class, method return value in method.
 277                     /*
 278                      * class Test3{
 279                      *     <E extends Comparable> Map<List<E>, E > foo(E e) {
 280                      *         class maptest <E> {
 281                      *             Map<List<E>,E> getMap() {
 282                      *                 Map<List<E>,E> Em = new HashMap<List<@A @B @D E>,@A @B @D E>();
 283                      *                 return Em;
 284                      *             }
 285                      *         }
 286                      *         return new maptest<E>().getMap();
 287                      *    }
 288                      *    Map<List<String>,String> shm = foo(new String("hello"));
 289                      * }
 290                      */
 291                 source = new String( source +
 292                     "// " + src.description + "\n" +
 293                     "class "+ testname + "{\n" +
 294                     "    <E extends Comparable> Map<List<E>, E > foo(E e) {\n" +
 295                     "        class maptest <E> {\n" +                  // inner class $1maptest
 296                     "            Map<List<E>,E> getMap() { \n" +
 297                     "                Map<List<E>,E> Em = new HashMap<List<_As_ _Bs_ _Ds_ E>,_As_ _Bs_ _Ds_ E>();\n" +
 298                     "                return Em;\n" +
 299                     "            }\n" +
 300                     "        }\n" +
 301                     "        return new maptest<E>().getMap();\n" +
 302                     "   }\n" +
 303                     "   Map<List<String>,String> shm = foo(new String(\"hello\"));\n" +
 304                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs).replace("_Ds_",Ds) +
 305                     "\n\n";
 306                     hasInnerClass=true;
 307                     innerClassname="$1maptest";
 308                 break;
 309             case src4: // (repeating)annotations on field in anonymous class
 310                     /*
 311                      * class Test95{
 312                      *     void mtest( Test95 t){  }
 313                      *     public void test() {
 314                      *         mtest( new Test95() {
 315                      *             @A @A @B @B String data2 = "test";
 316                      *         });
 317                      *     }
 318                      * }
 319                      */
 320                 source = new String( source +
 321                     "// " + src.description + "\n" +
 322                     "class "+ testname + "{\n" +
 323                     "    void mtest( "+ testname + " t){  }\n" +
 324                     "    public void test() {\n" +
 325                     "        mtest( new "+ testname + "() {\n" +
 326                     "            _As_ _Bs_ String data2 = \"test\";\n" +
 327                     "        });\n" +
 328                     "    }\n" +
 329                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 330                     "\n\n";
 331                     hasInnerClass=true;
 332                     innerClassname="$1";
 333                 break;
 334             case src5: // (repeating)annotations on method in anonymous class
 335                     /*
 336                      * class Test120{
 337                      *     void mtest( Test120 t){  }
 338                      *     public void test() {
 339                      *         mtest( new Test120() {
 340                      *             @A @B @A @B String m2(){return null;};
 341                      *         });
 342                      *     }
 343                      */
 344                 source = new String( source +
 345                     "// " + src.description + "\n" +
 346                     "class "+ testname + "{\n" +
 347                     "    void mtest( "+ testname + " t){  }\n" +
 348                     "    public void test() {\n" +
 349                     "        mtest( new "+ testname + "() {\n" +
 350                     "            _As_ _Bs_ String m2(){return null;};\n" +
 351                     "        });\n" +
 352                     "    }\n" +
 353                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 354                     "\n\n";
 355                     hasInnerClass=true;
 356                     innerClassname="$1";
 357                 break;
 358             case src6: // (repeating)annotations on void method declaration
 359                     /*
 360                      * class Test95{
 361                      *     @A @A @B @B public void test() { };
 362                      * }
 363                      */
 364                 source = new String( source +
 365                     "// " + src.description + "\n" +
 366                     "class "+ testname + "{\n" +
 367                     "    _As_ _Bs_ public void test() { }\n" +
 368                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 369                     "\n\n";
 370                     hasInnerClass=false;
 371                 break;
 372             case src7: // (repeating) type annotations in use of instanceof
 373                     /*
 374                      *   class Test10{
 375                      *       String data = "test";
 376                      *       boolean dataIsString = ( data instanceof @A @B @A @B String);
 377                      *   }
 378                      */
 379                 source = new String( source +
 380                     "// " + src.description + "\n" +
 381                     "class "+ testname + "{\n" +
 382                     "    String data = \"test\";\n" +
 383                     "    boolean dataIsString = ( data instanceof _As_ _Bs_ String);\n" +
 384                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 385                     "\n\n";
 386                     hasInnerClass=false;
 387                 break;
 388             case src8: // (repeating) type annotations in use of instanceof
 389                     /*
 390                      *   class Test20{
 391                      *       String data = "test";
 392                      *       Boolean isString() {
 393                      *           if( data instanceof @A @B @A @B String )
 394                      *               return true;
 395                      *           else
 396                      *               return( data instanceof @A @B @A @B String );
 397                      *       }
 398                      *   }
 399                      */
 400                 source = new String( source +
 401                     "// " + src.description + "\n" +
 402                     "class "+ testname + "{\n" +
 403                     "    String data = \"test\";\n" +
 404                     "    Boolean isString() { \n" +
 405                     "        if( data instanceof _As_ _Bs_ String )\n" +
 406                     "            return true;\n" +
 407                     "        else\n" +
 408                     "            return( data instanceof _As_ _Bs_ String );\n" +
 409                     "    }\n" +
 410                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 411                     "\n\n";
 412                     hasInnerClass=false;
 413                 break;
 414 
 415         }
 416         return imports + source;
 417     }
 418 }