OpenJDK / jigsaw / jake / langtools
changeset 3338:3571fc58f820
8049819: specify annotation processors in modules
line wrap: on
line diff
--- a/src/java.compiler/share/classes/javax/tools/JavaFileManager.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/java.compiler/share/classes/javax/tools/JavaFileManager.java Fri Oct 02 11:40:25 2015 -0700 @@ -29,7 +29,9 @@ import java.io.Flushable; import java.io.IOException; import java.util.Iterator; +import java.util.ServiceLoader; import java.util.Set; + import static javax.tools.JavaFileObject.Kind; /** @@ -124,6 +126,16 @@ * @return true if this is an output location, false otherwise */ boolean isOutputLocation(); + + /** + * Indicates if this location is expected to contain modules, + * as compared to a location which contains packages and classes. + * + * @return true if this location is expected to contain modules + */ + default boolean isModuleLocation() { + return false; + } } /** @@ -417,6 +429,16 @@ } /** + * Get a service loader for a specific service class from a given location. + * @param location the location + * @param service the service class + * @return a service loader for the given service class + */ + default <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException { + throw new UnsupportedOperationException(); + } + + /** * Infer the name of the module from its location, as returned by * getModuleLocation or listModuleLocations. * @param location
--- a/src/java.compiler/share/classes/javax/tools/StandardLocation.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/java.compiler/share/classes/javax/tools/StandardLocation.java Fri Oct 02 11:40:25 2015 -0700 @@ -63,6 +63,11 @@ ANNOTATION_PROCESSOR_PATH, /** + * Location to search for modules containing annotation processors. + */ + ANNOTATION_PROCESSOR_MODULE_PATH, + + /** * Location to search for platform classes. Sometimes called * the boot class path. */ @@ -137,4 +142,17 @@ return false; } } + + @Override + public boolean isModuleLocation() { + switch (this) { + case ANNOTATION_PROCESSOR_MODULE_PATH: + case UPGRADE_MODULE_PATH: + case SYSTEM_MODULE_PATH: + case MODULE_PATH: + return true; + default: + return false; + } + } }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/BasicJavacTask.java Fri Oct 02 11:40:25 2015 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,8 +195,7 @@ Set<List<String>> pluginsToCall = new LinkedHashSet<>(pluginOpts); JavacProcessingEnvironment pEnv = JavacProcessingEnvironment.instance(context); - ClassLoader cl = pEnv.getProcessorClassLoader(); - ServiceLoader<Plugin> sl = ServiceLoader.load(Plugin.class, cl); + ServiceLoader<Plugin> sl = pEnv.getServiceLoader(Plugin.class); for (Plugin plugin : sl) { for (List<String> p : pluginsToCall) { if (plugin.getName().equals(p.head)) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java Fri Oct 02 11:40:25 2015 -0700 @@ -50,6 +50,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -68,6 +69,11 @@ import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.ModuleWrappers.Configuration; +import com.sun.tools.javac.util.ModuleWrappers.Layer; +import com.sun.tools.javac.util.ModuleWrappers.ModuleClassLoader; +import com.sun.tools.javac.util.ModuleWrappers.ModuleFinder; +import com.sun.tools.javac.util.ModuleWrappers.ServiceLoaderHelper; import static javax.tools.StandardLocation.*; @@ -940,7 +946,23 @@ nullCheck(location); nullCheck(moduleName); return locations.getModuleLocation(location, moduleName); + } + @Override @DefinedBy(Api.COMPILER) + public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException { + nullCheck(location); + nullCheck(service); + if (location.isModuleLocation()) { + Collection<Path> paths = locations.getLocation(location); + ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()])); + Configuration cf = Configuration.resolve(ModuleFinder.empty(), Layer.boot(), finder); + cf = cf.bind(); + ModuleClassLoader cl = new ModuleClassLoader(cf); + Layer layer = Layer.create(cf, cl); + return ServiceLoaderHelper.load(layer, service); + } else { + return ServiceLoader.load(service, getClassLoader(location)); + } } @Override @DefinedBy(Api.COMPILER)
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java Fri Oct 02 11:40:25 2015 -0700 @@ -1386,6 +1386,7 @@ new ClassPathLocationHandler(), new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCEPATH), new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSORPATH), + new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, Option.PROCESSORMODULEPATH), new OutputLocationHandler(StandardLocation.CLASS_OUTPUT, Option.D), new OutputLocationHandler(StandardLocation.SOURCE_OUTPUT, Option.S), new OutputLocationHandler(StandardLocation.NATIVE_HEADER_OUTPUT, Option.H),
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Arguments.java Fri Oct 02 11:40:25 2015 -0700 @@ -520,6 +520,11 @@ } } + if (fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH) && + fm.hasLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH)) { + log.error("processorpath.no.procesormodulepath"); + } + if (obsoleteOptionFound) log.warning(LintCategory.OPTIONS, "option.obsolete.suppression");
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Fri Oct 02 11:40:25 2015 -0700 @@ -1281,6 +1281,7 @@ return options.isSet(PROCESSOR) || options.isSet(PROCESSORPATH) || + options.isSet(PROCESSORMODULEPATH) || options.isSet(PROC, "only") || options.isSet(XPRINT); }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Fri Oct 02 11:40:25 2015 -0700 @@ -252,6 +252,8 @@ PROCESSORPATH("-processorpath", "opt.arg.path", "opt.processorpath", STANDARD, FILEMANAGER), + PROCESSORMODULEPATH("-processormodulepath", "opt.arg.path", "opt.processormodulepath", STANDARD, FILEMANAGER), + PARAMETERS("-parameters","opt.parameters", STANDARD, BASIC), D("-d", "opt.arg.directory", "opt.d", STANDARD, FILEMANAGER),
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java Fri Oct 02 11:40:25 2015 -0700 @@ -27,6 +27,7 @@ import java.io.Closeable; import java.io.File; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Method; @@ -159,7 +160,10 @@ Source source; private ClassLoader processorClassLoader; - private SecurityException processorClassLoaderException; + private ServiceLoader<Processor> serviceLoader; + private SecurityException processorLoaderException; + + private final JavaFileManager fileManager; /** * JavacMessages object used for localization @@ -202,6 +206,7 @@ fatalErrors = options.isSet("fatalEnterError"); showResolveErrors = options.isSet("showResolveErrors"); werror = options.isSet(WERROR); + fileManager = context.get(JavaFileManager.class); platformAnnotations = initPlatformAnnotations(); // Initialize services before any processors are initialized @@ -222,7 +227,7 @@ initialCompleter = ClassFinder.instance(context).getCompleter(); chk = Check.instance(context); moduleHelper = ModuleHelper.instance(context); - initProcessorClassLoader(); + initProcessorLoader(); defaultModule = source.allowModules() && options.isUnset("noModules") ? symtab.unnamedModule : symtab.noModule; @@ -245,21 +250,28 @@ return Collections.unmodifiableSet(platformAnnotations); } - private void initProcessorClassLoader() { - JavaFileManager fileManager = context.get(JavaFileManager.class); + private void initProcessorLoader() { try { - // If processorpath is not explicitly set, use the classpath. - processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) - ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) - : fileManager.getClassLoader(CLASS_PATH); + if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) { + try { + serviceLoader = fileManager.getServiceLoader(ANNOTATION_PROCESSOR_MODULE_PATH, Processor.class); + } catch (IOException e) { + throw new Abort(e); + } + } else { + // If processorpath is not explicitly set, use the classpath. + processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) + ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) + : fileManager.getClassLoader(CLASS_PATH); - moduleHelper.addExports(processorClassLoader); + moduleHelper.addExports(processorClassLoader); - if (processorClassLoader != null && processorClassLoader instanceof Closeable) { - compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader); + if (processorClassLoader != null && processorClassLoader instanceof Closeable) { + compiler.closeables = compiler.closeables.prepend((Closeable) processorClassLoader); + } } } catch (SecurityException e) { - processorClassLoaderException = e; + processorLoaderException = e; } } @@ -279,14 +291,18 @@ } else if (processors != null) { processorIterator = processors.iterator(); } else { - String processorNames = options.get(PROCESSOR); - if (processorClassLoaderException == null) { + if (processorLoaderException == null) { /* * If the "-processor" option is used, search the appropriate * path for the named class. Otherwise, use a service * provider mechanism to create the processor iterator. */ - if (processorNames != null) { + String processorNames = options.get(PROCESSOR); + if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) { + processorIterator = (processorNames == null) ? + new ServiceIterator(serviceLoader, log) : + new NameServiceIterator(serviceLoader, log, processorNames); + } else if (processorNames != null) { processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log); } else { processorIterator = new ServiceIterator(processorClassLoader, log); @@ -299,7 +315,7 @@ * in service configuration file.) Otherwise, we cannot continue. */ processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", - processorClassLoaderException); + processorLoaderException); } } PlatformDescription platformProvider = context.get(PlatformDescription.class); @@ -317,6 +333,18 @@ discoveredProcs = new DiscoveredProcessors(compoundIterator); } + public <S> ServiceLoader<S> getServiceLoader(Class<S> service) { + if (fileManager.hasLocation(ANNOTATION_PROCESSOR_MODULE_PATH)) { + try { + return fileManager.getServiceLoader(ANNOTATION_PROCESSOR_MODULE_PATH, service); + } catch (IOException e) { + throw new Abort(e); + } + } else { + return ServiceLoader.load(service, getProcessorClassLoader()); + } + } + /** * Returns an empty processor iterator if no processors are on the * relevant path, otherwise if processors are present, logs an @@ -329,8 +357,6 @@ * @param e If non-null, pass this exception to Abort */ private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) { - JavaFileManager fileManager = context.get(JavaFileManager.class); - if (fileManager instanceof JavacFileManager) { StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager; Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) @@ -368,9 +394,9 @@ * needed but unavailable. */ private class ServiceIterator implements Iterator<Processor> { - private Iterator<Processor> iterator; - private Log log; - private ServiceLoader<Processor> loader; + Iterator<Processor> iterator; + Log log; + ServiceLoader<Processor> loader; ServiceIterator(ClassLoader classLoader, Log log) { this.log = log; @@ -388,9 +414,16 @@ } } + ServiceIterator(ServiceLoader<Processor> loader, Log log) { + this.log = log; + this.loader = loader; + this.iterator = loader.iterator(); + } + + @Override public boolean hasNext() { try { - return iterator.hasNext(); + return internalHasNext(); } catch(ServiceConfigurationError sce) { log.error("proc.bad.config.file", sce.getLocalizedMessage()); throw new Abort(sce); @@ -399,9 +432,14 @@ } } + boolean internalHasNext() { + return iterator.hasNext(); + } + + @Override public Processor next() { try { - return iterator.next(); + return internalNext(); } catch (ServiceConfigurationError sce) { log.error("proc.bad.config.file", sce.getLocalizedMessage()); throw new Abort(sce); @@ -410,6 +448,11 @@ } } + Processor internalNext() { + return iterator.next(); + } + + @Override public void remove() { throw new UnsupportedOperationException(); } @@ -425,6 +468,58 @@ } } + private class NameServiceIterator extends ServiceIterator { + private Map<String, Processor> namedProcessorsMap = new HashMap<>();; + private Iterator<String> processorNames = null; + private Processor nextProc = null; + + public NameServiceIterator(ServiceLoader<Processor> loader, Log log, String theNames) { + super(loader, log); + this.processorNames = Arrays.asList(theNames.split(",")).iterator(); + } + + @Override + boolean internalHasNext() { + if (nextProc != null) { + return true; + } + if (!processorNames.hasNext()) { + namedProcessorsMap = null; + return false; + } + String processorName = processorNames.next(); + Processor theProcessor = namedProcessorsMap.get(processorName); + if (theProcessor != null) { + namedProcessorsMap.remove(processorName); + nextProc = theProcessor; + return true; + } else { + while (iterator.hasNext()) { + theProcessor = iterator.next(); + String name = theProcessor.getClass().getName(); + if (name.equals(processorName)) { + nextProc = theProcessor; + return true; + } else { + namedProcessorsMap.put(name, theProcessor); + } + } + log.error("proc.processor.not.found", processorName); + return false; + } + } + + @Override + Processor internalNext() { + if (hasNext()) { + Processor p = nextProc; + nextProc = null; + return p; + } else { + throw new NoSuchElementException(); + } + } + } private static class NameProcessIterator implements Iterator<Processor> { Processor nextProc = null; @@ -640,7 +735,7 @@ // TODO: These two classes can probably be rewritten better... /** * This class holds information about the processors that have - * been discoverd so far as well as the means to discover more, if + * been discovered so far as well as the means to discover more, if * necessary. A single iterator should be used per round of * annotation processing. The iterator first visits already * discovered processors then fails over to the service provider
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Oct 02 11:40:25 2015 -0700 @@ -2725,6 +2725,9 @@ compiler.err.xmodule.no.module.sourcepath=\ illegal combination of -Xmodule and -modulesourcepath +compiler.err.processorpath.no.procesormodulepath=\ + illegal combination of -processorpath and -procesormodulepath + # 0: symbol compiler.warn.package.in.other.module.1=\ package exists in another module: {0}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties Fri Oct 02 11:40:25 2015 -0700 @@ -65,6 +65,8 @@ Override location of installed extensions javac.opt.processorpath=\ Specify where to find annotation processors +javac.opt.processormodulepath=\ + Specify a module path where to find annotation processors javac.opt.processor=\ Names of the annotation processors to run; bypasses default discovery process javac.opt.parameters=\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/ModuleWrappers.java Fri Oct 02 11:40:25 2015 -0700 @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javac.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.nio.file.Path; +import java.security.SecureClassLoader; +import java.util.ServiceLoader; + +/** This class provides wrappers for classes and methods that are new in JDK 9, and which are not + * available on older versions of the platform on which javac may be compiled and run. + * In future releases, when javac is always compiled on JDK 9 or later, the use of these wrappers + * can be replaced by use of the real underlying classes. + */ +public class ModuleWrappers { + public static final class ServiceLoaderHelper { + @SuppressWarnings("unchecked") + public static <S> ServiceLoader<S> load(Layer layer, Class<S> service) { + try { + Class<?> layerClass = LayerHelper.getLayerClass(); + Method loadMethod = ServiceLoader.class + .getDeclaredMethod("load", layerClass, Class.class); + Object result = loadMethod.invoke(ServiceLoader.class, layer.theRealLayer, service); + return (ServiceLoader<S>)result; + } catch (NoSuchMethodException | + SecurityException | + IllegalArgumentException | + IllegalAccessException | + InvocationTargetException ex) { + throw new Abort(ex); + } + } + } + + public static final class ModuleClassLoader extends SecureClassLoader { + Object theRealModuleClassLoader; + + private ModuleClassLoader(Object moduleClassLoader) { + this.theRealModuleClassLoader = moduleClassLoader; + } + + public ModuleClassLoader(Configuration configuration) { + try { + theRealModuleClassLoader = ModuleClassLoaderHelper.getConfigurationCtor().newInstance(configuration.theRealConfiguration); + } catch (InstantiationException | + IllegalAccessException | + IllegalArgumentException | + InvocationTargetException ex) { + throw new Abort(ex); + } + } + } + + private static class ModuleClassLoaderHelper { + static Constructor<?> configurationCtor = null; + static Class<?> moduleClassLoaderClass; + + static Constructor<?> getConfigurationCtor() { + if (moduleClassLoaderClass == null) { + try { + Class<?> configurationClass = ConfigurationHelper.getConfigurationClass(); + configurationCtor = getModuleClassLoaderClass().getDeclaredConstructor(new Class<?>[] { configurationClass }); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return configurationCtor; + } + + static Class<?> getModuleClassLoaderClass() { + if (moduleClassLoaderClass == null) { + try { + moduleClassLoaderClass = Class.forName("java.lang.ModuleClassLoader", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException ex) { + throw new Abort(ex); + } + } + return moduleClassLoaderClass; + } + } + + public static class ModuleFinder { + Object theRealModuleFinder; + + private ModuleFinder(Object moduleFinder) { + this.theRealModuleFinder = moduleFinder; + } + + public static ModuleFinder of(Path... dirs) { + try { + Object result = ModuleFinderHelper.getOfMethod() + .invoke(ModuleFinderHelper.moduleFinderInterface, (Object)dirs); + ModuleFinder mFinder = new ModuleFinder(result); + return mFinder; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + + public static ModuleFinder empty() { + try { + Object result = ModuleFinderHelper.getEmptyMethod() + .invoke(ModuleFinderHelper.moduleFinderInterface); + ModuleFinder mFinder = new ModuleFinder(result); + return mFinder; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + } + + private static class ModuleFinderHelper { + static Method ofMethod = null; + static Method emptyMethod = null; + static Class<?> moduleFinderInterface; + + static Method getOfMethod() { + if (ModuleFinderHelper.ofMethod == null) { + try { + getModuleFinderInterface(); + ofMethod = moduleFinderInterface.getDeclaredMethod("of", Path[].class); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return ofMethod; + } + + static Method getEmptyMethod() { + if (emptyMethod == null) { + try { + getModuleFinderInterface(); + emptyMethod = moduleFinderInterface.getDeclaredMethod("empty"); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return emptyMethod; + } + + static Class<?> getModuleFinderInterface() { + if (moduleFinderInterface == null) { + try { + moduleFinderInterface = Class.forName("java.lang.module.ModuleFinder", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException ex) { + throw new Abort(ex); + } + } + return moduleFinderInterface; + } + } + + public static final class Configuration { + Object theRealConfiguration; + + private Configuration(Object configuration) { + this.theRealConfiguration = configuration; + } + + public static Configuration resolve( + ModuleFinder beforeFinder, + Layer parent, + ModuleFinder afterFinder, + String... roots) { + try { + Object result = ConfigurationHelper.getResolveMethod() + .invoke(ConfigurationHelper.getConfigurationClass(), + beforeFinder.theRealModuleFinder, + parent.theRealLayer, + afterFinder.theRealModuleFinder, + roots + ); + Configuration configuration = new Configuration(result); + return configuration; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + + public Configuration bind() { + try { + Object result = ConfigurationHelper.getBindMethod().invoke(theRealConfiguration); + Configuration configuration = new Configuration(result); + return configuration; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + } + + private static class ConfigurationHelper { + static Method resolveMethod = null; + static Method bindMethod = null; + static Class<?> configurationClass; + + static Method getResolveMethod() { + if (ConfigurationHelper.resolveMethod == null) { + try { + getConfigurationClass(); + Class<?> moduleFinderInterface = ModuleFinderHelper.getModuleFinderInterface(); + Class<?> layerClass = LayerHelper.getLayerClass(); + resolveMethod = configurationClass.getDeclaredMethod("resolve", + moduleFinderInterface, + layerClass, + moduleFinderInterface, + String[].class + ); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return resolveMethod; + } + + static Method getBindMethod() { + if (bindMethod == null) { + try { + bindMethod = getConfigurationClass().getDeclaredMethod("bind"); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return bindMethod; + } + + static Class<?> getConfigurationClass() { + if (configurationClass == null) { + try { + configurationClass = Class.forName("java.lang.module.Configuration", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException ex) { + throw new Abort(ex); + } + } + return configurationClass; + } + } + + public static final class Layer { + Object theRealLayer; + + private Layer(Object layer) { + this.theRealLayer = layer; + } + + public static Layer boot() { + try { + Object result = LayerHelper.getBootMethod().invoke(LayerHelper.getLayerClass()); + Layer layer = new Layer(result); + return layer; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + + public static Layer create(Configuration configuration, ModuleClassLoader modClassLoader) { + try { + Class<?> classLoaderFinderInterface = LayerHelper.getClassLoaderFinderInterface(); + LayerHelper.ClassLoaderFinderInvocationHandler handler = + new LayerHelper.ClassLoaderFinderInvocationHandler(modClassLoader); + Object proxy = Proxy.newProxyInstance( + ClassLoader.getSystemClassLoader(), + new Class<?>[] { classLoaderFinderInterface }, + handler); + Object result = LayerHelper.getCreateMethod() + .invoke(LayerHelper.getLayerClass(), configuration.theRealConfiguration, proxy); + Layer layer = new Layer(result); + return layer; + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + throw new Abort(ex); + } + } + } + + private static class LayerHelper { + static Class<?> layerClass; + static Class<?> classLoaderFinderInterface; + static Method bootMethod = null; + static Method createMethod = null; + + static Class<?> getLayerClass() { + if (layerClass == null) { + try { + layerClass = Class.forName("java.lang.reflect.Layer", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException ex) { + throw new Abort(ex); + } + } + return layerClass; + } + + static class ClassLoaderFinderInvocationHandler implements InvocationHandler { + ModuleClassLoader moduleClassLoader; + + public ClassLoaderFinderInvocationHandler(ModuleClassLoader moduleClassLoader) { + this.moduleClassLoader = moduleClassLoader; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return moduleClassLoader.theRealModuleClassLoader; + } + } + + static Class<?> getClassLoaderFinderInterface() { + if (classLoaderFinderInterface == null) { + try { + classLoaderFinderInterface = Class.forName("java.lang.reflect.Layer$ClassLoaderFinder", false, ClassLoader.getSystemClassLoader()); + } catch (ClassNotFoundException ex) { + throw new Abort(ex); + } + } + return classLoaderFinderInterface; + } + + static Method getBootMethod() { + if (bootMethod == null) { + try { + bootMethod = getLayerClass().getDeclaredMethod("boot"); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return bootMethod; + } + + static Method getCreateMethod() { + if (createMethod == null) { + try { + createMethod = getLayerClass().getDeclaredMethod("create", + ConfigurationHelper.getConfigurationClass(), + getClassLoaderFinderInterface() + ); + } catch (NoSuchMethodException | SecurityException ex) { + throw new Abort(ex); + } + } + return createMethod; + } + } +}
--- a/src/jdk.compiler/share/classes/module-info.java Thu Oct 01 16:55:39 2015 +0100 +++ b/src/jdk.compiler/share/classes/module-info.java Fri Oct 02 11:40:25 2015 -0700 @@ -59,7 +59,9 @@ uses javax.annotation.processing.Processor; uses com.sun.source.util.Plugin; uses com.sun.tools.javac.platform.PlatformProvider; - provides com.sun.tools.javac.platform.PlatformProvider with com.sun.tools.javac.platform.JDKPlatformProvider; + + provides com.sun.tools.javac.platform.PlatformProvider + with com.sun.tools.javac.platform.JDKPlatformProvider; provides javax.tools.JavaCompiler with com.sun.tools.javac.api.JavacTool;
--- a/test/tools/javac/api/TestClientCodeWrapper.java Thu Oct 01 16:55:39 2015 +0100 +++ b/test/tools/javac/api/TestClientCodeWrapper.java Fri Oct 02 11:40:25 2015 -0700 @@ -39,9 +39,10 @@ import javax.lang.model.*; import javax.lang.model.element.*; import javax.tools.*; +import javax.tools.JavaFileObject.Kind; + import com.sun.source.util.*; import com.sun.tools.javac.api.*; -import javax.tools.JavaFileObject.Kind; public class TestClientCodeWrapper extends JavacTestingAbstractProcessor { public static void main(String... args) throws Exception { @@ -60,7 +61,8 @@ try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { defaultFileManager = fm; - for (Method m: getMethodsExcept(JavaFileManager.class, "close", "getJavaFileForInput", "getModuleLocation")) { + for (Method m: getMethodsExcept(JavaFileManager.class, + "close", "getJavaFileForInput", "getModuleLocation", "getServiceLoader")) { test(m); } @@ -321,6 +323,12 @@ } @Override + public <S> ServiceLoader getServiceLoader(Location location, Class<S> service) throws IOException { + throwUserExceptionIfNeeded(fileManagerMethod, "getServiceLoader"); + return super.getServiceLoader(location, service); + } + + @Override public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { throwUserExceptionIfNeeded(fileManagerMethod, "list"); return wrap(super.list(location, packageName, kinds, recurse)); @@ -416,7 +424,6 @@ return super.listModuleLocations(location); } - public FileObject wrap(FileObject fo) { if (fileObjectMethod == null || fo == null) return fo;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/modules/AnnotationProcessorsInModulesTest.java Fri Oct 02 11:40:25 2015 -0700 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Verify that annotation processors inside modules works + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build ToolBox ModuleTestBase + * @run main AnnotationProcessorsInModulesTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; + +import com.sun.source.util.JavacTask; + +public class AnnotationProcessorsInModulesTest extends ModuleTestBase { + + public static void main(String... args) throws Exception { + new AnnotationProcessorsInModulesTest().runTests(); + } + + private static final String annotationProcessorModule1 = + "module anno_proc1 {\n" + + " requires java.compiler;\n" + + "\n" + + " provides javax.annotation.processing.Processor\n" + + " with mypkg1.MyProcessor1;\n" + + "}"; + + private static final String annotationProcessorModule2 = + "module anno_proc2 {\n" + + " requires java.compiler;\n" + + "\n" + + " provides javax.annotation.processing.Processor\n" + + " with mypkg2.MyProcessor2;\n" + + "}"; + + private static final String annotationProcessor1 = + "package mypkg1;\n" + + "\n" + + "import javax.annotation.processing.AbstractProcessor;\n" + + "import javax.annotation.processing.RoundEnvironment;\n" + + "import javax.annotation.processing.SupportedAnnotationTypes;\n" + + "import javax.lang.model.SourceVersion;\n" + + "import javax.lang.model.element.*;\n" + + "\n" + + "import java.util.*;\n" + + "\n" + + "@SupportedAnnotationTypes(\"*\")\n" + + "public final class MyProcessor1 extends AbstractProcessor {\n" + + "\n" + + " @Override\n" + + " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " @Override\n" + + " public SourceVersion getSupportedSourceVersion() {\n" + + " System.out.println(\"the annotation processor 1 is working!\");\n" + + " return SourceVersion.latest();\n" + + " }\n" + + "}"; + + private static final String annotationProcessor2 = + "package mypkg2;\n" + + "\n" + + "import javax.annotation.processing.AbstractProcessor;\n" + + "import javax.annotation.processing.RoundEnvironment;\n" + + "import javax.annotation.processing.SupportedAnnotationTypes;\n" + + "import javax.lang.model.SourceVersion;\n" + + "import javax.lang.model.element.*;\n" + + "\n" + + "import java.util.*;\n" + + "\n" + + "@SupportedAnnotationTypes(\"*\")\n" + + "public final class MyProcessor2 extends AbstractProcessor {\n" + + "\n" + + " @Override\n" + + " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " @Override\n" + + " public SourceVersion getSupportedSourceVersion() {\n" + + " System.out.println(\"the annotation processor 2 is working!\");\n" + + " return SourceVersion.latest();\n" + + " }\n" + + "}"; + + private static final String testClass = "class Test{}"; + + void initialization(Path base) throws Exception { + moduleSrc = base.resolve("anno_proc_src"); + Path anno_proc1 = moduleSrc.resolve("anno_proc1"); + Path anno_proc2 = moduleSrc.resolve("anno_proc2"); + + processorCompiledModules = base.resolve("mods"); + + Files.createDirectories(processorCompiledModules); + + tb.writeJavaFiles( + anno_proc1, + annotationProcessorModule1, + annotationProcessor1); + + tb.writeJavaFiles( + anno_proc2, + annotationProcessorModule2, + annotationProcessor2); + + String log = tb.new JavacTask() + .options("-modulesourcepath", moduleSrc.toString()) + .outdir(processorCompiledModules) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.DIRECT); + + if (!log.isEmpty()) { + throw new AssertionError("Unexpected output: " + log); + } + + classes = base.resolve("classes"); + Files.createDirectories(classes); + } + + Path processorCompiledModules; + Path moduleSrc; + Path classes; + + @Test + void testUseOnlyOneProcessor(Path base) throws Exception { + initialization(base); + String log = tb.new JavacTask() + .options("-processormodulepath", processorCompiledModules.toString(), + "-processor", "mypkg2.MyProcessor2") + .outdir(classes) + .sources(testClass) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.STDOUT); + if (!log.trim().equals("the annotation processor 2 is working!")) { + throw new AssertionError("Unexpected output: " + log); + } + } + + @Test + void testAnnotationProcessorExecutionOrder(Path base) throws Exception { + initialization(base); + String log = tb.new JavacTask() + .options("-processormodulepath", processorCompiledModules.toString(), + "-processor", "mypkg1.MyProcessor1,mypkg2.MyProcessor2") + .outdir(classes) + .sources(testClass) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.STDOUT); + if (!log.trim().equals("the annotation processor 1 is working!\n" + + "the annotation processor 2 is working!")) { + throw new AssertionError("Unexpected output: " + log); + } + + log = tb.new JavacTask() + .options("-processormodulepath", processorCompiledModules.toString(), + "-processor", "mypkg2.MyProcessor2,mypkg1.MyProcessor1") + .outdir(classes) + .sources(testClass) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.STDOUT); + if (!log.trim().equals("the annotation processor 2 is working!\n" + + "the annotation processor 1 is working!")) { + throw new AssertionError("Unexpected output: " + log); + } + } + + @Test + void testErrorOutputIfOneProcessorNameIsIncorrect(Path base) throws Exception { + initialization(base); + String log = tb.new JavacTask() + .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(), + "-processor", "mypkg2.MyProcessor2,noPackage.noProcessor,mypkg1.MyProcessor1") + .outdir(classes) + .sources(testClass) + .run(ToolBox.Expect.FAIL) + .writeAll() + .getOutputLines(ToolBox.OutputKind.STDOUT, ToolBox.OutputKind.DIRECT).toString(); + if (!log.trim().equals("[the annotation processor 2 is working!, - compiler.err.proc.processor.not.found: noPackage.noProcessor, 1 error]")) { + throw new AssertionError("Unexpected output: " + log); + } + } + + @Test + void testOptionsExclusion(Path base) throws Exception { + initialization(base); + String log = tb.new JavacTask() + .options("-XDrawDiagnostics", "-processormodulepath", processorCompiledModules.toString(), + "-processorpath", processorCompiledModules.toString()) + .outdir(classes) + .sources(testClass) + .run(ToolBox.Expect.FAIL) + .writeAll() + .getOutput(ToolBox.OutputKind.DIRECT); + if (!log.trim().equals("- compiler.err.processorpath.no.procesormodulepath\n" + + "1 error")) { + throw new AssertionError("Unexpected output: " + log); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/modules/PluginsInModulesTest.java Fri Oct 02 11:40:25 2015 -0700 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @summary Verify that plugins inside modules works + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build ToolBox ModuleTestBase + * @run main PluginsInModulesTest + */ + +import java.nio.file.Files; +import java.nio.file.Path; + +import com.sun.source.util.JavacTask; + +public class PluginsInModulesTest extends ModuleTestBase { + + public static void main(String... args) throws Exception { + new PluginsInModulesTest().runTests(); + } + + private static final String pluginModule1 = + "module pluginMod1 {\n" + + " requires jdk.compiler;\n" + + "\n" + + " provides com.sun.source.util.Plugin\n" + + " with mypkg1.SimplePlugin1;\n" + + "}"; + + private static final String plugin1 = + "package mypkg1;\n" + + "import com.sun.source.util.JavacTask;\n" + + "import com.sun.source.util.Plugin;\n" + + "import com.sun.source.util.TaskEvent;\n" + + "import com.sun.source.util.TaskListener;\n" + + "\n" + + "public class SimplePlugin1 implements Plugin {\n" + + "\n" + + " @Override\n" + + " public String getName() {\n" + + " return \"simpleplugin1\";\n" + + " }\n" + + "\n" + + " @Override\n" + + " public void init(JavacTask task, String... args) {\n" + + " task.addTaskListener(new PostAnalyzeTaskListener());\n" + + " }\n" + + "\n" + + " private static class PostAnalyzeTaskListener implements TaskListener {\n" + + " @Override\n" + + " public void started(TaskEvent taskEvent) { \n" + + " if (taskEvent.getKind().equals(TaskEvent.Kind.COMPILATION)) {\n" + + " System.out.println(\"simpleplugin1 started for event \" + taskEvent.getKind());\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + + " public void finished(TaskEvent taskEvent) {\n" + + " if (taskEvent.getKind().equals(TaskEvent.Kind.COMPILATION)) {\n" + + " System.out.println(\"simpleplugin1 finished for event \" + taskEvent.getKind());\n" + + " }\n" + + " }\n" + + " }\n" + + "}"; + + private static final String testClass = "class Test{}"; + + void initialization(Path base) throws Exception { + moduleSrc = base.resolve("plugin_mods_src"); + Path pluginMod1 = moduleSrc.resolve("pluginMod1"); + + processorCompiledModules = base.resolve("mods"); + + Files.createDirectories(processorCompiledModules); + + tb.writeJavaFiles( + pluginMod1, + pluginModule1, + plugin1); + + String log = tb.new JavacTask() + .options("-modulesourcepath", moduleSrc.toString()) + .outdir(processorCompiledModules) + .files(findJavaFiles(moduleSrc)) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.DIRECT); + + if (!log.isEmpty()) { + throw new AssertionError("Unexpected output: " + log); + } + + classes = base.resolve("classes"); + Files.createDirectories(classes); + } + + Path processorCompiledModules; + Path moduleSrc; + Path classes; + + @Test + void testUseOnlyOneProcessor(Path base) throws Exception { + initialization(base); + String log = tb.new JavacTask() + .options("-processormodulepath", processorCompiledModules.toString(), + "-Xplugin:simpleplugin1") + .outdir(classes) + .sources(testClass) + .run() + .writeAll() + .getOutput(ToolBox.OutputKind.STDOUT); + if (!log.trim().equals("simpleplugin1 started for event COMPILATION\n" + + "simpleplugin1 finished for event COMPILATION")) { + throw new AssertionError("Unexpected output: " + log); + } + } +}
--- a/test/tools/javac/plugin/showtype/Test.java Thu Oct 01 16:55:39 2015 +0100 +++ b/test/tools/javac/plugin/showtype/Test.java Fri Oct 02 11:40:25 2015 -0700 @@ -34,23 +34,17 @@ */ import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.nio.charset.Charset; -import java.nio.file.Files; import java.util.Arrays; import java.util.List; -import java.util.Locale; -import java.util.Objects; + import javax.tools.JavaCompiler; -import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; +import com.sun.source.util.JavacTask; + public class Test { public static void main(String... args) throws Exception { new Test().run();
--- a/test/tools/lib/ToolBox.java Thu Oct 01 16:55:39 2015 +0100 +++ b/test/tools/lib/ToolBox.java Fri Oct 02 11:40:25 2015 -0700 @@ -535,13 +535,17 @@ } /** - * Returns the content of a named stream as a list of lines. - * @param outputKind the kind of the selected stream - * @return the content that was written to that stream when the tool + * Returns the content of named streams as a list of lines. + * @param outputKinds the kinds of the selected streams + * @return the content that was written to the given streams when the tool * was executed. */ - public List<String> getOutputLines(OutputKind outputKind) { - return Arrays.asList(outputMap.get(outputKind).split(lineSeparator)); + public List<String> getOutputLines(OutputKind... outputKinds) { + List<String> result = new ArrayList<>(); + for (OutputKind outputKind : outputKinds) { + result.addAll(Arrays.asList(outputMap.get(outputKind).split(lineSeparator))); + } + return result; } /**