test/tools/javac/diags/Example.java

Wed, 31 Aug 2011 15:39:00 -0700

author
jjg
date
Wed, 31 Aug 2011 15:39:00 -0700
changeset 1073
f85d980faaf8
parent 894
23b64ad3eec8
child 1097
497571d34112
permissions
-rw-r--r--

7074416: Regression: JSR199: javac doesn't unwrap clientcodewrapper objects
Reviewed-by: mcimadamore

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

mercurial