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         src7p("(repeating) type annotations in use of instanceof with type test pattern", true),
  49         src8("(repeating) type annotations in use of instanceof in method", true),
  50         src8p("(repeating) type annotations in use of instanceof with type test pattern in method", true);
  51 
  52         String description;
  53         Boolean local;
  54 
  55         srce(String desc, Boolean b) {
  56             this.description = this + ": " +desc;
  57             this.local = b;
  58         }
  59     }
  60 
  61 
  62     String[] ETypes={"TYPE", "FIELD", "METHOD", "PARAMETER", "CONSTRUCTOR",
  63                      "LOCAL_VARIABLE", "ANNOTATION_TYPE", "PACKAGE"};
  64 
  65     // local class tests will have an inner class.
  66     Boolean hasInnerClass=false;
  67     String innerClassname="";
  68 
  69     public static void main(String[] args) throws Exception {
  70         new CombinationsTargetTest2().run();
  71     }
  72 
  73     void run() throws Exception {
  74         // Determines which repeat and order in source(ABMix).
  75         Boolean As= false, BDs=true, ABMix=false;
  76         int testrun=0;
  77         Boolean [][] bRepeat = new Boolean[][]{{false,false,false},//no repeats
  78                                                {true,false,false}, //repeat @A
  79                                                {false,true,false}, //repeat @B
  80                                                {true,true,false},  //repeat both
  81                                                {false,false,true}  //repeat mix
  82         };
  83 
  84         for(Boolean[] bCombo : bRepeat) {
  85             As=bCombo[0]; BDs=bCombo[1]; ABMix=bCombo[2];
  86             for(String et : ETypes) {
  87                switch(et) {
  88                    case "METHOD":
  89                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src1);
  90                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src1);
  91                        test( 2, 0, 2, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src5);
  92                        test( 0, 2, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src5);
  93                        test( 0, 0, 2, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src6);
  94                        test( 0, 0, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src6);
  95                        test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7);
  96                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7);
  97                        test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7p);
  98                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7p);
  99                        test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8);
 100                        test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8);
 101                        test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8p);
 102                        test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8p);
 103                        break;
 104                    case "FIELD":
 105                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src1);
 106                        test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src2);
 107                        test( 6, 0, 0, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src3);
 108                        test( 2, 0, 2, 0, As, BDs, ABMix, "CLASS",   et, ++testrun, srce.src4);
 109                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src1);
 110                        test( 0, 8, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src2);
 111                        test( 0, 6, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src3);
 112                        test( 0, 2, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src4);
 113                        break;
 114                    default:/*TYPE,PARAMETER,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE*/
 115                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src4);
 116                        test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src5);
 117                        break;
 118                }
 119             }
 120         }
 121     }
 122 
 123     public void test(int tinv, int tvis, int inv, int vis, Boolean Arepeats,
 124                      Boolean BDrepeats, Boolean ABmix, String rtn, String et2,
 125                      Integer N, srce source) throws Exception {
 126         ++testcount;
 127         expected_tvisibles = tvis;
 128         expected_tinvisibles = tinv;
 129         expected_visibles = vis;
 130         expected_invisibles = inv;
 131         File testFile = null;
 132         String tname="Test" + N.toString();
 133         hasInnerClass=false;
 134         String testDef = "Test " + testcount + " parameters: tinv=" + tinv +
 135                 ", tvis=" + tvis + ", inv=" + inv + ", vis=" + vis +
 136                 ", Arepeats=" + Arepeats + ", BDrepeats=" + BDrepeats +
 137                 ", ABmix=" + ABmix + ", retention: " + rtn + ", anno2: " +
 138                 et2 + ", src=" + source + "\n    " + source.description;
 139 
 140         println(testDef);
 141         // Create test source and File.
 142         String sourceString = sourceString(tname, rtn, et2, Arepeats,
 143                                            BDrepeats, ABmix, source);
 144         testFile = writeTestFile(tname+".java", sourceString);
 145         // Compile test source and read classfile.
 146         File classFile = null;
 147         try {
 148             classFile = compile(testFile);
 149         } catch (Error err) {
 150             System.err.println("Failed compile. Source:\n" + sourceString);
 151             throw err;
 152         }
 153         //if sourcString() set hasInnerClass it also set innerClassname.
 154         if(hasInnerClass) {
 155             StringBuffer sb = new StringBuffer(classFile.getAbsolutePath());
 156             classFile=new File(sb.insert(sb.lastIndexOf(".class"),innerClassname).toString());
 157             println("classfile: " + classFile.getAbsolutePath());
 158         }
 159         ClassFile cf = ClassFile.read(classFile);
 160 
 161         //Test class,fields and method counts.
 162         test(cf);
 163 
 164         for (Field f : cf.fields) {
 165             if(source.local)
 166                 test(cf, f, true);
 167             else
 168                 test(cf,f);
 169         }
 170         for (Method m: cf.methods) {
 171             if(source.local)
 172                 test(cf, m, true);
 173             else
 174                 test(cf, m);
 175         }
 176         countAnnotations();
 177         if (errors > 0) {
 178             System.err.println( testDef );
 179             System.err.println( "Source:\n" + sourceString );
 180             throw new Exception( errors + " errors found" );
 181         }
 182         println("Pass");
 183     }
 184 
 185     // Source for test cases
 186     String sourceString(String testname, String retentn, String annot2,
 187                         Boolean Arepeats, Boolean BDrepeats, Boolean ABmix,
 188                         srce src) {
 189 
 190         String As = "@A", Bs = "@B", Ds = "@D";
 191         if(Arepeats) As = "@A @A";
 192         if(BDrepeats) {
 193             Bs = "@B @B";
 194             Ds = "@D @D";
 195         }
 196         if(ABmix) { As = "@A @B"; Bs = "@A @B"; Ds = "@D @D"; }
 197 
 198         // Source to check for TYPE_USE and TYPE_PARAMETER annotations.
 199         // Source base (annotations) is same for all test cases.
 200         String source = new String();
 201         String imports = new String("import java.lang.annotation.*; \n" +
 202             "import static java.lang.annotation.RetentionPolicy.*; \n" +
 203             "import static java.lang.annotation.ElementType.*; \n" +
 204             "import java.util.List; \n" +
 205             "import java.util.HashMap; \n" +
 206             "import java.util.Map; \n\n");
 207 
 208             String sourceBase = new String("@Retention("+retentn+")\n" +
 209             "@Target({TYPE_USE,_OTHER_})\n" +
 210             "@Repeatable( AC.class )\n" +
 211             "@interface A { }\n\n" +
 212 
 213             "@Retention("+retentn+")\n" +
 214             "@Target({TYPE_USE,_OTHER_})\n" +
 215             "@interface AC { A[] value(); }\n\n" +
 216 
 217             "@Retention("+retentn+")\n" +
 218             "@Target({TYPE_USE,_OTHER_})\n" +
 219             "@Repeatable( BC.class )\n" +
 220             "@interface B { }\n\n" +
 221 
 222             "@Retention("+retentn+")\n" +
 223             "@Target({TYPE_USE,_OTHER_})\n" +
 224             "@interface BC { B[] value(); } \n\n" +
 225 
 226             "@Retention("+retentn+")\n" +
 227             "@Target({TYPE_USE,TYPE_PARAMETER,_OTHER_})\n" +
 228             "@Repeatable(DC.class)\n" +
 229             "@interface D { }\n\n" +
 230 
 231             "@Retention("+retentn+")\n" +
 232             "@Target({TYPE_USE,TYPE_PARAMETER,_OTHER_})\n" +
 233             "@interface DC { D[] value(); }\n\n");
 234 
 235         // Test case sources with sample generated source
 236         switch(src) {
 237             case src1: // (repeating) type annotations on field in method body
 238                     /*
 239                      * class Test1 {
 240                      * Test1(){}
 241                      * // type usage in method body
 242                      * String test(Test1 this, String param, String ... vararg) {
 243                      *     @A @B
 244                      *     Object o = new @A @B  String @A @B  [3];
 245                      *         return (@A @B  String) null;
 246                      * }}
 247                       */
 248                 source = new String(
 249                     "// " + src.description + "\n" +
 250                     "class " + testname + " {\n" +
 251                     "" + testname +"(){} \n" +
 252                     "// type usage in method body \n" +
 253                     "String test("+testname+" this, " +
 254                        "String param, String ... vararg) { \n" +
 255                     "    _As_ _Bs_\n    Object o = new _As_ _Bs_  String _As_ _Bs_  [3]; \n" +
 256                     "        return (_As_ _Bs_  String) null; \n" +
 257                     "} \n" +
 258                     "} \n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 259                     "\n\n";
 260                     break;
 261             case src2: // (repeating) annotations on type parameters, bounds and  type arguments in new statement.
 262                     /*
 263                      * class Test2<T extends Object> {
 264                      *     Map<List<String>, Integer> map =
 265                      *         new HashMap<@A @B List<@A @B String>, @A @B Integer>();
 266                      *     Map<List<String>, Integer> map2 = new @A @B HashMap<>();
 267                      *     String test(Test2<T> this) { return null;}
 268                      *     <T> String genericMethod(T t) { return null; }
 269                      * }
 270                      */
 271                 source = new String( source +
 272                     "// " + src.description + "\n" +
 273                     "class " + testname + "<T extends Object> {\n" +
 274                     "    Map<List<String>, Integer> map =\n" +
 275                     "        new HashMap<_As_ _Bs_ List<_As_ _Bs_ String>, _As_ _Bs_ Integer>();\n" +
 276                     "    Map<List<String>, Integer> map2 = new _As_ _Bs_ HashMap<>();\n" +
 277                     "    String test(" + testname + "<T> this) { return null;}\n" +
 278                     "    <T> String genericMethod(T t) { return null; }\n" +
 279                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 280                     "\n\n";
 281                 break;
 282             case src3: // (repeating)annotations on type parameters of class, method return value in method.
 283                     /*
 284                      * class Test3{
 285                      *     <E extends Comparable> Map<List<E>, E > foo(E e) {
 286                      *         class maptest <E> {
 287                      *             Map<List<E>,E> getMap() {
 288                      *                 Map<List<E>,E> Em = new HashMap<List<@A @B @D E>,@A @B @D E>();
 289                      *                 return Em;
 290                      *             }
 291                      *         }
 292                      *         return new maptest<E>().getMap();
 293                      *    }
 294                      *    Map<List<String>,String> shm = foo(new String("hello"));
 295                      * }
 296                      */
 297                 source = new String( source +
 298                     "// " + src.description + "\n" +
 299                     "class "+ testname + "{\n" +
 300                     "    <E extends Comparable> Map<List<E>, E > foo(E e) {\n" +
 301                     "        class maptest <E> {\n" +                  // inner class $1maptest
 302                     "            Map<List<E>,E> getMap() { \n" +
 303                     "                Map<List<E>,E> Em = new HashMap<List<_As_ _Bs_ _Ds_ E>,_As_ _Bs_ _Ds_ E>();\n" +
 304                     "                return Em;\n" +
 305                     "            }\n" +
 306                     "        }\n" +
 307                     "        return new maptest<E>().getMap();\n" +
 308                     "   }\n" +
 309                     "   Map<List<String>,String> shm = foo(new String(\"hello\"));\n" +
 310                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs).replace("_Ds_",Ds) +
 311                     "\n\n";
 312                     hasInnerClass=true;
 313                     innerClassname="$1maptest";
 314                 break;
 315             case src4: // (repeating)annotations on field in anonymous class
 316                     /*
 317                      * class Test95{
 318                      *     void mtest( Test95 t){  }
 319                      *     public void test() {
 320                      *         mtest( new Test95() {
 321                      *             @A @A @B @B String data2 = "test";
 322                      *         });
 323                      *     }
 324                      * }
 325                      */
 326                 source = new String( source +
 327                     "// " + src.description + "\n" +
 328                     "class "+ testname + "{\n" +
 329                     "    void mtest( "+ testname + " t){  }\n" +
 330                     "    public void test() {\n" +
 331                     "        mtest( new "+ testname + "() {\n" +
 332                     "            _As_ _Bs_ String data2 = \"test\";\n" +
 333                     "        });\n" +
 334                     "    }\n" +
 335                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 336                     "\n\n";
 337                     hasInnerClass=true;
 338                     innerClassname="$1";
 339                 break;
 340             case src5: // (repeating)annotations on method in anonymous class
 341                     /*
 342                      * class Test120{
 343                      *     void mtest( Test120 t){  }
 344                      *     public void test() {
 345                      *         mtest( new Test120() {
 346                      *             @A @B @A @B String m2(){return null;};
 347                      *         });
 348                      *     }
 349                      */
 350                 source = new String( source +
 351                     "// " + src.description + "\n" +
 352                     "class "+ testname + "{\n" +
 353                     "    void mtest( "+ testname + " t){  }\n" +
 354                     "    public void test() {\n" +
 355                     "        mtest( new "+ testname + "() {\n" +
 356                     "            _As_ _Bs_ String m2(){return null;};\n" +
 357                     "        });\n" +
 358                     "    }\n" +
 359                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 360                     "\n\n";
 361                     hasInnerClass=true;
 362                     innerClassname="$1";
 363                 break;
 364             case src6: // (repeating)annotations on void method declaration
 365                     /*
 366                      * class Test95{
 367                      *     @A @A @B @B public void test() { };
 368                      * }
 369                      */
 370                 source = new String( source +
 371                     "// " + src.description + "\n" +
 372                     "class "+ testname + "{\n" +
 373                     "    _As_ _Bs_ public void test() { }\n" +
 374                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 375                     "\n\n";
 376                     hasInnerClass=false;
 377                 break;
 378             case src7: // (repeating) type annotations in use of instanceof
 379                     /*
 380                      *   class Test10{
 381                      *       String data = "test";
 382                      *       boolean dataIsString = ( data instanceof @A @B @A @B String);
 383                      *   }
 384                      */
 385                 source = new String( source +
 386                     "// " + src.description + "\n" +
 387                     "class "+ testname + "{\n" +
 388                     "    String data = \"test\";\n" +
 389                     "    boolean dataIsString = ( data instanceof _As_ _Bs_ String);\n" +
 390                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 391                     "\n\n";
 392                     hasInnerClass=false;
 393                 break;
 394             case src7p: // (repeating) type annotations in use of instanceof with type test pattern
 395                     /*
 396                      *   class Test10{
 397                      *       String data = "test";
 398                      *       boolean dataIsString = ( data instanceof @A @B @A @B String str);
 399                      *   }
 400                      */
 401                 source = new String( source +
 402                     "// " + src.description + "\n" +
 403                     "class "+ testname + "{\n" +
 404                     "    String data = \"test\";\n" +
 405                     "    boolean dataIsString = ( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
 406                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 407                     "\n\n";
 408                     hasInnerClass=false;
 409                 break;
 410             case src8: // (repeating) type annotations in use of instanceof
 411                     /*
 412                      *   class Test20{
 413                      *       String data = "test";
 414                      *       Boolean isString() {
 415                      *           if( data instanceof @A @B @A @B String )
 416                      *               return true;
 417                      *           else
 418                      *               return( data instanceof @A @B @A @B String );
 419                      *       }
 420                      *   }
 421                      */
 422                 source = new String( source +
 423                     "// " + src.description + "\n" +
 424                     "class "+ testname + "{\n" +
 425                     "    String data = \"test\";\n" +
 426                     "    Boolean isString() { \n" +
 427                     "        if( data instanceof _As_ _Bs_ String )\n" +
 428                     "            return true;\n" +
 429                     "        else\n" +
 430                     "            return( data instanceof _As_ _Bs_ String );\n" +
 431                     "    }\n" +
 432                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 433                     "\n\n";
 434                     hasInnerClass=false;
 435                 break;
 436             case src8p: // (repeating) type annotations in use of instanceof with type test pattern
 437                    /*
 438                      *   class Test20{
 439                      *       String data = "test";
 440                      *       Boolean isString() {
 441                      *           if( data instanceof @A @B @A @B String )
 442                      *               return true;
 443                      *           else
 444                      *               return( data instanceof @A @B @A @B String );
 445                      *       }
 446                      *   }
 447                      */
 448                 source = new String( source +
 449                     "// " + src.description + "\n" +
 450                     "class "+ testname + "{\n" +
 451                     "    String data = \"test\";\n" +
 452                     "    Boolean isString() { \n" +
 453                     "        if( data instanceof _As_ _Bs_ String str)\n" +
 454                     "            return true;\n" +
 455                     "        else\n" +
 456                     "            return( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
 457                     "    }\n" +
 458                     "}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
 459                     "\n\n";
 460                     hasInnerClass=false;
 461                 break;
 462 
 463         }
 464         return imports + source;
 465     }
 466 }