8001098: Provide a simple light-weight "plug-in" mechanism for javac

Mon, 19 Nov 2012 11:38:49 -0800

author
jjg
date
Mon, 19 Nov 2012 11:38:49 -0800
changeset 1416
c0f0c41cafa0
parent 1415
01c9d4161882
child 1417
522a1ee72340

8001098: Provide a simple light-weight "plug-in" mechanism for javac
Reviewed-by: mcimadamore

src/share/classes/com/sun/source/util/Plugin.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/source/util/Trees.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/api/JavacTrees.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/main/JavaCompiler.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/main/Main.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/main/Option.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/resources/javac.properties file | annotate | diff | comparison | revisions
test/tools/javac/plugin/showtype/Identifiers.java file | annotate | diff | comparison | revisions
test/tools/javac/plugin/showtype/Identifiers.out file | annotate | diff | comparison | revisions
test/tools/javac/plugin/showtype/Identifiers_PI.out file | annotate | diff | comparison | revisions
test/tools/javac/plugin/showtype/ShowTypePlugin.java file | annotate | diff | comparison | revisions
test/tools/javac/plugin/showtype/Test.java file | annotate | diff | comparison | revisions
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/share/classes/com/sun/source/util/Plugin.java	Mon Nov 19 11:38:49 2012 -0800
     1.3 @@ -0,0 +1,64 @@
     1.4 +/*
     1.5 + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Oracle designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Oracle in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.25 + * or visit www.oracle.com if you need additional information or have any
    1.26 + * questions.
    1.27 + */
    1.28 +package com.sun.source.util;
    1.29 +
    1.30 +import java.util.ServiceLoader;
    1.31 +import javax.tools.StandardLocation;
    1.32 +
    1.33 +/**
    1.34 + * The interface for a javac plug-in.
    1.35 + *
    1.36 + * <p>The javac plug-in mechanism allows a user to specify one or more plug-ins
    1.37 + * on the javac command line, to be started soon after the compilation
    1.38 + * has begun. Plug-ins are identified by a user-friendly name. Each plug-in that
    1.39 + * is started will be passed an array of strings, which may be used to
    1.40 + * provide the plug-in with values for any desired options or other arguments.
    1.41 + *
    1.42 + * <p>Plug-ins are located via a {@link ServiceLoader},
    1.43 + * using the same class path as annotation processors (i.e.
    1.44 + * {@link StandardLocation#PROCESSOR_PATH PROCESSOR_PATH} or
    1.45 + * {@code -processorpath}).
    1.46 + *
    1.47 + * <p>It is expected that a typical plug-in will simply register a
    1.48 + * {@link TaskListener} to be informed of events during the execution
    1.49 + * of the compilation, and that the rest of the work will be done
    1.50 + * by the task listener.
    1.51 + *
    1.52 + * @since 1.8
    1.53 + */
    1.54 +public interface Plugin {
    1.55 +    /**
    1.56 +     * Get the user-friendly name of this plug-in.
    1.57 +     * @return the user-friendly name of the plug-in
    1.58 +     */
    1.59 +    String getName();
    1.60 +
    1.61 +    /**
    1.62 +     * Invoke the plug-in for a given compilation task.
    1.63 +     * @param task The compilation task that has just been started
    1.64 +     * @param args Arguments, if any, for the plug-in
    1.65 +     */
    1.66 +    void call(JavacTask task, String... args);
    1.67 +}
     2.1 --- a/src/share/classes/com/sun/source/util/Trees.java	Sat Nov 17 19:01:03 2012 +0000
     2.2 +++ b/src/share/classes/com/sun/source/util/Trees.java	Mon Nov 19 11:38:49 2012 -0800
     2.3 @@ -58,7 +58,9 @@
     2.4       * @throws IllegalArgumentException if the task does not support the Trees API.
     2.5       */
     2.6      public static Trees instance(CompilationTask task) {
     2.7 -        if (!task.getClass().getName().equals("com.sun.tools.javac.api.JavacTaskImpl"))
     2.8 +        String taskClassName = task.getClass().getName();
     2.9 +        if (!taskClassName.equals("com.sun.tools.javac.api.JavacTaskImpl")
    2.10 +                && !taskClassName.equals("com.sun.tools.javac.api.BasicJavacTask"))
    2.11              throw new IllegalArgumentException();
    2.12          return getJavacTrees(CompilationTask.class, task);
    2.13      }
     3.1 --- a/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Sat Nov 17 19:01:03 2012 +0000
     3.2 +++ b/src/share/classes/com/sun/tools/javac/api/BasicJavacTask.java	Mon Nov 19 11:38:49 2012 -0800
     3.3 @@ -140,6 +140,14 @@
     3.4       * For internal use only.  This method will be
     3.5       * removed without warning.
     3.6       */
     3.7 +    public Context getContext() {
     3.8 +        return context;
     3.9 +    }
    3.10 +
    3.11 +    /**
    3.12 +     * For internal use only.  This method will be
    3.13 +     * removed without warning.
    3.14 +     */
    3.15      public void updateContext(Context newContext) {
    3.16          context = newContext;
    3.17      }
     4.1 --- a/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java	Sat Nov 17 19:01:03 2012 +0000
     4.2 +++ b/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java	Mon Nov 19 11:38:49 2012 -0800
     4.3 @@ -489,22 +489,6 @@
     4.4       * For internal use only.  This method will be
     4.5       * removed without warning.
     4.6       */
     4.7 -    public Context getContext() {
     4.8 -        return context;
     4.9 -    }
    4.10 -
    4.11 -    /**
    4.12 -     * For internal use only.  This method will be
    4.13 -     * removed without warning.
    4.14 -     */
    4.15 -    public void updateContext(Context newContext) {
    4.16 -        context = newContext;
    4.17 -    }
    4.18 -
    4.19 -    /**
    4.20 -     * For internal use only.  This method will be
    4.21 -     * removed without warning.
    4.22 -     */
    4.23      public Type parseType(String expr, TypeElement scope) {
    4.24          if (expr == null || expr.equals(""))
    4.25              throw new IllegalArgumentException();
     5.1 --- a/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Sat Nov 17 19:01:03 2012 +0000
     5.2 +++ b/src/share/classes/com/sun/tools/javac/api/JavacTrees.java	Mon Nov 19 11:38:49 2012 -0800
     5.3 @@ -121,9 +121,9 @@
     5.4  
     5.5      // called reflectively from Trees.instance(CompilationTask task)
     5.6      public static JavacTrees instance(JavaCompiler.CompilationTask task) {
     5.7 -        if (!(task instanceof JavacTaskImpl))
     5.8 +        if (!(task instanceof BasicJavacTask))
     5.9              throw new IllegalArgumentException();
    5.10 -        return instance(((JavacTaskImpl)task).getContext());
    5.11 +        return instance(((BasicJavacTask)task).getContext());
    5.12      }
    5.13  
    5.14      // called reflectively from Trees.instance(ProcessingEnvironment env)
     6.1 --- a/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Sat Nov 17 19:01:03 2012 +0000
     6.2 +++ b/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Mon Nov 19 11:38:49 2012 -0800
     6.3 @@ -1040,7 +1040,8 @@
     6.4          if (options.isSet(PROC, "none")) {
     6.5              processAnnotations = false;
     6.6          } else if (procEnvImpl == null) {
     6.7 -            procEnvImpl = new JavacProcessingEnvironment(context, processors);
     6.8 +            procEnvImpl = JavacProcessingEnvironment.instance(context);
     6.9 +            procEnvImpl.setProcessors(processors);
    6.10              processAnnotations = procEnvImpl.atLeastOneProcessor();
    6.11  
    6.12              if (processAnnotations) {
     7.1 --- a/src/share/classes/com/sun/tools/javac/main/Main.java	Sat Nov 17 19:01:03 2012 +0000
     7.2 +++ b/src/share/classes/com/sun/tools/javac/main/Main.java	Mon Nov 19 11:38:49 2012 -0800
     7.3 @@ -33,24 +33,29 @@
     7.4  import java.security.MessageDigest;
     7.5  import java.util.Arrays;
     7.6  import java.util.Collection;
     7.7 +import java.util.Iterator;
     7.8  import java.util.LinkedHashSet;
     7.9 +import java.util.ServiceLoader;
    7.10  import java.util.Set;
    7.11 +
    7.12 +import javax.annotation.processing.Processor;
    7.13  import javax.tools.JavaFileManager;
    7.14  import javax.tools.JavaFileObject;
    7.15 -import javax.annotation.processing.Processor;
    7.16  
    7.17 +import com.sun.source.util.JavacTask;
    7.18 +import com.sun.source.util.Plugin;
    7.19  import com.sun.tools.javac.code.Source;
    7.20  import com.sun.tools.javac.file.CacheFSInfo;
    7.21  import com.sun.tools.javac.file.JavacFileManager;
    7.22  import com.sun.tools.javac.jvm.Target;
    7.23 +import com.sun.tools.javac.processing.AnnotationProcessingError;
    7.24 +import com.sun.tools.javac.processing.JavacProcessingEnvironment;
    7.25  import com.sun.tools.javac.util.*;
    7.26 +import com.sun.tools.javac.util.Log.PrefixKind;
    7.27  import com.sun.tools.javac.util.Log.WriterKind;
    7.28 -import com.sun.tools.javac.util.Log.PrefixKind;
    7.29 -import com.sun.tools.javac.processing.AnnotationProcessingError;
    7.30 -
    7.31  import static com.sun.tools.javac.main.Option.*;
    7.32  
    7.33 -/** This class provides a commandline interface to the GJC compiler.
    7.34 +/** This class provides a command line interface to the javac compiler.
    7.35   *
    7.36   *  <p><b>This is NOT part of any supported API.
    7.37   *  If you write code that depends on this, you do so at your own risk.
    7.38 @@ -423,6 +428,42 @@
    7.39              if (batchMode)
    7.40                  CacheFSInfo.preRegister(context);
    7.41  
    7.42 +            // invoke any available plugins
    7.43 +            String plugins = options.get(PLUGIN);
    7.44 +            if (plugins != null) {
    7.45 +                JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context);
    7.46 +                ClassLoader cl = pEnv.getProcessorClassLoader();
    7.47 +                ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, cl);
    7.48 +                Set<List<String>> pluginsToCall = new LinkedHashSet<List<String>>();
    7.49 +                for (String plugin: plugins.split("\\x00")) {
    7.50 +                    pluginsToCall.add(List.from(plugin.split("\\s+")));
    7.51 +                }
    7.52 +                JavacTask task = null;
    7.53 +                Iterator<Plugin> iter = sl.iterator();
    7.54 +                while (iter.hasNext()) {
    7.55 +                    Plugin plugin = iter.next();
    7.56 +                    for (List<String> p: pluginsToCall) {
    7.57 +                        if (plugin.getName().equals(p.head)) {
    7.58 +                            pluginsToCall.remove(p);
    7.59 +                            try {
    7.60 +                                if (task == null)
    7.61 +                                    task = JavacTask.instance(pEnv);
    7.62 +                                plugin.call(task, p.tail.toArray(new String[p.tail.size()]));
    7.63 +                            } catch (Throwable ex) {
    7.64 +                                if (apiMode)
    7.65 +                                    throw new RuntimeException(ex);
    7.66 +                                pluginMessage(ex);
    7.67 +                                return Result.SYSERR;
    7.68 +                            }
    7.69 +
    7.70 +                        }
    7.71 +                    }
    7.72 +                }
    7.73 +                for (List<String> p: pluginsToCall) {
    7.74 +                    log.printLines(PrefixKind.JAVAC, "msg.plugin.not.found", p.head);
    7.75 +                }
    7.76 +            }
    7.77 +
    7.78              fileManager = context.get(JavaFileManager.class);
    7.79  
    7.80              comp = JavaCompiler.instance(context);
    7.81 @@ -537,6 +578,14 @@
    7.82          ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE));
    7.83      }
    7.84  
    7.85 +    /** Print a message reporting an uncaught exception from an
    7.86 +     * annotation processor.
    7.87 +     */
    7.88 +    void pluginMessage(Throwable ex) {
    7.89 +        log.printLines(PrefixKind.JAVAC, "msg.plugin.uncaught.exception");
    7.90 +        ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
    7.91 +    }
    7.92 +
    7.93      /** Display the location and checksum of a class. */
    7.94      void showClass(String className) {
    7.95          PrintWriter pw = log.getWriter(WriterKind.NOTICE);
     8.1 --- a/src/share/classes/com/sun/tools/javac/main/Option.java	Sat Nov 17 19:01:03 2012 +0000
     8.2 +++ b/src/share/classes/com/sun/tools/javac/main/Option.java	Mon Nov 19 11:38:49 2012 -0800
     8.3 @@ -393,6 +393,16 @@
     8.4      /* -Xjcov produces tables to support the code coverage tool jcov. */
     8.5      XJCOV("-Xjcov", null, HIDDEN, BASIC),
     8.6  
     8.7 +    PLUGIN("-Xplugin:", "opt.arg.plugin", "opt.plugin", EXTENDED, BASIC) {
     8.8 +        @Override
     8.9 +        public boolean process(OptionHelper helper, String option) {
    8.10 +            String p = option.substring(option.indexOf(':') + 1);
    8.11 +            String prev = helper.get(PLUGIN);
    8.12 +            helper.put(PLUGIN.text, (prev == null) ? p : prev + '\0' + p.trim());
    8.13 +            return false;
    8.14 +        }
    8.15 +    },
    8.16 +
    8.17      /* This is a back door to the compiler's option table.
    8.18       * -XDx=y sets the option x to the value y.
    8.19       * -XDx sets the option x to the value x.
     9.1 --- a/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Sat Nov 17 19:01:03 2012 +0000
     9.2 +++ b/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Mon Nov 19 11:38:49 2012 -0800
     9.3 @@ -145,6 +145,7 @@
     9.4      Source source;
     9.5  
     9.6      private ClassLoader processorClassLoader;
     9.7 +    private SecurityException processorClassLoaderException;
     9.8  
     9.9      /**
    9.10       * JavacMessages object used for localization
    9.11 @@ -155,7 +156,15 @@
    9.12  
    9.13      private Context context;
    9.14  
    9.15 -    public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
    9.16 +    /** Get the JavacProcessingEnvironment instance for this context. */
    9.17 +    public static JavacProcessingEnvironment instance(Context context) {
    9.18 +        JavacProcessingEnvironment instance = context.get(JavacProcessingEnvironment.class);
    9.19 +        if (instance == null)
    9.20 +            instance = new JavacProcessingEnvironment(context);
    9.21 +        return instance;
    9.22 +    }
    9.23 +
    9.24 +    protected JavacProcessingEnvironment(Context context) {
    9.25          this.context = context;
    9.26          log = Log.instance(context);
    9.27          source = Source.instance(context);
    9.28 @@ -184,6 +193,11 @@
    9.29          unmatchedProcessorOptions = initUnmatchedProcessorOptions();
    9.30          messages = JavacMessages.instance(context);
    9.31          taskListener = MultiTaskListener.instance(context);
    9.32 +        initProcessorClassLoader();
    9.33 +    }
    9.34 +
    9.35 +    public void setProcessors(Iterable<? extends Processor> processors) {
    9.36 +        Assert.checkNull(discoveredProcs);
    9.37          initProcessorIterator(context, processors);
    9.38      }
    9.39  
    9.40 @@ -199,6 +213,23 @@
    9.41          return Collections.unmodifiableSet(platformAnnotations);
    9.42      }
    9.43  
    9.44 +    private void initProcessorClassLoader() {
    9.45 +        JavaFileManager fileManager = context.get(JavaFileManager.class);
    9.46 +        try {
    9.47 +            // If processorpath is not explicitly set, use the classpath.
    9.48 +            processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
    9.49 +                ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
    9.50 +                : fileManager.getClassLoader(CLASS_PATH);
    9.51 +
    9.52 +            if (processorClassLoader != null && processorClassLoader instanceof Closeable) {
    9.53 +                JavaCompiler compiler = JavaCompiler.instance(context);
    9.54 +                compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader);
    9.55 +            }
    9.56 +        } catch (SecurityException e) {
    9.57 +            processorClassLoaderException = e;
    9.58 +        }
    9.59 +    }
    9.60 +
    9.61      private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
    9.62          Log   log   = Log.instance(context);
    9.63          Iterator<? extends Processor> processorIterator;
    9.64 @@ -217,18 +248,7 @@
    9.65              processorIterator = processors.iterator();
    9.66          } else {
    9.67              String processorNames = options.get(PROCESSOR);
    9.68 -            JavaFileManager fileManager = context.get(JavaFileManager.class);
    9.69 -            try {
    9.70 -                // If processorpath is not explicitly set, use the classpath.
    9.71 -                processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
    9.72 -                    ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
    9.73 -                    : fileManager.getClassLoader(CLASS_PATH);
    9.74 -
    9.75 -                if (processorClassLoader != null && processorClassLoader instanceof Closeable) {
    9.76 -                    JavaCompiler compiler = JavaCompiler.instance(context);
    9.77 -                    compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader);
    9.78 -                }
    9.79 -
    9.80 +            if (processorClassLoaderException == null) {
    9.81                  /*
    9.82                   * If the "-processor" option is used, search the appropriate
    9.83                   * path for the named class.  Otherwise, use a service
    9.84 @@ -239,14 +259,15 @@
    9.85                  } else {
    9.86                      processorIterator = new ServiceIterator(processorClassLoader, log);
    9.87                  }
    9.88 -            } catch (SecurityException e) {
    9.89 +            } else {
    9.90                  /*
    9.91                   * A security exception will occur if we can't create a classloader.
    9.92                   * Ignore the exception if, with hindsight, we didn't need it anyway
    9.93                   * (i.e. no processor was specified either explicitly, or implicitly,
    9.94                   * in service configuration file.) Otherwise, we cannot continue.
    9.95                   */
    9.96 -                processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
    9.97 +                processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader",
    9.98 +                        processorClassLoaderException);
    9.99              }
   9.100          }
   9.101          discoveredProcs = new DiscoveredProcessors(processorIterator);
   9.102 @@ -1473,13 +1494,19 @@
   9.103      }
   9.104  
   9.105      /**
   9.106 -     * For internal use only.  This method will be
   9.107 -     * removed without warning.
   9.108 +     * For internal use only.  This method may be removed without warning.
   9.109       */
   9.110      public Context getContext() {
   9.111          return context;
   9.112      }
   9.113  
   9.114 +    /**
   9.115 +     * For internal use only.  This method may be removed without warning.
   9.116 +     */
   9.117 +    public ClassLoader getProcessorClassLoader() {
   9.118 +        return processorClassLoader;
   9.119 +    }
   9.120 +
   9.121      public String toString() {
   9.122          return "javac ProcessingEnvironment";
   9.123      }
    10.1 --- a/src/share/classes/com/sun/tools/javac/resources/javac.properties	Sat Nov 17 19:01:03 2012 +0000
    10.2 +++ b/src/share/classes/com/sun/tools/javac/resources/javac.properties	Mon Nov 19 11:38:49 2012 -0800
    10.3 @@ -99,6 +99,10 @@
    10.4      <release>
    10.5  javac.opt.arg.number=\
    10.6      <number>
    10.7 +javac.opt.plugin=\
    10.8 +    Name and optional arguments for a plug-in to be run
    10.9 +javac.opt.arg.plugin=\
   10.10 +    "name args"
   10.11  
   10.12  ## extended options
   10.13  
   10.14 @@ -185,6 +189,8 @@
   10.15      not a directory: {0}
   10.16  javac.err.file.not.file=\
   10.17      not a file: {0}
   10.18 +javac.msg.plugin.not.found=\
   10.19 +    plug-in not found: {0}
   10.20  ## messages
   10.21  
   10.22  javac.msg.usage.header=\
   10.23 @@ -212,6 +218,10 @@
   10.24  \n\nAn annotation processor threw an uncaught exception.\n\
   10.25  Consult the following stack trace for details.\n
   10.26  
   10.27 +javac.msg.plugin.uncaught.exception=\
   10.28 +\n\nA plugin threw an uncaught exception.\n\
   10.29 +Consult the following stack trace for details.\n
   10.30 +
   10.31  javac.msg.resource=\
   10.32  \n\nThe system is out of resources.\n\
   10.33  Consult the following stack trace for details.\n
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/test/tools/javac/plugin/showtype/Identifiers.java	Mon Nov 19 11:38:49 2012 -0800
    11.3 @@ -0,0 +1,7 @@
    11.4 +/* /nodynamiccopyright */
    11.5 +
    11.6 +public class Identifiers {
    11.7 +    public double E = Math.E;
    11.8 +    public double PI = Math.PI;
    11.9 +    public double PIE = PI + E;
   11.10 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/test/tools/javac/plugin/showtype/Identifiers.out	Mon Nov 19 11:38:49 2012 -0800
    12.3 @@ -0,0 +1,21 @@
    12.4 +Identifiers.java:3: Note: type is ()void
    12.5 +public class Identifiers {
    12.6 +       ^
    12.7 +Identifiers.java:4: Note: type is double
    12.8 +    public double E = Math.E;
    12.9 +                          ^
   12.10 +Identifiers.java:4: Note: type is java.lang.Math
   12.11 +    public double E = Math.E;
   12.12 +                      ^
   12.13 +Identifiers.java:5: Note: type is double
   12.14 +    public double PI = Math.PI;
   12.15 +                           ^
   12.16 +Identifiers.java:5: Note: type is java.lang.Math
   12.17 +    public double PI = Math.PI;
   12.18 +                       ^
   12.19 +Identifiers.java:6: Note: type is double
   12.20 +    public double PIE = PI + E;
   12.21 +                        ^
   12.22 +Identifiers.java:6: Note: type is double
   12.23 +    public double PIE = PI + E;
   12.24 +                             ^
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/test/tools/javac/plugin/showtype/Identifiers_PI.out	Mon Nov 19 11:38:49 2012 -0800
    13.3 @@ -0,0 +1,6 @@
    13.4 +Identifiers.java:5: Note: type is double
    13.5 +    public double PI = Math.PI;
    13.6 +                           ^
    13.7 +Identifiers.java:6: Note: type is double
    13.8 +    public double PIE = PI + E;
    13.9 +                        ^
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/test/tools/javac/plugin/showtype/ShowTypePlugin.java	Mon Nov 19 11:38:49 2012 -0800
    14.3 @@ -0,0 +1,106 @@
    14.4 +/*
    14.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
    14.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    14.7 + *
    14.8 + * This code is free software; you can redistribute it and/or modify it
    14.9 + * under the terms of the GNU General Public License version 2 only, as
   14.10 + * published by the Free Software Foundation.
   14.11 + *
   14.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   14.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   14.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14.15 + * version 2 for more details (a copy is included in the LICENSE file that
   14.16 + * accompanied this code).
   14.17 + *
   14.18 + * You should have received a copy of the GNU General Public License version
   14.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   14.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   14.21 + *
   14.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   14.23 + * or visit www.oracle.com if you need additional information or have any
   14.24 + * questions.
   14.25 + */
   14.26 +
   14.27 +import com.sun.source.tree.CompilationUnitTree;
   14.28 +import com.sun.source.tree.IdentifierTree;
   14.29 +import com.sun.source.tree.MemberSelectTree;
   14.30 +import com.sun.source.tree.Tree;
   14.31 +import com.sun.source.util.JavacTask;
   14.32 +import com.sun.source.util.Plugin;
   14.33 +import com.sun.source.util.TaskEvent;
   14.34 +import com.sun.source.util.TaskListener;
   14.35 +import com.sun.source.util.TreePathScanner;
   14.36 +import com.sun.source.util.Trees;
   14.37 +import java.util.regex.Pattern;
   14.38 +import javax.lang.model.type.TypeMirror;
   14.39 +import javax.tools.Diagnostic.Kind;
   14.40 +
   14.41 +public class ShowTypePlugin implements Plugin {
   14.42 +
   14.43 +    public String getName() {
   14.44 +        return "showtype";
   14.45 +    }
   14.46 +
   14.47 +    public void call(JavacTask task, String... args) {
   14.48 +        Pattern pattern = null;
   14.49 +        if (args.length == 1)
   14.50 +            pattern = Pattern.compile(args[0]);
   14.51 +        task.addTaskListener(new PostAnalyzeTaskListener(task, pattern));
   14.52 +    }
   14.53 +
   14.54 +    private static class PostAnalyzeTaskListener implements TaskListener {
   14.55 +        private final ShowTypeTreeVisitor visitor;
   14.56 +
   14.57 +        PostAnalyzeTaskListener(JavacTask task, Pattern pattern) {
   14.58 +            visitor = new ShowTypeTreeVisitor(task, pattern);
   14.59 +        }
   14.60 +
   14.61 +        @Override
   14.62 +        public void started(TaskEvent taskEvent) { }
   14.63 +
   14.64 +        @Override
   14.65 +        public void finished(TaskEvent taskEvent) {
   14.66 +            if (taskEvent.getKind().equals(TaskEvent.Kind.ANALYZE)) {
   14.67 +                CompilationUnitTree compilationUnit = taskEvent.getCompilationUnit();
   14.68 +                visitor.scan(compilationUnit, null);
   14.69 +            }
   14.70 +        }
   14.71 +    }
   14.72 +
   14.73 +    private static class ShowTypeTreeVisitor extends TreePathScanner<Void, Void> {
   14.74 +        private final Trees trees;
   14.75 +        private final Pattern pattern;
   14.76 +        private CompilationUnitTree currCompUnit;
   14.77 +
   14.78 +        ShowTypeTreeVisitor(JavacTask task, Pattern pattern) {
   14.79 +            trees = Trees.instance(task);
   14.80 +            this.pattern = pattern;
   14.81 +        }
   14.82 +
   14.83 +        @Override
   14.84 +        public Void visitCompilationUnit(CompilationUnitTree tree, Void ignore) {
   14.85 +            currCompUnit = tree;
   14.86 +            return super.visitCompilationUnit(tree, ignore);
   14.87 +        }
   14.88 +
   14.89 +        @Override
   14.90 +        public Void visitIdentifier(IdentifierTree tree, Void ignore) {
   14.91 +            show(tree, tree.getName());
   14.92 +            return super.visitIdentifier(tree, ignore);
   14.93 +        }
   14.94 +
   14.95 +        @Override
   14.96 +        public Void visitMemberSelect(MemberSelectTree tree, Void ignore) {
   14.97 +            show(tree, tree.getIdentifier());
   14.98 +            return super.visitMemberSelect(tree, ignore);
   14.99 +        }
  14.100 +
  14.101 +        void show(Tree tree, CharSequence name) {
  14.102 +            if (pattern == null || pattern.matcher(name).matches()) {
  14.103 +                TypeMirror type = trees.getTypeMirror(getCurrentPath());
  14.104 +                trees.printMessage(Kind.NOTE, "type is " + type, tree, currCompUnit);
  14.105 +            }
  14.106 +        }
  14.107 +    }
  14.108 +
  14.109 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/test/tools/javac/plugin/showtype/Test.java	Mon Nov 19 11:38:49 2012 -0800
    15.3 @@ -0,0 +1,171 @@
    15.4 +
    15.5 +import java.io.File;
    15.6 +import java.io.FileWriter;
    15.7 +import java.io.IOException;
    15.8 +import java.io.PrintWriter;
    15.9 +import java.io.StringWriter;
   15.10 +import java.nio.charset.Charset;
   15.11 +import java.nio.file.Files;
   15.12 +import java.util.Arrays;
   15.13 +import java.util.List;
   15.14 +import java.util.Locale;
   15.15 +import java.util.Objects;
   15.16 +import javax.tools.JavaCompiler;
   15.17 +import javax.tools.JavaFileManager;
   15.18 +import javax.tools.JavaFileObject;
   15.19 +import javax.tools.StandardJavaFileManager;
   15.20 +import javax.tools.StandardLocation;
   15.21 +import javax.tools.ToolProvider;
   15.22 +
   15.23 +/*
   15.24 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   15.25 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   15.26 + *
   15.27 + * This code is free software; you can redistribute it and/or modify it
   15.28 + * under the terms of the GNU General Public License version 2 only, as
   15.29 + * published by the Free Software Foundation.
   15.30 + *
   15.31 + * This code is distributed in the hope that it will be useful, but WITHOUT
   15.32 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   15.33 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   15.34 + * version 2 for more details (a copy is included in the LICENSE file that
   15.35 + * accompanied this code).
   15.36 + *
   15.37 + * You should have received a copy of the GNU General Public License version
   15.38 + * 2 along with this work; if not, write to the Free Software Foundation,
   15.39 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   15.40 + *
   15.41 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   15.42 + * or visit www.oracle.com if you need additional information or have any
   15.43 + * questions.
   15.44 + */
   15.45 +
   15.46 +/**
   15.47 + *  @test
   15.48 + *  @bug 8001098
   15.49 + *  @summary Provide a simple light-weight "plug-in" mechanism for javac
   15.50 + */
   15.51 +
   15.52 +public class Test {
   15.53 +    public static void main(String... args) throws Exception {
   15.54 +        new Test().run();
   15.55 +    }
   15.56 +
   15.57 +    final File testSrc;
   15.58 +    final File pluginSrc;
   15.59 +    final File pluginClasses ;
   15.60 +    final File pluginJar;
   15.61 +    final List<String> ref1;
   15.62 +    final List<String> ref2;
   15.63 +    final JavaCompiler compiler;
   15.64 +    final StandardJavaFileManager fm;
   15.65 +
   15.66 +    Test() throws Exception {
   15.67 +        testSrc = new File(System.getProperty("test.src"));
   15.68 +        pluginSrc = new File(testSrc, "ShowTypePlugin.java");
   15.69 +        pluginClasses = new File("plugin");
   15.70 +        pluginJar = new File("plugin.jar");
   15.71 +        ref1 = readFile(testSrc, "Identifiers.out");
   15.72 +        ref2 = readFile(testSrc, "Identifiers_PI.out");
   15.73 +        compiler = ToolProvider.getSystemJavaCompiler();
   15.74 +        fm = compiler.getStandardFileManager(null, null, null);
   15.75 +    }
   15.76 +
   15.77 +    void run() throws Exception {
   15.78 +        // compile the plugin explicitly, to a non-standard directory
   15.79 +        // so that we don't find it on the wrong path by accident
   15.80 +        pluginClasses.mkdirs();
   15.81 +        compile("-d", pluginClasses.getPath(), pluginSrc.getPath());
   15.82 +        writeFile(new File(pluginClasses, "META-INF/services/com.sun.source.util.Plugin"),
   15.83 +                "ShowTypePlugin\n");
   15.84 +        jar("cf", pluginJar.getPath(), "-C", pluginClasses.getPath(), ".");
   15.85 +
   15.86 +        testCommandLine("-Xplugin:showtype", ref1);
   15.87 +        testCommandLine("-Xplugin:showtype PI", ref2);
   15.88 +        testAPI("-Xplugin:showtype", ref1);
   15.89 +        testAPI("-Xplugin:showtype PI", ref2);
   15.90 +
   15.91 +        if (errors > 0)
   15.92 +            throw new Exception(errors + " errors occurred");
   15.93 +    }
   15.94 +
   15.95 +    void testAPI(String opt, List<String> ref) throws Exception {
   15.96 +        File identifiers = new File(testSrc, "Identifiers.java");
   15.97 +        fm.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, Arrays.asList(pluginJar));
   15.98 +        fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(".")));
   15.99 +        List<String> options = Arrays.asList(opt);
  15.100 +        Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(identifiers);
  15.101 +
  15.102 +        System.err.println("test api: " + options + " " + files);
  15.103 +
  15.104 +        StringWriter sw = new StringWriter();
  15.105 +        PrintWriter pw = new PrintWriter(sw);
  15.106 +        boolean ok = compiler.getTask(pw, fm, null, options, null, files).call();
  15.107 +        String out = sw.toString();
  15.108 +        System.err.println(out);
  15.109 +        if (!ok)
  15.110 +            error("testCommandLine: compilation failed");
  15.111 +        checkOutput(out, ref);
  15.112 +    }
  15.113 +
  15.114 +    void testCommandLine(String opt, List<String> ref) {
  15.115 +        File identifiers = new File(testSrc, "Identifiers.java");
  15.116 +        String[] args = {
  15.117 +            "-d", ".",
  15.118 +            "-processorpath", pluginJar.getPath(),
  15.119 +            opt,
  15.120 +            identifiers.getPath() };
  15.121 +
  15.122 +        System.err.println("test command line: " + Arrays.asList(args));
  15.123 +
  15.124 +        StringWriter sw = new StringWriter();
  15.125 +        PrintWriter pw = new PrintWriter(sw);
  15.126 +        int rc = com.sun.tools.javac.Main.compile(args, pw);
  15.127 +        String out = sw.toString();
  15.128 +        System.err.println(out);
  15.129 +        if (rc != 0)
  15.130 +            error("testCommandLine: compilation failed");
  15.131 +        checkOutput(out, ref);
  15.132 +    }
  15.133 +
  15.134 +    private void checkOutput(String out, List<String> ref) {
  15.135 +        List<String> lines = Arrays.asList(out
  15.136 +                .replaceAll(".*?([A-Za-z.]+:[0-9]+: .*)", "$1") // remove file directory
  15.137 +                .split("[\r\n]+"));                             // allow for newline formats
  15.138 +        if (!lines.equals(ref)) {
  15.139 +            error("unexpected output");
  15.140 +        }
  15.141 +    }
  15.142 +
  15.143 +    private void compile(String... args) throws Exception {
  15.144 +        System.err.println("compile: " + Arrays.asList(args));
  15.145 +        int rc = com.sun.tools.javac.Main.compile(args);
  15.146 +        if (rc != 0)
  15.147 +            throw new Exception("compiled failed, rc=" + rc);
  15.148 +    }
  15.149 +
  15.150 +    private void jar(String... args) throws Exception {
  15.151 +        System.err.println("jar: " + Arrays.asList(args));
  15.152 +        boolean ok = new sun.tools.jar.Main(System.out, System.err, "jar").run(args);
  15.153 +        if (!ok)
  15.154 +            throw new Exception("jar failed");
  15.155 +    }
  15.156 +
  15.157 +    private List<String> readFile(File dir, String name) throws IOException {
  15.158 +        return Files.readAllLines(new File(dir, name).toPath(), Charset.defaultCharset());
  15.159 +    }
  15.160 +
  15.161 +    private void writeFile(File f, String body) throws IOException {
  15.162 +        f.getParentFile().mkdirs();
  15.163 +        try (FileWriter out = new FileWriter(f)) {
  15.164 +            out.write(body);
  15.165 +        }
  15.166 +    }
  15.167 +
  15.168 +    private void error(String msg) {
  15.169 +        System.err.println(msg);
  15.170 +        errors++;
  15.171 +    }
  15.172 +
  15.173 +    int errors;
  15.174 +}

mercurial