1 /*
2  * Copyright (c) 2014, 2020, 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 8028563 8245147 8245586 8257453
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 import java.util.function.BiConsumer;
46 import java.util.function.Consumer;
47 
48 /*
49  * If not explicitly specified the latest source and latest target
50  * values are the defaults. If explicitly specified, the target value
51  * has to be greater than or equal to the source value.
52  */
53 public class Versions {
54 
55     protected JavaCompiler javacompiler;
56     protected int failedCases;
57 
Versions()58     public Versions() throws IOException {
59         javacompiler = ToolProvider.getSystemJavaCompiler();
60         genSourceFiles();
61         failedCases = 0;
62     }
63 
main(String... args)64     public static void main(String... args) throws IOException {
65         Versions versions = new Versions();
66         versions.run();
67     }
68 
69     public static final Set<String> RETIRED_SOURCES =
70         Set.of("1.2", "1.3", "1.4", "1.5", "1.6");
71 
72     public static final Set<String> VALID_SOURCES =
73         Set.of("1.7", "1.8", "1.9", "1.10", "11", "12", "13", "14", "15", "16", "17");
74 
75     public static final String LATEST_MAJOR_VERSION = "61.0";
76 
77     static enum SourceTarget {
78         SEVEN(true,   "51.0",  "7", Versions::checksrc7),
79         EIGHT(true,   "52.0",  "8", Versions::checksrc8),
80         NINE(true,    "53.0",  "9", Versions::checksrc9),
81         TEN(true,     "54.0", "10", Versions::checksrc10),
82         ELEVEN(false, "55.0", "11", Versions::checksrc11),
83         TWELVE(false, "56.0", "12", Versions::checksrc12),
84         THIRTEEN(false, "57.0", "13", Versions::checksrc13),
85         FOURTEEN(false, "58.0", "14", Versions::checksrc14),
86         FIFTEEN(false,  "59.0", "15", Versions::checksrc15),
87         SIXTEEN(false,  "60.0", "16", Versions::checksrc16),
88         SEVENTEEN(false,  "61.0", "17", Versions::checksrc17);
89 
90         private final boolean dotOne;
91         private final String classFileVer;
92         private final String target;
93         private final BiConsumer<Versions, List<String>> checker;
94 
SourceTarget(boolean dotOne, String classFileVer, String target, BiConsumer<Versions, List<String>> checker)95         private SourceTarget(boolean dotOne, String classFileVer, String target,
96                              BiConsumer<Versions, List<String>> checker) {
97             this.dotOne = dotOne;
98             this.classFileVer = classFileVer;
99             this.target = target;
100             this.checker = checker;
101         }
102 
checksrc(Versions version, List<String> args)103         public void checksrc(Versions version, List<String> args) {
104             checker.accept(version, args);
105         }
106 
dotOne()107         public boolean dotOne() {
108             return dotOne;
109         }
110 
classFileVer()111         public String classFileVer() {
112             return classFileVer;
113         }
114 
target()115         public String target() {
116             return target;
117         }
118     }
119 
run()120     void run() {
121         String TC = "";
122         System.out.println("Version.java: Starting");
123 
124         check(LATEST_MAJOR_VERSION);
125         for (String source : VALID_SOURCES) {
126             check(LATEST_MAJOR_VERSION, List.of("-source " + source));
127         }
128 
129         // Verify that a -source value less than a -target value is
130         // accepted and that the resulting class files are dependent
131         // on the target setting alone.
132         SourceTarget[] sourceTargets = SourceTarget.values();
133         for (int i = 0; i < sourceTargets.length; i++) {
134             SourceTarget st = sourceTargets[i];
135             String classFileVer = st.classFileVer();
136             String target = st.target();
137             boolean dotOne = st.dotOne();
138             check_source_target(dotOne, List.of(classFileVer, target, target));
139             for (int j = i - 1; j >= 0; j--) {
140                 String source = sourceTargets[j].target();
141                 check_source_target(dotOne, List.of(classFileVer, source, target));
142             }
143         }
144 
145         // Verify acceptance of different combinations of -source N,
146         // -target M; N <= M
147         for (int i = 0; i < sourceTargets.length; i++) {
148             SourceTarget st = sourceTargets[i];
149 
150             st.checksrc(this, List.of("-source " + st.target()));
151             st.checksrc(this, List.of("-source " + st.target(), "-target " + st.target()));
152 
153             if (st.dotOne()) {
154                 st.checksrc(this, List.of("-source 1." + st.target()));
155                 st.checksrc(this, List.of("-source 1." + st.target(), "-target 1." + st.target()));
156             }
157 
158             if (i == sourceTargets.length - 1) {
159                 // Can use -target without -source setting only for
160                 // most recent target since the most recent source is
161                 // the default.
162                 st.checksrc(this, List.of("-target " + st.target()));
163 
164                 if (!st.classFileVer().equals(LATEST_MAJOR_VERSION)) {
165                     throw new RuntimeException(st +
166                                                "does not have class file version" +
167                                                LATEST_MAJOR_VERSION);
168                 }
169             }
170         }
171 
172         // Verify that -source N -target (N-1) is rejected
173         for (int i = 1 /* Skip zeroth value */; i < sourceTargets.length; i++) {
174             fail(List.of("-source " + sourceTargets[i].target(),
175                  "-target " + sourceTargets[i-1].target(),
176                          "Base.java"));
177         }
178 
179         // Previously supported source/target values
180         for (String source  : RETIRED_SOURCES) {
181             fail(List.of("-source " + source, "-target " + source, "Base.java"));
182         }
183 
184         if (failedCases > 0) {
185             System.err.println("failedCases = " + String.valueOf(failedCases));
186             throw new Error("Test failed");
187         }
188 
189     }
190 
printargs(String fname, List<String> args)191     protected void printargs(String fname, List<String> args) {
192         System.out.printf("test: %s", fname);
193         for (String onearg : args) {
194             System.out.printf(" %s", onearg);
195         }
196         System.out.printf("\n", fname);
197     }
198 
check_source_target(boolean dotOne, List<String> args)199     protected void check_source_target(boolean dotOne, List<String> args) {
200         printargs("check_source_target", args);
201         check_target(dotOne, List.of(args.get(0), args.get(1), args.get(2)));
202         if (dotOne) {
203             check_target(dotOne, List.of(args.get(0), "1." + args.get(1), args.get(2)));
204         }
205     }
206 
check_target(boolean dotOne, List<String> args)207     protected void check_target(boolean dotOne, List<String> args) {
208         check(args.get(0), List.of("-source " + args.get(1), "-target " + args.get(2)));
209         if (dotOne) {
210             check(args.get(0), List.of("-source " + args.get(1), "-target 1." + args.get(2)));
211         }
212     }
213 
check(String major)214     protected void check(String major) {
215         check(major, List.of());
216     }
217 
check(String major, List<String> args)218     protected void check(String major, List<String> args) {
219         printargs("check", args);
220         List<String> jcargs = new ArrayList<>();
221         jcargs.add("-Xlint:-options");
222 
223         // add in args conforming to List requrements of JavaCompiler
224         for (String onearg : args) {
225             String[] fields = onearg.split(" ");
226             for (String onefield : fields) {
227                 jcargs.add(onefield);
228             }
229         }
230 
231         boolean creturn = compile("Base.java", jcargs);
232         if (!creturn) {
233             // compilation errors note and return.. assume no class file
234             System.err.println("check: Compilation Failed");
235             System.err.println("\t classVersion:\t" + major);
236             System.err.println("\t arguments:\t" + jcargs);
237             failedCases++;
238 
239         } else if (!checkClassFileVersion("Base.class", major)) {
240             failedCases++;
241         }
242     }
243 
checksrc7(List<String> args)244     protected void checksrc7(List<String> args) {
245         printargs("checksrc7", args);
246         expectedPass(args, List.of("New7.java"));
247         expectedFail(args, List.of("New8.java"));
248     }
249 
checksrc8(List<String> args)250     protected void checksrc8(List<String> args) {
251         printargs("checksrc8", args);
252         expectedPass(args, List.of("New7.java", "New8.java"));
253         expectedFail(args, List.of("New10.java"));
254     }
255 
checksrc9(List<String> args)256     protected void checksrc9(List<String> args) {
257         printargs("checksrc9", args);
258         expectedPass(args, List.of("New7.java", "New8.java"));
259         expectedFail(args, List.of("New10.java"));
260     }
261 
checksrc10(List<String> args)262     protected void checksrc10(List<String> args) {
263         printargs("checksrc10", args);
264         expectedPass(args, List.of("New7.java", "New8.java", "New10.java"));
265         expectedFail(args, List.of("New11.java"));
266     }
267 
checksrc11(List<String> args)268     protected void checksrc11(List<String> args) {
269         printargs("checksrc11", args);
270         expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java"));
271         expectedFail(args, List.of("New14.java"));
272     }
273 
checksrc12(List<String> args)274     protected void checksrc12(List<String> args) {
275         printargs("checksrc12", args);
276         expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java"));
277         expectedFail(args, List.of("New14.java"));
278     }
279 
checksrc13(List<String> args)280     protected void checksrc13(List<String> args) {
281         printargs("checksrc13", args);
282         expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java"));
283         expectedFail(args, List.of("New14.java"));
284     }
285 
checksrc14(List<String> args)286     protected void checksrc14(List<String> args) {
287         printargs("checksrc14", args);
288         expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java",
289                                    "New14.java"));
290         expectedFail(args, List.of("New15.java"));
291     }
292 
checksrc15(List<String> args)293    protected void checksrc15(List<String> args) {
294        printargs("checksrc15", args);
295        expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java",
296                                   "New14.java", "New15.java"));
297         expectedFail(args, List.of("New16.java"));
298     }
299 
checksrc16(List<String> args)300    protected void checksrc16(List<String> args) {
301        printargs("checksrc16", args);
302        expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java",
303                                   "New14.java", "New15.java", "New16.java"));
304        // Add expectedFail after new language features added in a later release.
305     }
306 
checksrc17(List<String> args)307    protected void checksrc17(List<String> args) {
308        printargs("checksrc17", args);
309        expectedPass(args, List.of("New7.java", "New8.java", "New10.java", "New11.java",
310                                   "New14.java", "New15.java", "New16.java"));
311        // Add expectedFail after new language features added in a later release.
312     }
313 
expected(List<String> args, List<String> fileNames, Consumer<List<String>> passOrFail)314     protected void expected(List<String> args, List<String> fileNames,
315                             Consumer<List<String>> passOrFail) {
316         ArrayList<String> fullArguments = new ArrayList<>(args);
317         // Issue compile with each filename in turn.
318         for(String fileName : fileNames) {
319             fullArguments.add(fileName);
320             passOrFail.accept(fullArguments);
321             fullArguments.remove(fullArguments.size() - 1);
322         }
323     }
324 
expectedPass(List<String> args, List<String> fileNames)325     protected void expectedPass(List<String> args, List<String> fileNames) {
326         expected(args, fileNames, this::pass);
327     }
328 
expectedFail(List<String> args, List<String> fileNames)329     protected void expectedFail(List<String> args, List<String> fileNames) {
330         expected(args, fileNames, this::fail);
331     }
332 
pass(List<String> args)333     protected void pass(List<String> args) {
334         printargs("pass", args);
335 
336         List<String> jcargs = new ArrayList<>();
337         jcargs.add("-Xlint:-options");
338 
339         // add in args conforming to List requrements of JavaCompiler
340         for (String onearg : args) {
341             String[] fields = onearg.split(" ");
342             for (String onefield : fields) {
343                 jcargs.add(onefield);
344             }
345         }
346 
347         // empty list is error
348         if (jcargs.isEmpty()) {
349             System.err.println("error: test error in pass() - No arguments");
350             System.err.println("\t arguments:\t" + jcargs);
351             failedCases++;
352             return;
353         }
354 
355         // the last argument is the filename *.java
356         String filename = jcargs.get(jcargs.size() - 1);
357         jcargs.remove(jcargs.size() - 1);
358 
359         boolean creturn = compile(filename, jcargs);
360         // expect a compilation failure, failure if otherwise
361         if (!creturn) {
362             System.err.println("pass: Compilation erroneously failed");
363             System.err.println("\t arguments:\t" + jcargs);
364             System.err.println("\t file     :\t" + filename);
365             failedCases++;
366 
367         }
368 
369     }
370 
fail(List<String> args)371     protected void fail(List<String> args) {
372         printargs("fail", args);
373 
374         List<String> jcargs = new ArrayList<>();
375         jcargs.add("-Xlint:-options");
376 
377         // add in args conforming to List requrements of JavaCompiler
378         for (String onearg : args) {
379             String[] fields = onearg.split(" ");
380             for (String onefield : fields) {
381                 jcargs.add(onefield);
382             }
383         }
384 
385         // empty list is error
386         if (jcargs.isEmpty()) {
387             System.err.println("error: test error in fail()- No arguments");
388             System.err.println("\t arguments:\t" + jcargs);
389             failedCases++;
390             return;
391         }
392 
393         // the last argument is the filename *.java
394         String filename = jcargs.get(jcargs.size() - 1);
395         jcargs.remove(jcargs.size() - 1);
396 
397         boolean creturn = compile(filename, jcargs);
398         // expect a compilation failure, failure if otherwise
399         if (creturn) {
400             System.err.println("fail: Compilation erroneously succeeded");
401             System.err.println("\t arguments:\t" + jcargs);
402             System.err.println("\t file     :\t" + filename);
403             failedCases++;
404         }
405     }
406 
compile(String sourceFile, List<String> options)407     protected boolean compile(String sourceFile, List<String> options) {
408         JavaCompiler.CompilationTask jctask;
409         try (StandardJavaFileManager fm = javacompiler.getStandardFileManager(null, null, null)) {
410             Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(sourceFile);
411 
412             jctask = javacompiler.getTask(
413                 null,    // Writer
414                 fm,      // JavaFileManager
415                 null,    // DiagnosticListener
416                 options, // Iterable<String>
417                 null,    // Iterable<String> classes
418                 files);  // Iterable<? extends JavaFileObject>
419 
420             try {
421                 return jctask.call();
422             } catch (IllegalStateException e) {
423                 System.err.println(e);
424                 return false;
425             }
426         } catch (IOException e) {
427             throw new Error(e);
428         }
429     }
430 
writeSourceFile(String fileName, String body)431     protected void writeSourceFile(String fileName, String body) throws IOException{
432         try (Writer fw = new FileWriter(fileName)) {
433             fw.write(body);
434         }
435     }
436 
genSourceFiles()437     protected void genSourceFiles() throws IOException{
438         /* Create a file that executes with all supported versions. */
439         writeSourceFile("Base.java","public class Base { }\n");
440 
441         /*
442          * Create a file with a new feature in 7, not in 6 : "<>"
443          */
444         writeSourceFile("New7.java",
445             """
446             import java.util.List;
447             import java.util.ArrayList;
448             class New7 { List<String> s = new ArrayList<>(); }
449             """
450         );
451 
452         /*
453          * Create a file with a new feature in 8, not in 7 : lambda
454          */
455         writeSourceFile("New8.java",
456             """
457             public class New8 {
458                 void m() {
459                 new Thread(() -> { });
460                 }
461             }
462              """
463         );
464 
465         /*
466          * Create a file with a new feature in 10, not in 9 : var
467          */
468         writeSourceFile("New10.java",
469             """
470             public class New10 {
471                 void m() {
472                 var tmp = new Thread(() -> { });
473                 }
474             }
475             """
476         );
477 
478         /*
479          * Create a file with a new feature in 11, not in 10 : var for lambda parameters
480          */
481         writeSourceFile("New11.java",
482             """
483             public class New11 {
484                 static java.util.function.Function<String,String> f = (var x) -> x.substring(0);
485                 void m(String name) {
486                 var tmp = new Thread(() -> { }, f.apply(name));
487                 }
488             }
489             """
490         );
491 
492         /*
493          * Create a file with a new feature in 14, not in 13 : switch expressions
494          */
495         writeSourceFile("New14.java",
496             """
497             public class New14 {
498                 static {
499                     int i = 5;
500                     System.out.println(
501                         switch(i) {
502                             case 0 -> false;
503                             default -> true;
504                         }
505                     );
506                 }
507             }
508             """
509         );
510 
511         /*
512          * Create a file with a new feature in 15, not in 14 : text blocks
513          */
514         writeSourceFile("New15.java",
515             """
516             public class New15 {
517                 public static final String s =
518                 \"\"\"
519                 Hello, World.
520                 \"\"\"
521                 ;
522             }
523             """
524         );
525 
526         /*
527          * Create a file with a new feature in 16, not in 15 : records
528          */
529         writeSourceFile("New16.java",
530             """
531             public class New16 {
532                 public record Record(double rpm) {
533                     public static final Record LONG_PLAY = new Record(100.0/3.0);
534                 }
535             }
536             """
537         );
538     }
539 
checkClassFileVersion(String filename,String classVersionNumber)540     protected boolean checkClassFileVersion
541         (String filename,String classVersionNumber) {
542         ByteBuffer bb = ByteBuffer.allocate(1024);
543         try (FileChannel fc = new FileInputStream(filename).getChannel()) {
544             bb.clear();
545             if (fc.read(bb) < 0)
546                 throw new IOException("Could not read from file : " + filename);
547             bb.flip();
548             int minor = bb.getShort(4);
549             int major = bb.getShort(6);
550             String fileVersion = major + "." + minor;
551             if (fileVersion.equals(classVersionNumber)) {
552                 return true;
553             } else {
554                 System.err.println("checkClassFileVersion : Failed");
555                 System.err.println("\tclassfile version mismatch");
556                 System.err.println("\texpected : " + classVersionNumber);
557                 System.err.println("\tfound    : " + fileVersion);
558                 return false;
559             }
560         }
561         catch (IOException e) {
562             System.err.println("checkClassFileVersion : Failed");
563             System.err.println("\terror :\t" + e.getMessage());
564             System.err.println("\tfile:\tfilename");
565         }
566         return false;
567     }
568 }
569 
570