1 /*
   2  * Copyright (c) 2014, 2018, 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 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 8000961 8030610 8028546 8188870 8173382 8173382 8193290 8205619
  27  * @summary Check interpretation of -target and -source options
  28  * @modules java.compiler
  29  *          jdk.compiler
  30  * @run main Versions
  31  */
  32 
  33 import java.io.*;
  34 import java.nio.*;
  35 import java.nio.channels.*;
  36 
  37 import javax.tools.JavaCompiler;
  38 import javax.tools.ToolProvider;
  39 import javax.tools.JavaFileObject;
  40 import javax.tools.StandardJavaFileManager;
  41 import java.util.List;
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Set;
  45 
  46 public class Versions {
  47 
  48     protected JavaCompiler javacompiler;
  49     protected int failedCases;
  50 
  51     public Versions() throws IOException {
  52         javacompiler = ToolProvider.getSystemJavaCompiler();
  53         genSourceFiles();
  54         failedCases = 0;
  55     }
  56 
  57     public static void main(String... args) throws IOException {
  58         Versions versions = new Versions();
  59         versions.run();
  60     }
  61 
  62     public static final Set<String> RETIRED_SOURCES =
  63         Set.of("1.2", "1.3", "1.4", "1.5");
  64 
  65     public static final Set<String> VALID_SOURCES =
  66         Set.of("1.6", "1.7", "1.8", "1.9", "1.10", "11", "12");
  67 
  68     public static final String LATEST_MAJOR_VERSION = "56.0";
  69 
  70     static enum SourceTarget {
  71         // Reminder: update switch in checksrc when adding or removing values.
  72         SIX(true,     "50.0",  "6"),
  73         SEVEN(true,   "51.0",  "7"),
  74         EIGHT(true,   "52.0",  "8"),
  75         NINE(true,    "53.0",  "9"),
  76         TEN(true,     "54.0", "10"),
  77         ELEVEN(false, "55.0", "11"),
  78         TWELVE(false, "56.0", "12");
  79 
  80         private boolean dotOne;
  81         private String classFileVer;
  82         private String target;
  83 
  84         private SourceTarget(boolean dotOne, String classFileVer, String target) {
  85             this.dotOne = dotOne;
  86             this.classFileVer = classFileVer;
  87             this.target = target;
  88         }
  89 
  90         public static void checksrc(SourceTarget st, Versions version, String... args) {
  91             switch(st) {
  92             case SIX:
  93                 version.checksrc16(args);
  94                 break;
  95 
  96             case SEVEN:
  97                 version.checksrc17(args);
  98                 break;
  99 
 100             case EIGHT:
 101                 version.checksrc18(args);
 102                 break;
 103 
 104             case NINE:
 105                 version.checksrc19(args);
 106                 break;
 107 
 108             case TEN:
 109                 version.checksrc110(args);
 110                 break;
 111 
 112             case ELEVEN:
 113                 version.checksrc111(args);
 114                 break;
 115 
 116              case TWELVE:
 117                  version.checksrc112(args);
 118                  break;
 119 
 120             default:
 121                 throw new AssertionError();
 122             }
 123         }
 124 
 125         public boolean dotOne() {
 126             return dotOne;
 127         }
 128 
 129         public String classFileVer() {
 130             return classFileVer;
 131         }
 132 
 133         public String target() {
 134             return target;
 135         }
 136     }
 137 
 138     void run() {
 139 
 140         String TC = "";
 141         System.out.println("Version.java: Starting");
 142 
 143         check(LATEST_MAJOR_VERSION);
 144         for (String source : VALID_SOURCES) {
 145             check(LATEST_MAJOR_VERSION, "-source " + source);
 146         }
 147         
 148         // Verify that a -source value less than a -target value is
 149         // accepted and that the resulting class files are dependent
 150         // on the target setting alone.
 151         SourceTarget[] sourceTargets = SourceTarget.values();
 152         for (int i = 0; i < sourceTargets.length; i++) {
 153             SourceTarget st = sourceTargets[i];
 154             String classFileVer = st.classFileVer();
 155             String target = st.target();
 156             boolean dotOne = st.dotOne();
 157             check_source_target(dotOne, classFileVer, target, target);
 158             for (int j = i; j > 0; j--) {
 159                 String source = sourceTargets[j].target();
 160                 check_source_target(dotOne, classFileVer, source, target);
 161             }
 162         }
 163 
 164         // Verify acceptance of different combinations of -source N,
 165         // -target N
 166         for (int i = 0; i < sourceTargets.length; i++) {
 167             SourceTarget st = sourceTargets[i];
 168             
 169             SourceTarget.checksrc(st, this, "-source " + st.target());
 170             SourceTarget.checksrc(st, this, "-source " + st.target(), "-target " + st.target());
 171 
 172             if (st.dotOne()) {
 173                 SourceTarget.checksrc(st, this, "-source 1." + st.target());
 174                 SourceTarget.checksrc(st, this, "-source 1." + st.target(), "-target 1." + st.target());
 175             }
 176 
 177             if (i == sourceTargets.length) {
 178                 // Can use -target without -source setting only for
 179                 // most recent target
 180                 SourceTarget.checksrc(st, this, "-target " + st.target());
 181 
 182                 if (!st.classFileVer().equals(LATEST_MAJOR_VERSION)) {
 183                     throw new RuntimeException(st +
 184                                                "does not have class file version" +
 185                                                LATEST_MAJOR_VERSION);
 186                 }
 187             }
 188         }
 189 
 190         // Verify that -source N -target (N-1) is rejected
 191         for (int i = 1 /* Skip zeroth value */; i < sourceTargets.length; i++) {
 192             fail("-source " + sourceTargets[i].target(),
 193                  "-target " + sourceTargets[i-1].target(),
 194                  "Base.java");
 195         }
 196 
 197         // Previously supported source/target values
 198         for (String source  : RETIRED_SOURCES) {
 199             fail("-source " + source, "-target " + source, "Base.java");
 200         }
 201 
 202         if (failedCases > 0) {
 203             System.err.println("failedCases = " + String.valueOf(failedCases));
 204             throw new Error("Test failed");
 205         }
 206 
 207     }
 208 
 209     protected void printargs(String fname,String... args) {
 210         System.out.printf("test: %s", fname);
 211         for (String onearg : args) {
 212             System.out.printf(" %s", onearg);
 213         }
 214         System.out.printf("\n", fname);
 215     }
 216 
 217     protected void check_source_target(boolean dotOne, String... args) {
 218         printargs("check_source_target", args);
 219         check_target(dotOne, args[0], args[1], args[2]);
 220         if (dotOne) {
 221             check_target(dotOne, args[0], "1." + args[1], args[2]);
 222         }
 223     }
 224 
 225     protected void check_target(boolean dotOne, String... args) {
 226         check(args[0], "-source " + args[1], "-target " + args[2]);
 227         if (dotOne) {
 228             check(args[0], "-source " + args[1], "-target 1." + args[2]);
 229         }
 230     }
 231 
 232     protected void check(String major, String... args) {
 233         printargs("check", args);
 234         List<String> jcargs = new ArrayList<>();
 235         jcargs.add("-Xlint:-options");
 236 
 237         // add in args conforming to List requrements of JavaCompiler
 238         for (String onearg : args) {
 239             String[] fields = onearg.split(" ");
 240             for (String onefield : fields) {
 241                 jcargs.add(onefield);
 242             }
 243         }
 244 
 245         boolean creturn = compile("Base.java", jcargs);
 246         if (!creturn) {
 247             // compilation errors note and return.. assume no class file
 248             System.err.println("check: Compilation Failed");
 249             System.err.println("\t classVersion:\t" + major);
 250             System.err.println("\t arguments:\t" + jcargs);
 251             failedCases++;
 252 
 253         } else if (!checkClassFileVersion("Base.class", major)) {
 254             failedCases++;
 255         }
 256     }
 257 
 258     protected void checksrc16(String... args) {
 259         printargs("checksrc16", args);
 260         int asize = args.length;
 261         String[] newargs = new String[asize + 1];
 262         System.arraycopy(args, 0, newargs, 0, asize);
 263         newargs[asize] = "Base.java";
 264         pass(newargs);
 265         newargs[asize] = "New17.java";
 266         fail(newargs);
 267     }
 268 
 269     protected void checksrc17(String... args) {
 270         printargs("checksrc17", args);
 271         int asize = args.length;
 272         String[] newargs = new String[asize+1];
 273         System.arraycopy(args, 0, newargs,0 , asize);
 274         newargs[asize] = "New17.java";
 275         pass(newargs);
 276         newargs[asize] = "New18.java";
 277         fail(newargs);
 278     }
 279 
 280     protected void checksrc18(String... args) {
 281         printargs("checksrc18", args);
 282         int asize = args.length;
 283         String[] newargs = new String[asize+1];
 284         System.arraycopy(args, 0, newargs,0 , asize);
 285         newargs[asize] = "New17.java";
 286         pass(newargs);
 287         newargs[asize] = "New18.java";
 288         pass(newargs);
 289         newargs[asize] = "New110.java";
 290         fail(newargs);
 291     }
 292 
 293     protected void checksrc19(String... args) {
 294         printargs("checksrc19", args);
 295         checksrc18(args);
 296     }
 297 
 298     protected void checksrc110(String... args) {
 299         printargs("checksrc110", args);
 300         int asize = args.length;
 301         String[] newargs = new String[asize+1];
 302         System.arraycopy(args, 0, newargs,0 , asize);
 303         newargs[asize] = "New17.java";
 304         pass(newargs);
 305         newargs[asize] = "New18.java";
 306         pass(newargs);
 307         newargs[asize] = "New110.java";
 308         pass(newargs);
 309     }
 310 
 311     protected void checksrc111(String... args) {
 312         printargs("checksrc111", args);
 313         int asize = args.length;
 314         String[] newargs = new String[asize+1];
 315         System.arraycopy(args, 0, newargs,0 , asize);
 316         newargs[asize] = "New17.java";
 317         pass(newargs);
 318         newargs[asize] = "New18.java";
 319         pass(newargs);
 320         newargs[asize] = "New110.java";
 321         pass(newargs);
 322         newargs[asize] = "New111.java";
 323         pass(newargs);
 324     }
 325 
 326     protected void checksrc112(String... args) {
 327         printargs("checksrc112", args);
 328         checksrc111(args);
 329     }
 330 
 331     protected void pass(String... args) {
 332         printargs("pass", args);
 333 
 334         List<String> jcargs = new ArrayList<String>();
 335         jcargs.add("-Xlint:-options");
 336 
 337         // add in args conforming to List requrements of JavaCompiler
 338         for (String onearg : args) {
 339             String[] fields = onearg.split(" ");
 340             for (String onefield : fields) {
 341                 jcargs.add(onefield);
 342             }
 343         }
 344 
 345         // empty list is error
 346         if (jcargs.isEmpty()) {
 347             System.err.println("error: test error in pass() - No arguments");
 348             System.err.println("\t arguments:\t" + jcargs);
 349             failedCases++;
 350             return;
 351         }
 352 
 353         // the last argument is the filename *.java
 354         String filename = jcargs.get(jcargs.size() - 1);
 355         jcargs.remove(jcargs.size() - 1);
 356 
 357         boolean creturn = compile(filename, jcargs);
 358         // expect a compilation failure, failure if otherwise
 359         if (!creturn) {
 360             System.err.println("pass: Compilation erroneously failed");
 361             System.err.println("\t arguments:\t" + jcargs);
 362             System.err.println("\t file     :\t" + filename);
 363             failedCases++;
 364 
 365         }
 366 
 367     }
 368 
 369     protected void fail(String... args) {
 370         printargs("fail", args);
 371 
 372         List<String> jcargs = new ArrayList<String>();
 373         jcargs.add("-Xlint:-options");
 374 
 375         // add in args conforming to List requrements of JavaCompiler
 376         for (String onearg : args) {
 377             String[] fields = onearg.split(" ");
 378             for (String onefield : fields) {
 379                 jcargs.add(onefield);
 380             }
 381         }
 382 
 383         // empty list is error
 384         if (jcargs.isEmpty()) {
 385             System.err.println("error: test error in fail()- No arguments");
 386             System.err.println("\t arguments:\t" + jcargs);
 387             failedCases++;
 388             return;
 389         }
 390 
 391         // the last argument is the filename *.java
 392         String filename = jcargs.get(jcargs.size() - 1);
 393         jcargs.remove(jcargs.size() - 1);
 394 
 395         boolean creturn = compile(filename, jcargs);
 396         // expect a compilation failure, failure if otherwise
 397         if (creturn) {
 398             System.err.println("fail: Compilation erroneously succeeded");
 399             System.err.println("\t arguments:\t" + jcargs);
 400             System.err.println("\t file     :\t" + filename);
 401             failedCases++;
 402         }
 403     }
 404 
 405     protected boolean compile(String sourceFile, List<String>options) {
 406         JavaCompiler.CompilationTask jctask;
 407         try (StandardJavaFileManager fm = javacompiler.getStandardFileManager(null, null, null)) {
 408             Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(sourceFile);
 409 
 410             jctask = javacompiler.getTask(
 411                 null,    // Writer
 412                 fm,      // JavaFileManager
 413                 null,    // DiagnosticListener
 414                 options, // Iterable<String>
 415                 null,    // Iterable<String> classes
 416                 files);  // Iterable<? extends JavaFileObject>
 417 
 418             try {
 419                 return jctask.call();
 420             } catch (IllegalStateException e) {
 421                 System.err.println(e);
 422                 return false;
 423             }
 424         } catch (IOException e) {
 425             throw new Error(e);
 426         }
 427     }
 428 
 429     protected void writeSourceFile(String fileName, String body) throws IOException{
 430         try (Writer fw = new FileWriter(fileName)) {
 431             fw.write(body);
 432         }
 433     }
 434 
 435     protected void genSourceFiles() throws IOException{
 436         /* Create a file that executes with all supported versions. */
 437         writeSourceFile("Base.java","public class Base { }\n");
 438 
 439         /*
 440          * Create a file with a new feature in 1.7, not in 1.6 : "<>"
 441          */
 442         writeSourceFile("New17.java",
 443             "import java.util.List;\n" +
 444             "import java.util.ArrayList;\n" +
 445             "class New17 { List<String> s = new ArrayList<>(); }\n"
 446         );
 447 
 448         /*
 449          * Create a file with a new feature in 1.8, not in 1.7 : lambda
 450          */
 451         writeSourceFile("New18.java",
 452             "public class New18 { \n" +
 453             "    void m() { \n" +
 454             "    new Thread(() -> { }); \n" +
 455             "    } \n" +
 456             "} \n"
 457         );
 458 
 459         /*
 460          * Create a file with a new feature in 1.10, not in 1.9 : var
 461          */
 462         writeSourceFile("New110.java",
 463             "public class New110 { \n" +
 464             "    void m() { \n" +
 465             "    var tmp = new Thread(() -> { }); \n" +
 466             "    } \n" +
 467             "} \n"
 468         );
 469 
 470         /*
 471          * Create a file with a new feature in 11, not in 10 : var for lambda parameters
 472          */
 473         writeSourceFile("New111.java",
 474             "public class New111 { \n" +
 475             "    static java.util.function.Function<String,String> f = (var x) -> x.substring(0);\n" +
 476             "    void m(String name) { \n" +
 477             "    var tmp = new Thread(() -> { }, f.apply(name)); \n" +
 478             "    } \n" +
 479             "} \n"
 480         );
 481     }
 482 
 483     protected boolean checkClassFileVersion
 484         (String filename,String classVersionNumber) {
 485         ByteBuffer bb = ByteBuffer.allocate(1024);
 486         try (FileChannel fc = new FileInputStream(filename).getChannel()) {
 487             bb.clear();
 488             if (fc.read(bb) < 0)
 489                 throw new IOException("Could not read from file : " + filename);
 490             bb.flip();
 491             int minor = bb.getShort(4);
 492             int major = bb.getShort(6);
 493             String fileVersion = major + "." + minor;
 494             if (fileVersion.equals(classVersionNumber)) {
 495                 return true;
 496             } else {
 497                 System.err.println("checkClassFileVersion : Failed");
 498                 System.err.println("\tclassfile version mismatch");
 499                 System.err.println("\texpected : " + classVersionNumber);
 500                 System.err.println("\tfound    : " + fileVersion);
 501                 return false;
 502             }
 503         }
 504         catch (IOException e) {
 505             System.err.println("checkClassFileVersion : Failed");
 506             System.err.println("\terror :\t" + e.getMessage());
 507             System.err.println("\tfile:\tfilename");
 508         }
 509         return false;
 510     }
 511 }
 512