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