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 
  32 import com.sun.tools.classfile.*;
  33 import java.io.File;
  34 import java.util.Vector;
  35 
  36 public 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 }