|
1 /* |
|
2 * Copyright (c) 2014, 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 * @summary Tests which path is used to represent an implicit type given |
|
27 * various xprefer arguments and multiple .class / .java files involved. |
|
28 * @bug 8028196 |
|
29 */ |
|
30 |
|
31 import java.io.File; |
|
32 import java.io.FileWriter; |
|
33 import java.io.IOException; |
|
34 import java.io.PrintWriter; |
|
35 import java.io.StringWriter; |
|
36 import java.util.ArrayList; |
|
37 import java.util.Arrays; |
|
38 import java.util.Collection; |
|
39 import java.util.Iterator; |
|
40 import java.util.List; |
|
41 import java.util.ListIterator; |
|
42 import java.util.NoSuchElementException; |
|
43 import java.util.Scanner; |
|
44 |
|
45 import javax.tools.JavaCompiler; |
|
46 import javax.tools.JavaCompiler.CompilationTask; |
|
47 import javax.tools.ToolProvider; |
|
48 |
|
49 |
|
50 public class XPreferTest { |
|
51 |
|
52 enum Dir { |
|
53 SOURCE_PATH("src"), |
|
54 CLASS_PATH("cp"), |
|
55 BOOT_PATH("boot"); |
|
56 |
|
57 File file; |
|
58 Dir(String dir) { |
|
59 this.file = new File(dir); |
|
60 } |
|
61 } |
|
62 |
|
63 enum ImplicitOption { |
|
64 XPREFER_SOURCE("-Xprefer:source"), |
|
65 XPREFER_NEWER("-Xprefer:newer"), |
|
66 XXUSERPATHSFIRST("-XXuserPathsFirst"); |
|
67 |
|
68 final String optionString; |
|
69 private ImplicitOption(String optionString) { |
|
70 this.optionString = optionString; |
|
71 } |
|
72 } |
|
73 |
|
74 final static JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); |
|
75 final static File OUTPUT_DIR = new File("out"); |
|
76 |
|
77 public static void main(String... args) throws Exception { |
|
78 |
|
79 // Initialize test-directories |
|
80 OUTPUT_DIR.mkdir(); |
|
81 for (Dir dir : Dir.values()) |
|
82 dir.file.mkdir(); |
|
83 |
|
84 int testCaseCounter = 0; |
|
85 |
|
86 for (List<Dir> dirSubset : SubseqIter.subseqsOf(Dir.values())) { |
|
87 |
|
88 if (dirSubset.isEmpty()) |
|
89 continue; |
|
90 |
|
91 for (ImplicitOption policy : ImplicitOption.values()) { |
|
92 for (List<Dir> dirOrder : PermutationIterator.permutationsOf(dirSubset)) { |
|
93 new TestCase(dirOrder, policy, testCaseCounter++).run(); |
|
94 } |
|
95 } |
|
96 } |
|
97 } |
|
98 |
|
99 static class TestCase { |
|
100 |
|
101 String classId; |
|
102 List<Dir> dirs; |
|
103 ImplicitOption option; |
|
104 |
|
105 public TestCase(List<Dir> dirs, ImplicitOption option, int testCaseNum) { |
|
106 this.dirs = dirs; |
|
107 this.option = option; |
|
108 this.classId = "XPreferTestImplicit" + testCaseNum; |
|
109 } |
|
110 |
|
111 void run() throws Exception { |
|
112 |
|
113 System.out.println("Test:"); |
|
114 System.out.println(" Class id: " + classId); |
|
115 System.out.println(" Dirs: " + dirs); |
|
116 System.out.println(" Option: " + option); |
|
117 |
|
118 createTestFiles(); |
|
119 String compileOutput = compile(); |
|
120 Dir actual = getChosenOrigin(compileOutput); |
|
121 Dir expected = getExpectedOrigin(); |
|
122 |
|
123 System.out.println(" Expected: " + expected); |
|
124 System.out.println(" Actual: " + actual); |
|
125 |
|
126 if (actual != expected) { |
|
127 throw new RuntimeException(String.format( |
|
128 "Expected javac to choose %s but %s was chosen", |
|
129 expected == null ? "<none>" : expected.name(), |
|
130 actual == null ? "<none>" : actual.name())); |
|
131 } |
|
132 } |
|
133 |
|
134 Dir getExpectedOrigin() { |
|
135 |
|
136 Dir newest = dirs.get(0); |
|
137 |
|
138 switch (option) { |
|
139 |
|
140 case XPREFER_NEWER: |
|
141 |
|
142 Dir cls = dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH |
|
143 : dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH |
|
144 : null; |
|
145 |
|
146 Dir src = dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH |
|
147 : null; |
|
148 |
|
149 for (Dir dir : dirs) |
|
150 if (dir == cls || dir == src) |
|
151 return dir; |
|
152 |
|
153 return null; |
|
154 |
|
155 case XPREFER_SOURCE: |
|
156 return dirs.contains(Dir.SOURCE_PATH) ? Dir.SOURCE_PATH |
|
157 : dirs.contains(Dir.BOOT_PATH) ? Dir.BOOT_PATH |
|
158 : dirs.contains(Dir.CLASS_PATH) ? Dir.CLASS_PATH |
|
159 : null; |
|
160 |
|
161 case XXUSERPATHSFIRST: |
|
162 |
|
163 for (Dir dir : dirs) |
|
164 if (dir == Dir.SOURCE_PATH || dir == Dir.CLASS_PATH) |
|
165 return dir; |
|
166 |
|
167 // Neither SOURCE_PATH nor CLASS_PATH among dirs. Safty check: |
|
168 if (newest != Dir.BOOT_PATH) |
|
169 throw new AssertionError("Expected to find BOOT_PATH"); |
|
170 |
|
171 return Dir.BOOT_PATH; |
|
172 |
|
173 default: |
|
174 throw new RuntimeException("Unhandled policy case."); |
|
175 } |
|
176 } |
|
177 |
|
178 Dir getChosenOrigin(String compilerOutput) { |
|
179 Scanner s = new Scanner(compilerOutput); |
|
180 while (s.hasNextLine()) { |
|
181 String line = s.nextLine(); |
|
182 if (line.matches("\\[loading .*\\]")) |
|
183 for (Dir dir : Dir.values()) |
|
184 if (line.contains(dir.file.getName() + "/" + classId)) |
|
185 return dir; |
|
186 } |
|
187 return null; |
|
188 } |
|
189 |
|
190 String compile() throws IOException { |
|
191 |
|
192 // Create a class that references classId |
|
193 File explicit = new File("ExplicitClass.java"); |
|
194 FileWriter filewriter = new FileWriter(explicit); |
|
195 filewriter.append("class ExplicitClass { " + classId + " implicit; }"); |
|
196 filewriter.close(); |
|
197 |
|
198 StringWriter sw = new StringWriter(); |
|
199 |
|
200 com.sun.tools.javac.Main.compile(new String[] { |
|
201 "-verbose", |
|
202 option.optionString, |
|
203 "-sourcepath", Dir.SOURCE_PATH.file.getPath(), |
|
204 "-classpath", Dir.CLASS_PATH.file.getPath(), |
|
205 "-Xbootclasspath/p:" + Dir.BOOT_PATH.file.getPath(), |
|
206 "-d", XPreferTest.OUTPUT_DIR.getPath(), |
|
207 explicit.getPath() |
|
208 }, new PrintWriter(sw)); |
|
209 |
|
210 return sw.toString(); |
|
211 } |
|
212 |
|
213 void createTestFiles() throws IOException { |
|
214 long t = 1390927988755L; // Tue Jan 28 17:53:08 CET 2014 |
|
215 for (Dir dir : dirs) { |
|
216 createFile(dir).setLastModified(t); |
|
217 t -= 10000; |
|
218 } |
|
219 } |
|
220 |
|
221 File createFile(Dir dir) throws IOException { |
|
222 File src = new File(dir.file, classId + ".java"); |
|
223 try (FileWriter w = new FileWriter(src)) { |
|
224 w.append("public class " + classId + " {}"); |
|
225 } |
|
226 // If we're after the ".java" representation, we're done... |
|
227 if(dir == Dir.SOURCE_PATH) |
|
228 return src; |
|
229 // ...otherwise compile into a ".class". |
|
230 CompilationTask task = comp.getTask(null, null, null, null, null, |
|
231 comp.getStandardFileManager(null, null, null).getJavaFileObjects(src)); |
|
232 File dest = new File(dir.file, classId + ".class"); |
|
233 if(!task.call() || !dest.exists()) |
|
234 throw new RuntimeException("Compilation failure."); |
|
235 src.delete(); |
|
236 return dest; |
|
237 } |
|
238 } |
|
239 } |
|
240 |
|
241 // Iterator for iteration over all subsequences of a given list. |
|
242 class SubseqIter<T> implements Iterator<List<T>> { |
|
243 |
|
244 List<T> elements; |
|
245 boolean[] states; |
|
246 |
|
247 public SubseqIter(Collection<T> c) { |
|
248 states = new boolean[c.size()]; |
|
249 elements = new ArrayList<T>(c); |
|
250 } |
|
251 |
|
252 public static <T> Iterable<List<T>> subseqsOf(final T[] t) { |
|
253 return new Iterable<List<T>>() { |
|
254 @Override |
|
255 public Iterator<List<T>> iterator() { |
|
256 return new SubseqIter<T>(Arrays.asList(t)); |
|
257 } |
|
258 }; |
|
259 } |
|
260 |
|
261 // Roll values in 'states' from index i and forward. |
|
262 // Return true if we wrapped back to zero. |
|
263 private boolean roll(int i) { |
|
264 if (i == states.length) |
|
265 return true; |
|
266 if (!roll(i + 1)) |
|
267 return false; |
|
268 states[i] = !states[i]; |
|
269 return !states[i]; |
|
270 } |
|
271 |
|
272 @Override |
|
273 public List<T> next() { |
|
274 if (!hasNext()) |
|
275 throw new NoSuchElementException(); |
|
276 // Include element i if states[i] is true |
|
277 List<T> next = new ArrayList<T>(); |
|
278 for (int i = 0; i < states.length; i++) |
|
279 if (states[i]) |
|
280 next.add(elements.get(i)); |
|
281 if (roll(0)) |
|
282 states = null; // hasNext() == false from now on. |
|
283 return next; |
|
284 } |
|
285 |
|
286 @Override |
|
287 public boolean hasNext() { |
|
288 return states != null; |
|
289 } |
|
290 |
|
291 @Override |
|
292 public void remove() { |
|
293 throw new UnsupportedOperationException(); |
|
294 } |
|
295 } |
|
296 |
|
297 class PermutationIterator<T> implements Iterator<List<T>> { |
|
298 |
|
299 DirInt head; |
|
300 boolean hasNext = true; |
|
301 |
|
302 public PermutationIterator(List<T> toPermute) { |
|
303 ListIterator<T> iter = toPermute.listIterator(); |
|
304 if (iter.hasNext()) |
|
305 head = new DirInt(iter.nextIndex(), iter.next()); |
|
306 DirInt prev = head; |
|
307 while (iter.hasNext()) { |
|
308 DirInt di = new DirInt(iter.nextIndex(), iter.next()); |
|
309 di.left = prev; |
|
310 prev.right = di; |
|
311 prev = di; |
|
312 } |
|
313 } |
|
314 |
|
315 public static <T> Iterable<List<T>> permutationsOf(final List<T> list) { |
|
316 return new Iterable<List<T>>() { |
|
317 public Iterator<List<T>> iterator() { |
|
318 return new PermutationIterator<>(list); |
|
319 } |
|
320 }; |
|
321 } |
|
322 |
|
323 @Override |
|
324 public boolean hasNext() { |
|
325 return hasNext; |
|
326 } |
|
327 |
|
328 @Override |
|
329 public List<T> next() { |
|
330 // Prep return value based on current state |
|
331 List<T> result = new ArrayList<>(); |
|
332 for (DirInt di = head; di != null; di = di.right) |
|
333 result.add(di.object); |
|
334 |
|
335 // Step state forward |
|
336 DirInt maxMob = null; |
|
337 for (DirInt di = head; di != null; di = di.right) |
|
338 if (di.isMobile() && (maxMob == null || di.val > maxMob.val)) |
|
339 maxMob = di; |
|
340 |
|
341 if (maxMob == null) { |
|
342 hasNext = false; |
|
343 } else { |
|
344 maxMob.swapWithAdjacent(); |
|
345 for (DirInt di = head; di != null; di = di.right) |
|
346 if (di.val > maxMob.val) |
|
347 di.facingLeft = !di.facingLeft; |
|
348 } |
|
349 return result; |
|
350 } |
|
351 |
|
352 private final class DirInt { |
|
353 int val; |
|
354 T object; |
|
355 DirInt left, right; |
|
356 boolean facingLeft = true; |
|
357 |
|
358 public DirInt(int val, T object) { |
|
359 this.val = val; |
|
360 this.object = object; |
|
361 } |
|
362 |
|
363 boolean isMobile() { |
|
364 DirInt adjacent = facingLeft ? left : right; |
|
365 return adjacent != null && val > adjacent.val; |
|
366 } |
|
367 |
|
368 public void swapWithAdjacent() { |
|
369 DirInt l = facingLeft ? left : this; |
|
370 DirInt r = facingLeft ? this : right; |
|
371 if (head == l) head = r; |
|
372 if (l.left != null) l.left.right = r; |
|
373 if (r.right != null) r.right.left = l; |
|
374 l.right = r.right; |
|
375 r.left = l.left; |
|
376 r.right = l; |
|
377 l.left = r; |
|
378 } |
|
379 } |
|
380 |
|
381 @Override |
|
382 public void remove() { |
|
383 throw new UnsupportedOperationException(); |
|
384 } |
|
385 } |