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