Fri, 08 Feb 2013 09:12:37 +0000
7166455: javac doesn't set ACC_STRICT bit on <clinit> for strictfp class
Reviewed-by: mcimadamore
1 /*
2 * Copyright (c) 2010, 2012, 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 */
24 import java.io.*;
25 import java.net.URL;
26 import java.net.URLClassLoader;
27 import java.util.*;
28 import java.util.regex.*;
29 import javax.annotation.processing.Processor;
30 import javax.tools.Diagnostic;
31 import javax.tools.DiagnosticCollector;
32 import javax.tools.JavaCompiler;
33 import javax.tools.JavaCompiler.CompilationTask;
34 import javax.tools.JavaFileObject;
35 import javax.tools.StandardJavaFileManager;
36 import javax.tools.ToolProvider;
38 // The following two classes are both used, but cannot be imported directly
39 // import com.sun.tools.javac.Main
40 // import com.sun.tools.javac.main.Main
42 import com.sun.tools.javac.api.ClientCodeWrapper;
43 import com.sun.tools.javac.file.JavacFileManager;
44 import com.sun.tools.javac.main.Main;
45 import com.sun.tools.javac.util.Context;
46 import com.sun.tools.javac.util.JavacMessages;
47 import com.sun.tools.javac.util.JCDiagnostic;
49 /**
50 * Class to handle example code designed to illustrate javac diagnostic messages.
51 */
52 class Example implements Comparable<Example> {
53 /* Create an Example from the files found at path.
54 * The head of the file, up to the first Java code, is scanned
55 * for information about the test, such as what resource keys it
56 * generates when run, what options are required to run it, and so on.
57 */
58 Example(File file) {
59 this.file = file;
60 declaredKeys = new TreeSet<String>();
61 srcFiles = new ArrayList<File>();
62 procFiles = new ArrayList<File>();
63 supportFiles = new ArrayList<File>();
64 srcPathFiles = new ArrayList<File>();
66 findFiles(file, srcFiles);
67 for (File f: srcFiles) {
68 parse(f);
69 }
71 if (infoFile == null)
72 throw new Error("Example " + file + " has no info file");
73 }
75 private void findFiles(File f, List<File> files) {
76 if (f.isDirectory()) {
77 for (File c: f.listFiles()) {
78 if (files == srcFiles && c.getName().equals("processors"))
79 findFiles(c, procFiles);
80 else if (files == srcFiles && c.getName().equals("sourcepath")) {
81 srcPathDir = c;
82 findFiles(c, srcPathFiles);
83 } else if (files == srcFiles && c.getName().equals("support"))
84 findFiles(c, supportFiles);
85 else
86 findFiles(c, files);
87 }
88 } else if (f.isFile() && f.getName().endsWith(".java")) {
89 files.add(f);
90 }
91 }
93 private void parse(File f) {
94 Pattern keyPat = Pattern.compile(" *// *key: *([^ ]+) *");
95 Pattern optPat = Pattern.compile(" *// *options: *(.*)");
96 Pattern runPat = Pattern.compile(" *// *run: *(.*)");
97 Pattern javaPat = Pattern.compile(" *@?[A-Za-z].*");
98 try {
99 String[] lines = read(f).split("[\r\n]+");
100 for (String line: lines) {
101 Matcher keyMatch = keyPat.matcher(line);
102 if (keyMatch.matches()) {
103 foundInfo(f);
104 declaredKeys.add(keyMatch.group(1));
105 continue;
106 }
107 Matcher optMatch = optPat.matcher(line);
108 if (optMatch.matches()) {
109 foundInfo(f);
110 options = Arrays.asList(optMatch.group(1).trim().split(" +"));
111 continue;
112 }
113 Matcher runMatch = runPat.matcher(line);
114 if (runMatch.matches()) {
115 foundInfo(f);
116 runOpts = Arrays.asList(runMatch.group(1).trim().split(" +"));
117 }
118 if (javaPat.matcher(line).matches())
119 break;
120 }
121 } catch (IOException e) {
122 throw new Error(e);
123 }
124 }
126 private void foundInfo(File file) {
127 if (infoFile != null && !infoFile.equals(file))
128 throw new Error("multiple info files found: " + infoFile + ", " + file);
129 infoFile = file;
130 }
132 String getName() {
133 return file.getName();
134 }
136 /**
137 * Get the set of resource keys that this test declares it will generate
138 * when it is run.
139 */
140 Set<String> getDeclaredKeys() {
141 return declaredKeys;
142 }
144 /**
145 * Get the set of resource keys that this test generates when it is run.
146 * The test will be run if it has not already been run.
147 */
148 Set<String> getActualKeys() {
149 if (actualKeys == null)
150 actualKeys = run(false);
151 return actualKeys;
152 }
154 /**
155 * Run the test. Information in the test header is used to determine
156 * how to run the test.
157 */
158 void run(PrintWriter out, boolean raw, boolean verbose) {
159 if (out == null)
160 throw new NullPointerException();
161 try {
162 run(out, null, raw, verbose);
163 } catch (IOException e) {
164 e.printStackTrace(out);
165 }
166 }
168 Set<String> run(boolean verbose) {
169 Set<String> keys = new TreeSet<String>();
170 try {
171 run(null, keys, true, verbose);
172 } catch (IOException e) {
173 e.printStackTrace(System.err);
174 }
175 return keys;
176 }
178 /**
179 * Run the test. Information in the test header is used to determine
180 * how to run the test.
181 */
182 private void run(PrintWriter out, Set<String> keys, boolean raw, boolean verbose)
183 throws IOException {
184 ClassLoader loader = getClass().getClassLoader();
185 if (supportFiles.size() > 0) {
186 File supportDir = new File(tempDir, "support");
187 supportDir.mkdirs();
188 clean(supportDir);
189 List<String> sOpts = Arrays.asList("-d", supportDir.getPath());
190 new Jsr199Compiler(verbose).run(null, null, false, sOpts, procFiles);
191 URLClassLoader ucl =
192 new URLClassLoader(new URL[] { supportDir.toURI().toURL() }, loader);
193 loader = ucl;
194 }
196 File classesDir = new File(tempDir, "classes");
197 classesDir.mkdirs();
198 clean(classesDir);
200 List<String> opts = new ArrayList<String>();
201 opts.add("-d");
202 opts.add(classesDir.getPath());
203 if (options != null)
204 opts.addAll(options);
206 if (procFiles.size() > 0) {
207 List<String> pOpts = Arrays.asList("-d", classesDir.getPath());
208 new Jsr199Compiler(verbose).run(null, null, false, pOpts, procFiles);
209 opts.add("-classpath"); // avoid using -processorpath for now
210 opts.add(classesDir.getPath());
211 createAnnotationServicesFile(classesDir, procFiles);
212 } else if (options != null) {
213 int i = options.indexOf("-processor");
214 // check for built-in anno-processor(s)
215 if (i != -1 && options.get(i + 1).equals("DocCommentProcessor")) {
216 opts.add("-classpath");
217 opts.add(System.getProperty("test.classes"));
218 }
219 }
221 if (srcPathDir != null) {
222 opts.add("-sourcepath");
223 opts.add(srcPathDir.getPath());
224 }
226 try {
227 Compiler c = Compiler.getCompiler(runOpts, verbose);
228 c.run(out, keys, raw, opts, srcFiles);
229 } catch (IllegalArgumentException e) {
230 if (out != null) {
231 out.println("Invalid value for run tag: " + runOpts);
232 }
233 }
234 }
236 void createAnnotationServicesFile(File dir, List<File> procFiles) throws IOException {
237 File servicesDir = new File(new File(dir, "META-INF"), "services");
238 servicesDir.mkdirs();
239 File annoServices = new File(servicesDir, Processor.class.getName());
240 Writer out = new FileWriter(annoServices);
241 try {
242 for (File f: procFiles) {
243 out.write(f.getName().toString().replace(".java", ""));
244 }
245 } finally {
246 out.close();
247 }
248 }
250 @Override
251 public int compareTo(Example e) {
252 return file.compareTo(e.file);
253 }
255 @Override
256 public String toString() {
257 return file.getPath();
258 }
260 /**
261 * Read the contents of a file.
262 */
263 private String read(File f) throws IOException {
264 byte[] bytes = new byte[(int) f.length()];
265 DataInputStream in = new DataInputStream(new FileInputStream(f));
266 try {
267 in.readFully(bytes);
268 } finally {
269 in.close();
270 }
271 return new String(bytes);
272 }
274 /**
275 * Clean the contents of a directory.
276 */
277 boolean clean(File dir) {
278 boolean ok = true;
279 for (File f: dir.listFiles()) {
280 if (f.isDirectory())
281 ok &= clean(f);
282 ok &= f.delete();
283 }
284 return ok;
285 }
287 File file;
288 List<File> srcFiles;
289 List<File> procFiles;
290 File srcPathDir;
291 List<File> srcPathFiles;
292 List<File> supportFiles;
293 File infoFile;
294 private List<String> runOpts;
295 private List<String> options;
296 private Set<String> actualKeys;
297 private Set<String> declaredKeys;
299 static File tempDir = new File(System.getProperty("java.io.tmpdir"));
300 static void setTempDir(File tempDir) {
301 Example.tempDir = tempDir;
302 }
304 abstract static class Compiler {
305 interface Factory {
306 Compiler getCompiler(List<String> opts, boolean verbose);
307 }
309 static class DefaultFactory implements Factory {
310 public Compiler getCompiler(List<String> opts, boolean verbose) {
311 String first;
312 String[] rest;
313 if (opts == null || opts.isEmpty()) {
314 first = null;
315 rest = new String[0];
316 } else {
317 first = opts.get(0);
318 rest = opts.subList(1, opts.size()).toArray(new String[opts.size() - 1]);
319 }
320 if (first == null || first.equals("jsr199"))
321 return new Jsr199Compiler(verbose, rest);
322 else if (first.equals("simple"))
323 return new SimpleCompiler(verbose);
324 else if (first.equals("backdoor"))
325 return new BackdoorCompiler(verbose);
326 else
327 throw new IllegalArgumentException(first);
328 }
329 }
331 static Factory factory;
333 static Compiler getCompiler(List<String> opts, boolean verbose) {
334 if (factory == null)
335 factory = new DefaultFactory();
337 return factory.getCompiler(opts, verbose);
338 }
340 protected Compiler(boolean verbose) {
341 this.verbose = verbose;
342 }
344 abstract boolean run(PrintWriter out, Set<String> keys, boolean raw,
345 List<String> opts, List<File> files);
347 void setSupportClassLoader(ClassLoader cl) {
348 loader = cl;
349 }
351 protected ClassLoader loader;
352 protected boolean verbose;
353 }
355 /**
356 * Compile using the JSR 199 API. The diagnostics generated are
357 * scanned for resource keys. Not all diagnostic keys are generated
358 * via the JSR 199 API -- for example, rich diagnostics are not directly
359 * accessible, and some diagnostics generated by the file manager may
360 * not be generated (for example, the JSR 199 file manager does not see
361 * -Xlint:path).
362 */
363 static class Jsr199Compiler extends Compiler {
364 List<String> fmOpts;
366 Jsr199Compiler(boolean verbose, String... args) {
367 super(verbose);
368 for (int i = 0; i < args.length; i++) {
369 String arg = args[i];
370 if (arg.equals("-filemanager") && (i + 1 < args.length)) {
371 fmOpts = Arrays.asList(args[++i].split(","));
372 } else
373 throw new IllegalArgumentException(arg);
374 }
375 }
377 @Override
378 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
379 if (out != null && keys != null)
380 throw new IllegalArgumentException();
382 if (verbose)
383 System.err.println("run_jsr199: " + opts + " " + files);
385 DiagnosticCollector<JavaFileObject> dc = null;
386 if (keys != null)
387 dc = new DiagnosticCollector<JavaFileObject>();
389 if (raw) {
390 List<String> newOpts = new ArrayList<String>();
391 newOpts.add("-XDrawDiagnostics");
392 newOpts.addAll(opts);
393 opts = newOpts;
394 }
396 JavaCompiler c = ToolProvider.getSystemJavaCompiler();
398 StandardJavaFileManager fm = c.getStandardFileManager(dc, null, null);
399 if (fmOpts != null)
400 fm = new FileManager(fm, fmOpts);
402 Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
404 CompilationTask t = c.getTask(out, fm, dc, opts, null, fos);
405 Boolean ok = t.call();
407 if (keys != null) {
408 for (Diagnostic<? extends JavaFileObject> d: dc.getDiagnostics()) {
409 scanForKeys(unwrap(d), keys);
410 }
411 }
413 return ok;
414 }
416 /**
417 * Scan a diagnostic for resource keys. This will not detect additional
418 * sub diagnostics that might be generated by a rich diagnostic formatter.
419 */
420 private static void scanForKeys(JCDiagnostic d, Set<String> keys) {
421 keys.add(d.getCode());
422 for (Object o: d.getArgs()) {
423 if (o instanceof JCDiagnostic) {
424 scanForKeys((JCDiagnostic) o, keys);
425 }
426 }
427 for (JCDiagnostic sd: d.getSubdiagnostics())
428 scanForKeys(sd, keys);
429 }
431 private JCDiagnostic unwrap(Diagnostic<? extends JavaFileObject> diagnostic) {
432 if (diagnostic instanceof JCDiagnostic)
433 return (JCDiagnostic) diagnostic;
434 if (diagnostic instanceof ClientCodeWrapper.DiagnosticSourceUnwrapper)
435 return ((ClientCodeWrapper.DiagnosticSourceUnwrapper)diagnostic).d;
436 throw new IllegalArgumentException();
437 }
438 }
440 /**
441 * Run the test using the standard simple entry point.
442 */
443 static class SimpleCompiler extends Compiler {
444 SimpleCompiler(boolean verbose) {
445 super(verbose);
446 }
448 @Override
449 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
450 if (out != null && keys != null)
451 throw new IllegalArgumentException();
453 if (verbose)
454 System.err.println("run_simple: " + opts + " " + files);
456 List<String> args = new ArrayList<String>();
458 if (keys != null || raw)
459 args.add("-XDrawDiagnostics");
461 args.addAll(opts);
462 for (File f: files)
463 args.add(f.getPath());
465 StringWriter sw = null;
466 PrintWriter pw;
467 if (keys != null) {
468 sw = new StringWriter();
469 pw = new PrintWriter(sw);
470 } else
471 pw = out;
473 int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), pw);
475 if (keys != null) {
476 pw.close();
477 scanForKeys(sw.toString(), keys);
478 }
480 return (rc == 0);
481 }
483 private static void scanForKeys(String text, Set<String> keys) {
484 StringTokenizer st = new StringTokenizer(text, " ,\r\n():");
485 while (st.hasMoreElements()) {
486 String t = st.nextToken();
487 if (t.startsWith("compiler."))
488 keys.add(t);
489 }
490 }
491 }
493 static class BackdoorCompiler extends Compiler {
494 BackdoorCompiler(boolean verbose) {
495 super(verbose);
496 }
498 @Override
499 boolean run(PrintWriter out, Set<String> keys, boolean raw, List<String> opts, List<File> files) {
500 if (out != null && keys != null)
501 throw new IllegalArgumentException();
503 if (verbose)
504 System.err.println("run_simple: " + opts + " " + files);
506 List<String> args = new ArrayList<String>();
508 if (out != null && raw)
509 args.add("-XDrawDiagnostics");
511 args.addAll(opts);
512 for (File f: files)
513 args.add(f.getPath());
515 StringWriter sw = null;
516 PrintWriter pw;
517 if (keys != null) {
518 sw = new StringWriter();
519 pw = new PrintWriter(sw);
520 } else
521 pw = out;
523 Context c = new Context();
524 JavacFileManager.preRegister(c); // can't create it until Log has been set up
525 MessageTracker.preRegister(c, keys);
526 Main m = new Main("javac", pw);
527 Main.Result rc = m.compile(args.toArray(new String[args.size()]), c);
529 if (keys != null) {
530 pw.close();
531 }
533 return rc.isOK();
534 }
536 static class MessageTracker extends JavacMessages {
538 MessageTracker(Context context) {
539 super(context);
540 }
542 static void preRegister(Context c, final Set<String> keys) {
543 if (keys != null) {
544 c.put(JavacMessages.messagesKey, new Context.Factory<JavacMessages>() {
545 public JavacMessages make(Context c) {
546 return new MessageTracker(c) {
547 @Override
548 public String getLocalizedString(Locale l, String key, Object... args) {
549 keys.add(key);
550 return super.getLocalizedString(l, key, args);
551 }
552 };
553 }
554 });
555 }
556 }
557 }
559 }
560 }