1 /*
   2  * Copyright (c) 2015, 2017, 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  * @bug 8171177
  27  * @summary Verify that ModuleResolution attribute flags are honored.
  28  * @library /tools/lib
  29  * @modules jdk.compiler/com.sun.tools.javac.api
  30  *          jdk.compiler/com.sun.tools.javac.main
  31  *          jdk.jdeps/com.sun.tools.classfile
  32  *          jdk.jdeps/com.sun.tools.javap
  33  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavapTask ModuleTestBase
  34  * @run main IncubatingTest
  35  */
  36 
  37 import java.io.IOException;
  38 import java.io.OutputStream;
  39 import java.net.URI;
  40 import java.nio.file.FileSystem;
  41 import java.nio.file.FileSystems;
  42 import java.nio.file.Files;
  43 import java.nio.file.Path;
  44 import java.util.ArrayList;
  45 import java.util.Arrays;
  46 import java.util.HashMap;
  47 import java.util.List;
  48 import java.util.Map;
  49 
  50 import com.sun.tools.classfile.Attribute;
  51 import com.sun.tools.classfile.Attributes;
  52 import com.sun.tools.classfile.ClassFile;
  53 import com.sun.tools.classfile.ClassWriter;
  54 import com.sun.tools.classfile.ConstantPool;
  55 import com.sun.tools.classfile.ConstantPool.CONSTANT_Utf8_info;
  56 import com.sun.tools.classfile.ConstantPool.CPInfo;
  57 import com.sun.tools.classfile.ModuleResolution_attribute;
  58 import toolbox.JavacTask;
  59 import toolbox.Task;
  60 import toolbox.Task.Expect;
  61 
  62 public class IncubatingTest extends ModuleTestBase {
  63 
  64     public static void main(String... args) throws Exception {
  65         new IncubatingTest().runTests();
  66     }
  67 
  68     @Test
  69     public void testDoNotResolve(Path base) throws Exception {
  70         Path src = base.resolve("src");
  71         tb.writeJavaFiles(src,
  72                           "module jdk.i { exports api; }",
  73                           "package api; public class Api { }");
  74         Path classes = base.resolve("classes");
  75         Files.deleteIfExists(classes);
  76         Path iClasses = classes.resolve("jdk.i");
  77         tb.createDirectories(iClasses);
  78 
  79         new JavacTask(tb)
  80                 .outdir(iClasses)
  81                 .files(findJavaFiles(src))
  82                 .run()
  83                 .writeAll();
  84 
  85         copyJavaBase(classes);
  86 
  87         Path jdkIModuleInfo = iClasses.resolve("module-info.class");
  88         addModuleResolutionAttribute(jdkIModuleInfo, ModuleResolution_attribute.DO_NOT_RESOLVE_BY_DEFAULT);
  89 
  90         Path testSrc = base.resolve("test-src");
  91         tb.writeJavaFiles(testSrc,
  92                           "class T { api.Api api; }");
  93         Path testClasses = base.resolve("test-classes");
  94         tb.createDirectories(testClasses);
  95 
  96         List<String> log;
  97         List<String> expected;
  98 
  99         log = new JavacTask(tb)
 100                 .options("--system", "none",
 101                          "--upgrade-module-path", classes.toString(),
 102                          "-XDrawDiagnostics")
 103                 .outdir(testClasses)
 104                 .files(findJavaFiles(testSrc))
 105                 .run(Expect.FAIL)
 106                 .writeAll()
 107                 .getOutputLines(Task.OutputKind.DIRECT);
 108 
 109         expected = Arrays.asList(
 110                 "T.java:1:11: compiler.err.package.not.visible: api, (compiler.misc.not.def.access.does.not.read.from.unnamed: api, jdk.i)",
 111                 "1 error"
 112         );
 113 
 114         if (!expected.equals(log)) {
 115             throw new AssertionError("Unexpected output: " + log);
 116         }
 117 
 118         log = new JavacTask(tb)
 119                 .options("--system", "none",
 120                          "--upgrade-module-path", classes.toString(),
 121                          "--add-modules", "ALL-SYSTEM",
 122                          "-XDrawDiagnostics")
 123                 .outdir(testClasses)
 124                 .files(findJavaFiles(testSrc))
 125                 .run(Expect.SUCCESS)
 126                 .writeAll()
 127                 .getOutputLines(Task.OutputKind.DIRECT);
 128 
 129         expected = Arrays.asList("");
 130 
 131         if (!expected.equals(log)) {
 132             throw new AssertionError("Unexpected output: " + log);
 133         }
 134 
 135         new JavacTask(tb)
 136                 .options("--system", "none",
 137                          "--upgrade-module-path", classes.toString(),
 138                          "--add-modules", "jdk.i")
 139                 .outdir(testClasses)
 140                 .files(findJavaFiles(testSrc))
 141                 .run()
 142                 .writeAll();
 143 
 144         Path testModuleSrc = base.resolve("test-module-src");
 145         tb.writeJavaFiles(testModuleSrc,
 146                           "module test { requires jdk.i; }", //explicit requires of an incubating module
 147                           "class T { api.Api api; }");
 148         Path testModuleClasses = base.resolve("test-module-classes");
 149         tb.createDirectories(testModuleClasses);
 150 
 151         new JavacTask(tb)
 152                 .options("--system", "none",
 153                          "--upgrade-module-path", classes.toString())
 154                 .outdir(testModuleClasses)
 155                 .files(findJavaFiles(testModuleSrc))
 156                 .run()
 157                 .writeAll();
 158     }
 159 
 160     @Test
 161     public void testIncubating(Path base) throws Exception {
 162         Path src = base.resolve("src");
 163         tb.writeJavaFiles(src,
 164                           "module jdk.i { exports api; }",
 165                           "package api; public class Api { }");
 166         Path classes = base.resolve("classes");
 167         Files.deleteIfExists(classes);
 168         Path iClasses = classes.resolve("jdk.i");
 169         tb.createDirectories(iClasses);
 170 
 171         new JavacTask(tb)
 172                 .outdir(iClasses)
 173                 .files(findJavaFiles(src))
 174                 .run()
 175                 .writeAll();
 176 
 177         Path jdkIModuleInfo = iClasses.resolve("module-info.class");
 178         addModuleResolutionAttribute(jdkIModuleInfo, ModuleResolution_attribute.WARN_INCUBATING);
 179 
 180         Path testSrc = base.resolve("test-src");
 181         tb.writeJavaFiles(testSrc,
 182                           "class T { api.Api api; }");
 183         Path testClasses = base.resolve("test-classes");
 184         tb.createDirectories(testClasses);
 185 
 186         List<String> log;
 187         List<String> expected;
 188 
 189         log = new JavacTask(tb)
 190                 .options("--module-path", classes.toString(),
 191                          "--add-modules", "jdk.i",
 192                          "-XDrawDiagnostics")
 193                 .outdir(testClasses)
 194                 .files(findJavaFiles(testSrc))
 195                 .run(Expect.SUCCESS)
 196                 .writeAll()
 197                 .getOutputLines(Task.OutputKind.DIRECT);
 198 
 199         expected = Arrays.asList(
 200                 "- compiler.note.incubating.modules: jdk.i"
 201         );
 202 
 203         if (!expected.equals(log)) {
 204             throw new AssertionError("Unexpected output: " + log);
 205         }
 206 
 207         Path testModuleSrc = base.resolve("test-module-src");
 208         tb.writeJavaFiles(testModuleSrc,
 209                           "module test { requires jdk.i; }", //explicit requires of an incubating module
 210                           "class T { api.Api api; }");
 211         Path testModuleClasses = base.resolve("test-module-classes");
 212         tb.createDirectories(testModuleClasses);
 213 
 214         log = new JavacTask(tb)
 215                 .options("--module-path", classes.toString(),
 216                          "-XDrawDiagnostics")
 217                 .outdir(testModuleClasses)
 218                 .files(findJavaFiles(testModuleSrc))
 219                 .run(Expect.SUCCESS)
 220                 .writeAll()
 221                 .getOutputLines(Task.OutputKind.DIRECT);
 222 
 223         expected = Arrays.asList(
 224                 "- compiler.note.incubating.modules: jdk.i"
 225         );
 226 
 227         if (!expected.equals(log)) {
 228             throw new AssertionError("Unexpected output: " + log);
 229         }
 230     }
 231 
 232     private void copyJavaBase(Path targetDir) throws IOException {
 233         FileSystem jrt = FileSystems.getFileSystem(URI.create("jrt:/"));
 234         Path javaBase = jrt.getPath("modules", "java.base");
 235 
 236         if (!Files.exists(javaBase)) {
 237             throw new AssertionError("No java.base?");
 238         }
 239 
 240         Path javaBaseClasses = targetDir.resolve("java.base");
 241 
 242         for (Path clazz : tb.findFiles("class", javaBase)) {
 243             Path target = javaBaseClasses.resolve(javaBase.relativize(clazz).toString());
 244             Files.createDirectories(target.getParent());
 245             Files.copy(clazz, target);
 246         }
 247     }
 248 
 249     private void addModuleResolutionAttribute(Path classfile, int resolution_flags) throws Exception {
 250         ClassFile cf = ClassFile.read(classfile);
 251         Attributes attrs = cf.attributes;
 252         List<CPInfo> cpData = new ArrayList<>();
 253         cpData.add(null);
 254         for (CPInfo info : cf.constant_pool.entries()) {
 255             cpData.add(info);
 256             if (info.size() == 2)
 257                 cpData.add(null);
 258         }
 259         cpData.add(new CONSTANT_Utf8_info(Attribute.ModuleResolution));
 260         ConstantPool newCP = new ConstantPool(cpData.toArray(new CPInfo[0]));
 261         ModuleResolution_attribute res = new ModuleResolution_attribute(newCP, resolution_flags);
 262         Map<String, Attribute> newAttributeMap = new HashMap<>(attrs.map);
 263         newAttributeMap.put(Attribute.ModuleResolution, res);
 264         Attributes newAttrs = new Attributes(newAttributeMap);
 265         ClassFile newCF = new ClassFile(cf.magic,
 266                                         cf.minor_version,
 267                                         cf.major_version,
 268                                         newCP,
 269                                         cf.access_flags,
 270                                         cf.this_class,
 271                                         cf.super_class,
 272                                         cf.interfaces,
 273                                         cf.fields,
 274                                         cf.methods,
 275                                         newAttrs);
 276         try (OutputStream out = Files.newOutputStream(classfile)) {
 277             new ClassWriter().write(newCF, out);
 278         }
 279     }
 280 }