1 /*
   2  * Copyright (c) 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 tests for multi-module mode compilation
  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.classfile
  31  *          jdk.jdeps/com.sun.tools.javap
  32  * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase
  33  * @run main OpenModulesTest
  34  */
  35 
  36 import java.io.OutputStream;
  37 import java.nio.file.Files;
  38 import java.nio.file.Path;
  39 import java.util.Arrays;
  40 import java.util.HashMap;
  41 import java.util.List;
  42 import java.util.Map;
  43 import java.util.Objects;
  44 
  45 import com.sun.tools.classfile.Attribute;
  46 import com.sun.tools.classfile.Attributes;
  47 import com.sun.tools.classfile.ClassFile;
  48 import com.sun.tools.classfile.ClassWriter;
  49 import com.sun.tools.classfile.Module_attribute;
  50 import toolbox.JavacTask;
  51 import toolbox.JavapTask;
  52 import toolbox.Task;
  53 import toolbox.Task.Expect;
  54 import toolbox.Task.OutputKind;
  55 
  56 public class OpenModulesTest extends ModuleTestBase {
  57 
  58     public static void main(String... args) throws Exception {
  59         new OpenModulesTest().runTests();
  60     }
  61 
  62     @Test
  63     public void testStrongModule(Path base) throws Exception {
  64         Path m1 = base.resolve("m1");
  65         tb.writeJavaFiles(m1,
  66                           "module m1 { exports api1; opens api2; }",
  67                           "package api1; public class Api1 {}",
  68                           "package api2; public class Api2 {}",
  69                           "package impl; public class Impl {}");
  70         Path classes = base.resolve("classes");
  71         Path m1Classes = classes.resolve("m1");
  72         tb.createDirectories(m1Classes);
  73 
  74         String log = new JavacTask(tb)
  75                 .outdir(m1Classes)
  76                 .files(findJavaFiles(m1))
  77                 .run()
  78                 .writeAll()
  79                 .getOutput(Task.OutputKind.DIRECT);
  80 
  81         if (!log.isEmpty())
  82             throw new Exception("expected output not found: " + log);
  83 
  84         String decompiled = new JavapTask(tb)
  85                 .options("--system", "none", "-bootclasspath", "")
  86                 .classes(m1Classes.resolve("module-info.class").toString())
  87                 .run()
  88                 .writeAll()
  89                 .getOutput(OutputKind.DIRECT)
  90                 .replace(System.getProperty("line.separator"), "\n");
  91 
  92         String expected = "module m1 {\n" +
  93                           "  requires java.base;\n" +
  94                           "  exports api1;\n" +
  95                           "  opens api2;\n" +
  96                           "}";
  97 
  98         if (!decompiled.contains(expected)) {
  99             throw new Exception("expected output not found: " + decompiled);
 100         }
 101 
 102         //compiling against a strong module read from binary:
 103         Path m2 = base.resolve("m2");
 104         tb.writeJavaFiles(m2,
 105                           "module m2 { requires m1; }",
 106                           "package test; public class Test { api1.Api1 a1; api2.Api2 a2; }");
 107         Path m2Classes = classes.resolve("m2");
 108         tb.createDirectories(m2Classes);
 109 
 110         List<String> log2 = new JavacTask(tb)
 111                 .options("--module-path", m1Classes.toString(),
 112                          "-XDrawDiagnostics")
 113                 .outdir(m2Classes)
 114                 .files(findJavaFiles(m2))
 115                 .run(Expect.FAIL)
 116                 .writeAll()
 117                 .getOutputLines(Task.OutputKind.DIRECT);
 118 
 119         List<String> expected2 = Arrays.asList("Test.java:1:53: compiler.err.doesnt.exist: api2",
 120                                                "1 error");
 121         if (!Objects.equals(log2, expected2))
 122             throw new Exception("expected output not found: " + log2);
 123     }
 124 
 125     @Test
 126     public void testOpenModule(Path base) throws Exception {
 127         Path m1 = base.resolve("m1");
 128         tb.writeJavaFiles(m1,
 129                           "open module m1 { exports api1; }",
 130                           "package api1; public class Api1 {}",
 131                           "package api2; public class Api2 {}",
 132                           "package impl; public class Impl {}");
 133         Path classes = base.resolve("classes");
 134         Path m1Classes = classes.resolve("m1");
 135         tb.createDirectories(m1Classes);
 136 
 137         String log = new JavacTask(tb)
 138                 .outdir(m1Classes)
 139                 .files(findJavaFiles(m1))
 140                 .run()
 141                 .writeAll()
 142                 .getOutput(Task.OutputKind.DIRECT);
 143 
 144         if (!log.isEmpty())
 145             throw new Exception("expected output not found: " + log);
 146 
 147         String decompiled = new JavapTask(tb)
 148                 .options("--system", "none", "-bootclasspath", "")
 149                 .classes(m1Classes.resolve("module-info.class").toString())
 150                 .run()
 151                 .writeAll()
 152                 .getOutput(OutputKind.DIRECT)
 153                 .replace(System.getProperty("line.separator"), "\n");
 154 
 155         String expected = "open module m1 {\n" +
 156                           "  requires java.base;\n" +
 157                           "  exports api1;\n" +
 158                           "}";
 159 
 160         if (!decompiled.contains(expected)) {
 161             throw new Exception("expected output not found: " + decompiled);
 162         }
 163 
 164         //compiling against a ordinary module read from binary:
 165         Path m2 = base.resolve("m2");
 166         tb.writeJavaFiles(m2,
 167                           "module m2 { requires m1; }",
 168                           "package test; public class Test { api1.Api1 a1; api2.Api2 a2; }");
 169         Path m2Classes = classes.resolve("m2");
 170         tb.createDirectories(m2Classes);
 171 
 172         List<String> log2 = new JavacTask(tb)
 173                 .options("--module-path", m1Classes.toString(),
 174                          "-XDrawDiagnostics")
 175                 .outdir(m2Classes)
 176                 .files(findJavaFiles(m2))
 177                 .run(Expect.FAIL)
 178                 .writeAll()
 179                 .getOutputLines(Task.OutputKind.DIRECT);
 180 
 181         List<String> expected2 = Arrays.asList("Test.java:1:53: compiler.err.doesnt.exist: api2",
 182                                                "1 error");
 183         if (!Objects.equals(log2, expected2))
 184             throw new Exception("expected output not found: " + log2);
 185     }
 186 
 187     @Test
 188     public void testOpenModuleNoOpens(Path base) throws Exception {
 189         Path m1 = base.resolve("m1");
 190         tb.writeJavaFiles(m1,
 191                           "open module m1 { exports api1; opens api2; }",
 192                           "package api1; public class Api1 {}",
 193                           "package api2; public class Api2 {}",
 194                           "package impl; public class Impl {}");
 195         Path classes = base.resolve("classes");
 196         Path m1Classes = classes.resolve("m1");
 197         tb.createDirectories(m1Classes);
 198 
 199         List<String> log = new JavacTask(tb)
 200                 .options("-XDrawDiagnostics")
 201                 .outdir(m1Classes)
 202                 .files(findJavaFiles(m1))
 203                 .run(Expect.FAIL)
 204                 .writeAll()
 205                 .getOutputLines(Task.OutputKind.DIRECT);
 206 
 207         List<String> expected = Arrays.asList("module-info.java:1:32: compiler.err.no.opens.unless.strong",
 208                                               "1 error");
 209 
 210         if (!expected.equals(log))
 211             throw new Exception("expected output not found: " + log);
 212 
 213     }
 214 
 215     @Test
 216     public void testNonZeroOpensInOpen(Path base) throws Exception {
 217         Path m1 = base.resolve("m1");
 218         tb.writeJavaFiles(m1,
 219                           "module m1 { opens api; }",
 220                           "package api; public class Api {}");
 221         Path classes = base.resolve("classes");
 222         Path m1Classes = classes.resolve("m1");
 223         tb.createDirectories(m1Classes);
 224 
 225         new JavacTask(tb)
 226             .options("-XDrawDiagnostics")
 227             .outdir(m1Classes)
 228             .files(findJavaFiles(m1))
 229             .run(Expect.SUCCESS)
 230             .writeAll();
 231 
 232         Path miClass = m1Classes.resolve("module-info.class");
 233         ClassFile cf = ClassFile.read(miClass);
 234         Module_attribute module = (Module_attribute) cf.attributes.map.get(Attribute.Module);
 235         Module_attribute newModule = new Module_attribute(module.attribute_name_index,
 236                                                           module.module_name,
 237                                                           module.module_flags | Module_attribute.ACC_OPEN,
 238                                                           module.requires,
 239                                                           module.exports,
 240                                                           module.opens,
 241                                                           module.uses_index,
 242                                                           module.provides);
 243         Map<String, Attribute> attrs = new HashMap<>(cf.attributes.map);
 244 
 245         attrs.put(Attribute.Module, newModule);
 246 
 247         Attributes newAttributes = new Attributes(attrs);
 248         ClassFile newClassFile = new ClassFile(cf.magic,
 249                                                cf.minor_version,
 250                                                cf.major_version,
 251                                                cf.constant_pool,
 252                                                cf.access_flags,
 253                                                cf.this_class,
 254                                                cf.super_class,
 255                                                cf.interfaces,
 256                                                cf.fields,
 257                                                cf.methods,
 258                                                newAttributes);
 259 
 260         try (OutputStream out = Files.newOutputStream(miClass)) {
 261             new ClassWriter().write(newClassFile, out);
 262         }
 263 
 264         Path test = base.resolve("test");
 265         tb.writeJavaFiles(test,
 266                           "package impl; public class Impl extends api.Api {}");
 267         Path testClasses = base.resolve("test-classes");
 268         tb.createDirectories(testClasses);
 269 
 270         List<String> log = new JavacTask(tb)
 271                 .options("-XDrawDiagnostics",
 272                          "--module-path", classes.toString(),
 273                          "--add-modules", "m1")
 274                 .outdir(testClasses)
 275                 .files(findJavaFiles(test))
 276                 .run(Expect.FAIL)
 277                 .writeAll()
 278                 .getOutputLines(Task.OutputKind.DIRECT);
 279 
 280         List<String> expected = Arrays.asList("- compiler.err.cant.access: m1.module-info, (compiler.misc.bad.class.file.header: module-info.class, (compiler.misc.module.non.zero.opens: m1))",
 281                                               "1 error");
 282 
 283         if (!expected.equals(log))
 284             throw new Exception("expected output not found: " + log);
 285     }
 286 
 287 }