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 }