CombinationsTargetTest3.java revision 2942:08092deced3f
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 8005681 8008769 8010015
27 * @summary Check (repeating)type annotations on lambda usage.
28 * @modules jdk.jdeps/com.sun.tools.classfile
29 * @run main CombinationsTargetTest3
30 */
31
32import com.sun.tools.classfile.*;
33import java.io.File;
34import java.util.Vector;
35
36public class CombinationsTargetTest3 extends ClassfileTestHelper {
37
38    // Helps identify test case in event of failure.
39    int testcount = 0;
40
41    // Known failure cases due to open bugs.
42    Vector<String> skippedTests = new Vector<>();
43    void printSkips() {
44        if(!skippedTests.isEmpty()) {
45            println(skippedTests.size() + " tests were skipped:");
46            for(String t : skippedTests)
47                println("    " + t);
48        }
49    }
50
51    // Test case descriptions and expected annotation counts.
52    enum srce  {
53        src1("type annotations on lambda expression as method arg.",4,0),
54        src2("type annotations on new in single line lambda expression",2,0),
55        src3("type annotations in lambda expression code block",4,0),
56        src4("type annotations in code block with recursion,cast",2,0),
57        src5("type annotations in lambda expression code block",4,0),
58        src6("type annotations on type parm in method reference",4,0),
59        src7("type annotations on inner class field of lambda expression",2,2),
60        src8("type annotations in inner class of lambda expression",4,2),
61        src9("type annotations on static method of interface",4,2);
62
63        String description;
64        // Expected annotation counts are same for Vis or Invis, but which one
65        // depends on retention type.
66        Integer[] exp = { 0, 0 };
67
68        // If class to test is inner class, this may be set in SourceString()
69        String innerClassname = null ;
70
71        // If class to test is not main or inner class; set in sourceString()
72        String altClassName = null;
73
74        srce(String desc, int e1, int e2) {
75            description = this + ": " +desc;
76            exp[0]=e1;
77            exp[1]=e2;
78        }
79    }
80
81    // Check for RuntimeInvisible or RuntimeVisible annotations.
82    String[] RType={"CLASS", "RUNTIME"};
83
84    // This can be a compile only test.
85    static boolean compileonly=false;
86
87    // Collect failure for end of test report()
88    Vector<String> vFailures = new Vector<>();
89
90    // pass/fail determined after all tests have run.
91    void report() {
92        if(vFailures.isEmpty()) {
93            printSkips();
94            println("PASS");
95        } else {
96           System.err.println("FAILED: There were failures:");
97           for(String f : vFailures)
98               System.err.println(f);
99           throw new RuntimeException("There were failures. See test log.");
100        }
101    }
102
103    public static void main(String[] args) throws Exception {
104        if(args.length>0 && args[0].compareTo("compileonly")==0)
105            compileonly=true;
106        new CombinationsTargetTest3().run();
107    }
108
109    void run() throws Exception {
110        // Determines which repeat and order in source(ABMix).
111        Boolean As= false, BDs=true, ABMix=false;
112        int testrun=0;
113        // A repeats and/or B/D repeats, ABMix for order of As and Bs.
114        Boolean [][] bRepeat = new Boolean[][]{{false,false,false}, //no repeats
115                                               {true,false,false}, //repeat @A
116                                               {false,true,false}, //repeat @B
117                                               {true,true,false},  //repeat both
118                                               {false,false,true}  //repeat mix
119        };
120        // Added ElementType's. All set; not permuted (so far) for this test
121        String et = "TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE";
122
123        // test loop
124        for(Boolean[] bCombo : bRepeat) {
125            As=bCombo[0]; BDs=bCombo[1]; ABMix=bCombo[2];
126            for(srce src : srce.values())
127                for( String rtype : RType ) {
128                   switch( rtype ) {
129                       case "RUNTIME":
130                           test(0,src.exp[0],0,src.exp[1],As, BDs, ABMix,
131                                "RUNTIME", et, ++testrun, src);
132                           break;
133                       case "CLASS":
134                           test(src.exp[0],0,src.exp[1],0,As, BDs, ABMix,
135                                "CLASS", et, ++testrun, src);
136                           break;
137                }
138            }
139        }
140        report();
141    }
142
143    // Filter out skipped cases, compile, pass class file to test method,
144    // count annotations and asses results.
145    public void test(int tinv, int tvis, int inv, int vis, Boolean Arepeats,
146                     Boolean BDrepeats, Boolean ABmix, String rtn, String et2,
147                     Integer N, srce source) throws Exception {
148        ++testcount;
149        expected_tvisibles = tvis;
150        expected_tinvisibles = tinv;
151        expected_visibles = vis;
152        expected_invisibles = inv;
153        File testFile = null;
154        String tname="Test" + N.toString();
155        String testDef = "Test " + testcount + " parameters: tinv=" + tinv +
156                ", tvis=" + tvis + ", inv=" + inv + ", vis=" + vis +
157                ", Arepeats=" + Arepeats + ", BDrepeats=" + BDrepeats +
158                ", ABmix=" + ABmix + ", retention: " + rtn + ", anno2: " +
159                et2 + ", src=" + source;
160
161        // Skip failing cases with bug ID's
162        if ((source.equals(srce.src2) || source.equals(srce.src4) ||
163            source.equals(srce.src5)) &&
164            (ABmix || (Arepeats && BDrepeats))) {
165                skippedTests.add(testDef +
166                  "\n--8005681 repeated type-annotations on new/cast/array in" +
167                  " inner class in lambda expression.");
168            return;
169        }//8008769 Repeated type-annotations on type parm of local variable
170         else if (source.equals(srce.src6) &&
171                   (ABmix || (Arepeats && BDrepeats))) {
172            skippedTests.add(testDef +  "\n--8008769 Repeated " +
173                             "type-annotations on type parm of local variable");
174            return;
175        }
176
177        println(testDef);
178        // Create test source and File.
179        String sourceString = sourceString(tname, rtn, et2, Arepeats,
180                                           BDrepeats, ABmix, source);
181        testFile = writeTestFile(tname+".java", sourceString);
182        // Compile test source and read classfile.
183        File classFile = null;
184        try {
185            classFile = compile(testFile);
186            System.out.println("pass compile: " + tname + ".java");
187        } catch (Error err) {
188            System.err.println("fail compile. Source:\n" + sourceString);
189            throw err;
190        }
191        if(!compileonly) {
192            //check if innerClassname is set
193            String classdir = classFile.getAbsolutePath();
194            if(source.innerClassname != null) {
195                StringBuffer sb = new StringBuffer(classdir);
196                classFile=new File(sb.insert(sb.lastIndexOf(".class"),
197                                   source.innerClassname).toString());
198                source.innerClassname=null;
199            } else if (source.altClassName != null) {
200                classdir = classdir.substring(0,classdir.lastIndexOf("Test"));
201                classFile=new File(classdir.concat(source.altClassName));
202                source.innerClassname=null;
203            }
204            ClassFile cf = ClassFile.read(classFile);
205
206            println("Testing classfile: " + cf.getName());
207            //Test class,fields and method counts.
208            test(cf);
209
210            for (Field f : cf.fields) {
211                test(cf, f);
212                test(cf, f, true);
213            }
214            for (Method m: cf.methods) {
215                test(cf, m);
216                test(cf, m, true);
217            }
218
219            countAnnotations(); // sets errors=0 before counting.
220            if (errors > 0) {
221                System.err.println( testDef );
222                System.err.println( "Source:\n" + sourceString );
223                vFailures.add(testDef);
224            }
225        }
226        if(errors==0) println("Pass"); println("");
227    }
228
229    /*
230     * Source definitions for test cases.
231     * To add a test:
232     *   Add enum to srce(near top of file) with expected annotation counts.
233     *   Add source defintion below.
234     */
235    String sourceString(String testname, String retentn, String annot2,
236                        Boolean Arepeats, Boolean BDrepeats, Boolean ABmix,
237                        srce src) {
238
239        String As = "@A", Bs = "@B", Ds = "@D";
240        if(Arepeats) As = "@A @A";
241        if(BDrepeats) {
242            Bs = "@B @B";
243            Ds = "@D @D";
244        }
245        if(ABmix) { As = "@A @B"; Bs = "@A @B"; Ds = "@D @D"; }
246
247        // Source to check for TYPE_USE and TYPE_PARAMETER annotations.
248        // Source base (annotations) is same for all test cases.
249        String source = new String();
250        String imports = new String("import java.lang.annotation.*; \n" +
251            "import static java.lang.annotation.RetentionPolicy.*; \n" +
252            "import static java.lang.annotation.ElementType.*; \n" +
253            "import java.util.List; \n" +
254            "import java.util.ArrayList;\n\n");
255
256            String sourceBase = new String(
257            "@Retention("+retentn+") @Target({TYPE_USE,_OTHER_}) @Repeatable( AC.class ) @interface A { }\n" +
258            "@Retention("+retentn+") @Target({TYPE_USE,_OTHER_}) @interface AC { A[] value(); } \n" +
259            "@Retention("+retentn+") @Target({TYPE_USE,_OTHER_}) @Repeatable( BC.class ) @interface B { }\n" +
260            "@Retention("+retentn+") @Target({TYPE_USE,_OTHER_}) @interface BC { B[] value(); } \n" +
261            "@Retention("+retentn+") @Target({TYPE_USE,TYPE_PARAMETER,_OTHER_}) @Repeatable(DC.class) @interface D { }\n" +
262            "@Retention("+retentn+") @Target({TYPE_USE,TYPE_PARAMETER,_OTHER_}) @interface DC { D[] value(); }");
263
264        // Test case sources with sample generated source
265        switch(src) {
266            case src1: //(repeating) type annotations on lambda expressions.
267                /*
268                 * class Test1 {
269                 * Test1(){}
270                 * interface MapFun<T,R> {  R m( T n); }
271                 * void meth( MapFun<String,Integer> mf ) {
272                 *     assert( mf.m("four") == 4);
273                 * }
274                 * void test(Integer i) {
275                 *     // lambda expression as method arg
276                 *     meth( (@A @B String s) -> { @A @B Integer len = s.length(); return len; } );
277                 * }}
278                 */
279                source = new String( source +
280                "// " + src.description + "\n" +
281                "class " + testname + " {\n" +
282                "  " + testname +"(){} \n" +
283                "  interface MapFun<T,R> {  R m( T n); }\n\n" +
284                "  void meth( MapFun<String,Integer> mf ) {\n" +
285                "    assert( mf.m(\"four\") == 4);\n" +
286                "  }\n\n" +
287                "  void test(Integer i) {\n" +
288                "    // lambda expression as method arg\n" +
289                "    meth( (_As_ _Bs_ String s) -> { _As_ _Bs_ Integer len = s.length(); return len; } );\n" +
290                "}}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
291                "\n";
292                break;
293            case src2: //(repeating) type annotations on new in single line lambda expression.
294                /*
295                 * //case2: (repeating) type annotations on new in single lambda expressions.
296                 * class Test2{
297                 *   interface MapFun<T, R> {  R m( T n); }
298                 *   MapFun<Integer, String> its;
299                 * void test(Integer i) {
300                 *   its = a -> "~"+new @A @B Integer(a).toString()+"~";
301                 *   System.out.println("in: " + i + " out: " + its.m(i));
302                 * }}
303                 */
304                source = new String( source +
305                "// " + src.description + "\n" +
306                "class " + testname + "{\n" +
307                "  interface MapFun<T, R> {  R m( T n); }\n" +
308                "  MapFun<Integer, String> its;\n" +
309                "  void test(Integer i) {\n" +
310                "    its = a -> \"~\"+new _As_ _Bs_ Integer(a).toString()+\"~\";\n" +
311                "    System.out.println(\"in: \" + i + \" out: \" + its.m(i));\n" +
312                "  }\n" +
313                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
314                "\n";
315            break;
316            case src3: //(repeating) type annotations in lambda expression code block.
317                /*
318                 * class Test183{
319                 *   interface MapFun<T, R> {  R m( T n); }
320                 *   MapFun<List<Integer>, String> iLs;
321                 *   void testm(Integer i) {
322                 *       iLs = l -> { @A @B @A @B String ret = new String();
323                 *                    for( @A @B @A @B Integer i2 : l)
324                 *                        ret=ret.concat(i2.toString() + " ");
325                 *                    return ret; };
326                 *   List<Integer> li = new ArrayList<>();
327                 *   for(int j=0; j<i; j++) li.add(j);
328                 *   System.out.println(iLs.m(li) );
329                 * }}
330                 */
331                source = new String( source +
332                "// " + src.description + "\n" +
333                "class "+ testname + "{\n" +
334                "  interface MapFun<T, R> {  R m( T n); }\n" +
335                "  MapFun<List<Integer>, String> iLs;\n" +
336                "  void testm(Integer i) {\n" +
337                "    iLs = l -> { _As_ _Bs_ String ret = new String();\n" +
338                "                 for( _As_ _Bs_ Integer i2 : l)\n" +
339                "                   ret=ret.concat(i2.toString() + \" \");\n" +
340                "                 return ret; };\n" +
341                "  List<Integer> li = new ArrayList<>();\n" +
342                "  for(int j=0; j<i; j++) li.add(j);\n" +
343                "  System.out.println(iLs.m(li) );\n" +
344                "}\n" +
345                "\n" +
346                "    public static void main(String... args) {new " + testname + "().testm(5); }\n" +
347                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
348                "\n";
349            break;
350            case src4: //(repeating) type annotations in code block with recursion,cast
351                /*
352                 * class Test194{
353                 *   interface MapFun<T, R> {  R m( T n); }
354                 *   MapFun<Integer, Double>  nf;
355                 *   void testm(Integer i) {
356                 *       nf = j -> { return j == 1 ? 1.0 : (@A @B @A @B  Double)(nf.m(j-1) * j); };
357                 *       System.out.println( "nf.m(" + i + "): " + nf.m(i));
358                 *   }
359                 * }
360                 */
361                source = new String( source +
362                "// " + src.description + "\n" +
363                "class "+ testname + "{\n" +
364                "  interface MapFun<T, R> {  R m( T n); }\n" +
365                "  MapFun<Integer, Double>  nf;\n" +
366                "  void testm(Integer i) {\n" +
367                "    nf = j -> { return j == 1 ? 1.0 : (_As_ _Bs_  Double)(nf.m(j-1) * j); };\n" +
368                "    System.out.println( \"nf.m(\" + i + \"): \" + nf.m(i));\n" +
369                "  }\n" +
370                "  public static void main(String... args) {new " + testname + "().testm(5); }\n" +
371                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
372                "\n";
373            break;
374            case src5: //(repeating) type annotations in lambda expression code block.
375                   /*
376                    * class Test180 {
377                    *   interface MapFun<T, R> {  R m( T n); }
378                    *   MapFun<Integer,List<Integer>> iLi;
379                    *   void test(Integer i) {
380                    *     // type parameter use.
381                    *     iLi = n -> { List<@A @B @A @B Integer> LI = new ArrayList<@A @B @A @B Integer>(n);
382                    *                  for(int nn = n; nn >=0; nn--) LI.add(nn);
383                    *                  return LI; };
384                    *     List<Integer> li = iLi.m(i);
385                    *     for(Integer k : li) System.out.print(k);
386                    *   }
387                    * }
388                    */
389                source = new String( source +
390                "// " + src.description + "\n" +
391                "class "+ testname + "{\n" +
392                "  interface MapFun<T, R> {  R m( T n); }\n" +
393                "  MapFun<Integer,List<Integer>> iLi;\n" +
394                "  void test(Integer i) {\n" +
395                "    // type parameter use.\n" +
396                "    iLi = n -> { List<_As_ _Bs_ Integer> LI = new ArrayList<_As_ _Bs_ Integer>(n);\n" +
397                "                 for(int nn = n; nn >=0; nn--) LI.add(nn);\n" +
398                "                 return LI; };\n" +
399                "    List<Integer> li = iLi.m(i);\n" +
400                "    for(Integer k : li) System.out.print(k);\n" +
401                "}\n" +
402                "  public static void main(String... args) {new " + testname + "().test(5); }\n" +
403                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs).replace("_Ds_",Ds) +
404                "\n";
405            break;
406            case src6: //(repeating) type annotations on type parm in method reference.
407                /*
408                 * class Test240{
409                 *   interface PrintString { void print(String s); }
410                 *   public void printArray(Object[] oa, PrintString ps) {
411                 *       for(Object o : oa ) ps.print(o.toString());
412                 *   }
413                 *   public void test() {
414                 *       Integer[] intarray = {1,2,3,4,5};
415                 *       printArray(intarray, @A @B @A @B TPrint::<@A @B @A @B String>print);
416                 *   }
417                 * }
418                 * class TPrint {
419                 *    public static <T> void print(T t) { System.out.println( t.toString()); }
420                 * }
421                 */
422                source = new String( source +
423                "// " + src.description + "\n" +
424                "class "+ testname + "{\n" +
425                "  interface PrintString { void print(String s); }\n" +
426                "  public void printArray(Object[] oa, PrintString ps) {\n" +
427                "      for(Object o : oa ) ps.print(o.toString());\n" +
428                "  }\n" +
429                "  public void test() {\n" +
430                "    Integer[] intarray = {1,2,3,4,5};\n" +
431                "    printArray(intarray, _As_ _Bs_ TPrint::<_As_ _Bs_ String>print);\n" +
432                "  }\n" +
433                "  public static void main(String... args) {new " + testname + "().test(); }\n" +
434                "}\n\n" +
435                "class TPrint {\n" +
436                "  public static <T> void print(T t) { System.out.println( t.toString()); }\n" +
437                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
438                "\n";
439            break;
440            case src7: //(repeating)type annotations in inner class of lambda expression.
441                /*
442                 * class Test2{
443                 *   interface MapFun<T, R> {  R m( T n); }
444                 *   MapFun<Class<?>,String> cs;
445                 *   void test() {
446                 *     cs = c -> {
447                 *         class innerClass   {
448                 *           @A @B Class<?> icc = null;
449                 *           String getString() { return icc.toString(); }
450                 *         }
451                 *         return new innerClass().getString();
452                 *     };
453                 *     System.out.println("cs.m : " + cs.m(Integer.class));
454                 *   }
455                 * }
456                 */
457                source = new String( source +
458                "// " + src.description + "\n" +
459                "class "+ testname + "{\n" +
460                "  interface MapFun<T, R> {  R m( T n); }\n" +
461                "  MapFun<Class<?>,String> cs;\n" +
462                "  void test() {\n" +
463                "    cs = c -> {\n" +
464                "        class innerClass   {\n" +
465                "          _As_ _Bs_ Class<?> icc = null;\n" +
466                "          innerClass(Class<?> _c) { icc = _c; }\n" +
467                "          String getString() { return icc.toString(); }\n" +
468                "        }\n" +
469                "        return new innerClass(c).getString();\n" +
470                "    };\n" +
471                "    System.out.println(\"cs.m : \" + cs.m(Integer.class));\n" +
472                "  }\n" +
473                "\n" +
474                "    public static void main(String... args) {new " + testname + "().test(); }\n" +
475                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
476                "\n";
477                src.innerClassname="$1innerClass";
478            break;
479            case src8: //(repeating)type annotations in inner class of lambda expression.
480                /*
481                 * class Test2{
482                 *   interface MapFun<T, R> {  R m( T n); }
483                 *   MapFun<Class<?>,String> cs;
484                 *   void test() {
485                 *     cs = c -> {
486                 *         class innerClass   {
487                 *             Class<?> icc;
488                 *             innerClass(@A @B Class<?> _c) { icc = _c; }
489                 *             @A @B String getString() { return icc.toString(); }
490                 *         }
491                 *         return new innerClass(c).getString();
492                 *     };
493                 *     System.out.println("cs.m : " + cs.m(Integer.class));
494                 *   }
495                 * }
496                 */
497                source = new String( source +
498                "// " + src.description + "\n" +
499                "class "+ testname + "{\n" +
500                "  interface MapFun<T, R> {  R m( T n); }\n" +
501                "  MapFun<Class<?>,String> cs;\n" +
502                "  void test() {\n" +
503                "    cs = c -> {\n" +
504                "        class innerClass {\n" +
505                "            Class<?> icc;\n" +
506                "            innerClass(_As_ _Bs_ Class<?> _c) { icc = _c; }\n" +
507                "            _As_ _Bs_ String getString() { return icc.toString(); }\n" +
508                "        }\n" +
509                "        return new innerClass(c).getString();\n" +
510                "    };\n" +
511                "    System.out.println(\"cs.m : \" + cs.m(Integer.class));\n" +
512                "  }\n" +
513                "\n" +
514                "    public static void main(String... args) {new " + testname + "().test(); }\n" +
515                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
516                "\n";
517                src.innerClassname="$1innerClass";
518            break;
519            case src9: //(repeating)type annotations on static method of interface
520                /*
521                 *  class Test90{
522                 *    interface I  {
523                 *      static @A @B @A @B String m() { @A @B @A @B String ret = "I.m"; return ret; }
524                 *    }
525                 *  }
526                 */
527                source = new String( source +
528                "// " + src.description + "\n" +
529                "class "+ testname + "{\n" +
530                "  interface I  { \n" +
531                "    static _As_ _Bs_ String m() { _As_ _Bs_ String ret = \"I.m\"; return ret; }\n" +
532                "  }\n" +
533                "}\n\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
534                "\n";
535                src.innerClassname="$I";
536            break;
537        }
538        return imports + source;
539    }
540}
541