1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @summary Test the -XaddReads option
  27  * @library /tools/lib
  28  * @modules jdk.compiler/com.sun.tools.javac.api
  29  *          jdk.compiler/com.sun.tools.javac.main
  30  *          jdk.jdeps/com.sun.tools.javap
  31  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavapTask ModuleTestBase
  32  * @run main AddReadsTest
  33  */
  34 
  35 import java.nio.file.Files;
  36 import java.nio.file.Path;
  37 import java.util.Set;
  38 
  39 import javax.annotation.processing.AbstractProcessor;
  40 import javax.annotation.processing.RoundEnvironment;
  41 import javax.annotation.processing.SupportedAnnotationTypes;
  42 import javax.lang.model.SourceVersion;
  43 import javax.lang.model.element.ModuleElement;
  44 import javax.lang.model.element.ModuleElement.RequiresDirective;
  45 import javax.lang.model.element.TypeElement;
  46 import javax.lang.model.util.ElementFilter;
  47 
  48 import toolbox.JarTask;
  49 import toolbox.JavacTask;
  50 import toolbox.JavapTask;
  51 import toolbox.Task;
  52 import toolbox.ToolBox;
  53 
  54 public class AddReadsTest extends ModuleTestBase {
  55 
  56     public static void main(String... args) throws Exception {
  57         new AddReadsTest().runTests();
  58     }
  59 
  60     @Test
  61     public void testAddReads(Path base) throws Exception {
  62         Path src = base.resolve("src");
  63         Path src_m1 = src.resolve("m1");
  64         tb.writeJavaFiles(src_m1,
  65                           "module m1 { exports api; }",
  66                           "package api; public class Api { }");
  67         Path src_m2 = src.resolve("m2");
  68         tb.writeJavaFiles(src_m2,
  69                           "module m2 { }",
  70                           "package test; public class Test extends api.Api { }");
  71         Path classes = base.resolve("classes");
  72         tb.createDirectories(classes);
  73 
  74         String log = new JavacTask(tb)
  75                 .options("-XDrawDiagnostics",
  76                          "-modulesourcepath", src.toString())
  77                 .outdir(classes)
  78                 .files(findJavaFiles(src))
  79                 .run(Task.Expect.FAIL)
  80                 .writeAll()
  81                 .getOutput(Task.OutputKind.DIRECT);
  82 
  83         if (!log.contains("Test.java:1:44: compiler.err.not.def.access.package.cant.access: api.Api, api"))
  84             throw new Exception("expected output not found");
  85 
  86         //test add dependencies:
  87         new JavacTask(tb)
  88                 .options("-XaddReads:m2=m1",
  89                          "-modulesourcepath", src.toString(),
  90                          "-processor", VerifyRequires.class.getName())
  91                 .outdir(classes)
  92                 .files(findJavaFiles(src))
  93                 .run()
  94                 .writeAll();
  95 
  96         String decompiled = new JavapTask(tb)
  97                 .options("-verbose", classes.resolve("m2").resolve("module-info.class").toString())
  98                 .run()
  99                 .getOutput(Task.OutputKind.DIRECT);
 100 
 101         if (decompiled.contains("m1")) {
 102             throw new Exception("Incorrectly refers to m1 module.");
 103         }
 104 
 105         //cyclic dependencies OK when created through addReads:
 106         new JavacTask(tb)
 107                 .options("-XaddReads:m2=m1,m1=m2",
 108                          "-modulesourcepath", src.toString())
 109                 .outdir(classes)
 110                 .files(findJavaFiles(src))
 111                 .run()
 112                 .writeAll();
 113 
 114         tb.writeJavaFiles(src_m2,
 115                           "module m2 { requires m1; }");
 116 
 117         new JavacTask(tb)
 118                 .options("-XaddReads:m1=m2",
 119                          "-modulesourcepath", src.toString())
 120                 .outdir(classes)
 121                 .files(findJavaFiles(src))
 122                 .run()
 123                 .writeAll();
 124     }
 125 
 126     @SupportedAnnotationTypes("*")
 127     public static final class VerifyRequires extends AbstractProcessor {
 128 
 129         @Override
 130         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 131             ModuleElement m2Module = processingEnv.getElementUtils().getModuleElement("m2");
 132             if (m2Module == null) {
 133                 throw new AssertionError("Cannot find the m2 module!");
 134             }
 135             boolean foundM1 = false;
 136             for (RequiresDirective rd : ElementFilter.requiresIn(m2Module.getDirectives())) {
 137                 foundM1 |= rd.getDependency().getSimpleName().contentEquals("m1");
 138             }
 139             if (!foundM1) {
 140                 throw new AssertionError("Cannot find the dependency on m1 module!");
 141             }
 142             return false;
 143         }
 144 
 145         @Override
 146         public SourceVersion getSupportedSourceVersion() {
 147             return SourceVersion.latest();
 148         }
 149 
 150     }
 151 
 152     @Test
 153     public void testAddReadsUnnamedModule(Path base) throws Exception {
 154         Path jar = prepareTestJar(base);
 155 
 156         Path moduleSrc = base.resolve("module-src");
 157         Path m1 = moduleSrc.resolve("m1");
 158 
 159         Path classes = base.resolve("classes");
 160 
 161         Files.createDirectories(classes);
 162 
 163         tb.writeJavaFiles(m1,
 164                           "module m1 { }",
 165                           "package impl; public class Impl { api.Api api; }");
 166 
 167         new JavacTask(tb)
 168           .options("-classpath", jar.toString(),
 169                    "-XaddReads:m1=ALL-UNNAMED",
 170                    "-XDrawDiagnostics")
 171           .outdir(classes)
 172           .files(findJavaFiles(moduleSrc))
 173           .run()
 174           .writeAll();
 175     }
 176 
 177     @Test
 178     public void testAddReadsUnnamedModulePackageConflict(Path base) throws Exception {
 179         Path jar = prepareTestJar(base);
 180 
 181         Path moduleSrc = base.resolve("module-src");
 182         Path m1 = moduleSrc.resolve("m1");
 183 
 184         Path classes = base.resolve("classes");
 185 
 186         Files.createDirectories(classes);
 187 
 188         tb.writeJavaFiles(m1,
 189                           "module m1 { }",
 190                           "package api; public class Api { public static void test() { } }",
 191                           "package impl; public class Impl { { api.Api.test(); } }");
 192 
 193         new JavacTask(tb)
 194           .options("-classpath", jar.toString(),
 195                    "-modulesourcepath", moduleSrc.toString(),
 196                    "-XaddReads:m1=ALL-UNNAMED",
 197                    "-XDrawDiagnostics")
 198           .outdir(classes)
 199           .files(m1.resolve("impl").resolve("Impl.java"))
 200           .run()
 201           .writeAll();
 202     }
 203 
 204     @Test
 205     public void testAddReadsUnnamedToJavaBase(Path base) throws Exception {
 206         Path jar = prepareTestJar(base);
 207         Path src = base.resolve("src");
 208         Path classes = base.resolve("classes");
 209 
 210         Files.createDirectories(classes);
 211 
 212         tb.writeJavaFiles(src,
 213                           "package impl; public class Impl { api.Api a; }");
 214 
 215         new JavacTask(tb)
 216           .options("-classpath", jar.toString(),
 217                    "-XaddReads:java.base=ALL-UNNAMED",
 218                    "-Xmodule:java.base")
 219           .outdir(classes)
 220           .files(src.resolve("impl").resolve("Impl.java"))
 221           .run()
 222           .writeAll();
 223     }
 224 
 225     @Test
 226     public void testAddReadsToJavaBase(Path base) throws Exception {
 227         Path src = base.resolve("src");
 228         Path classes = base.resolve("classes");
 229 
 230         Files.createDirectories(classes);
 231 
 232         tb.writeJavaFiles(src,
 233                           "package impl; public class Impl { javax.swing.JButton b; }");
 234 
 235         new JavacTask(tb)
 236           .options("-XaddReads:java.base=java.desktop",
 237                    "-Xmodule:java.base")
 238           .outdir(classes)
 239           .files(findJavaFiles(src))
 240           .run()
 241           .writeAll();
 242     }
 243 
 244     private Path prepareTestJar(Path base) throws Exception {
 245         Path legacySrc = base.resolve("legacy-src");
 246         tb.writeJavaFiles(legacySrc,
 247                           "package api; public abstract class Api {}");
 248         Path legacyClasses = base.resolve("legacy-classes");
 249         Files.createDirectories(legacyClasses);
 250 
 251         String log = new JavacTask(tb)
 252                 .options()
 253                 .outdir(legacyClasses)
 254                 .files(findJavaFiles(legacySrc))
 255                 .run()
 256                 .writeAll()
 257                 .getOutput(Task.OutputKind.DIRECT);
 258 
 259         if (!log.isEmpty()) {
 260             throw new Exception("unexpected output: " + log);
 261         }
 262 
 263         Path lib = base.resolve("lib");
 264 
 265         Files.createDirectories(lib);
 266 
 267         Path jar = lib.resolve("test-api-1.0.jar");
 268 
 269         new JarTask(tb, jar)
 270           .baseDir(legacyClasses)
 271           .files("api/Api.class")
 272           .run();
 273 
 274         return jar;
 275     }
 276 
 277     @Test
 278     public void testX(Path base) throws Exception {
 279         Path src = base.resolve("src");
 280         Path src_m1 = src.resolve("m1");
 281         tb.writeJavaFiles(src_m1,
 282                           "module m1 { provides java.lang.Runnable with impl.Impl; }",
 283                           "package impl; public class Impl implements Runnable { public void run() { } }");
 284         Path classes = base.resolve("classes");
 285         tb.createDirectories(classes);
 286 
 287         new JavacTask(tb)
 288                 .options("-modulesourcepath", src.toString())
 289                 .outdir(classes)
 290                 .files(findJavaFiles(src))
 291                 .run()
 292                 .writeAll();
 293 
 294         Path unnamedSrc = base.resolve("unnamed-src");
 295         Path unnamedClasses = base.resolve("unnamed-classes");
 296 
 297         Files.createDirectories(unnamedClasses);
 298 
 299         tb.writeJavaFiles(unnamedSrc,
 300                           "package impl; public class Impl { }");
 301 
 302         new JavacTask(tb)
 303           .options("-XaddReads:m1=ALL-UNNAMED",
 304                    "-Xmodule:m1",
 305                    "-modulepath", classes.toString())
 306           .outdir(unnamedClasses)
 307           .files(findJavaFiles(unnamedSrc))
 308           .run()
 309           .writeAll();
 310     }
 311 }