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                          "-Werror")
 194                 .outdir(testClasses)
 195                 .files(findJavaFiles(testSrc))
 196                 .run(Expect.FAIL)
 197                 .writeAll()
 198                 .getOutputLines(Task.OutputKind.DIRECT);
 199 
 200         expected = Arrays.asList(
 201                 "- compiler.warn.incubating.modules: jdk.i",
 202                 "- compiler.err.warnings.and.werror",
 203                 "1 error",
 204                 "1 warning"
 205         );
 206 
 207         if (!expected.equals(log)) {
 208             throw new AssertionError("Unexpected output: " + log);
 209         }
 210 
 211         Path testModuleSrc = base.resolve("test-module-src");
 212         tb.writeJavaFiles(testModuleSrc,
 213                           "module test { requires jdk.i; }", //explicit requires of an incubating module
 214                           "class T { api.Api api; }");
 215         Path testModuleClasses = base.resolve("test-module-classes");
 216         tb.createDirectories(testModuleClasses);
 217 
 218         log = new JavacTask(tb)
 219                 .options("--module-path", classes.toString(),
 220                          "-XDrawDiagnostics",
 221                          "-Werror")
 222                 .outdir(testModuleClasses)
 223                 .files(findJavaFiles(testModuleSrc))
 224                 .run(Expect.FAIL)
 225                 .writeAll()
 226                 .getOutputLines(Task.OutputKind.DIRECT);
 227 
 228         expected = Arrays.asList(
 229                 "- compiler.warn.incubating.modules: jdk.i",
 230                 "- compiler.err.warnings.and.werror",
 231                 "1 error",
 232                 "1 warning"
 233         );
 234 
 235         if (!expected.equals(log)) {
 236             throw new AssertionError("Unexpected output: " + log);
 237         }
 238     }
 239 
 240     private void copyJavaBase(Path targetDir) throws IOException {
 241         FileSystem jrt = FileSystems.getFileSystem(URI.create("jrt:/"));
 242         Path javaBase = jrt.getPath("modules", "java.base");
 243 
 244         if (!Files.exists(javaBase)) {
 245             throw new AssertionError("No java.base?");
 246         }
 247 
 248         Path javaBaseClasses = targetDir.resolve("java.base");
 249 
 250         for (Path clazz : tb.findFiles("class", javaBase)) {
 251             Path target = javaBaseClasses.resolve(javaBase.relativize(clazz).toString());
 252             Files.createDirectories(target.getParent());
 253             Files.copy(clazz, target);
 254         }
 255     }
 256 
 257     private void addModuleResolutionAttribute(Path classfile, int resolution_flags) throws Exception {
 258         ClassFile cf = ClassFile.read(classfile);
 259         Attributes attrs = cf.attributes;
 260         List<CPInfo> cpData = new ArrayList<>();
 261         cpData.add(null);
 262         for (CPInfo info : cf.constant_pool.entries()) {
 263             cpData.add(info);
 264             if (info.size() == 2)
 265                 cpData.add(null);
 266         }
 267         cpData.add(new CONSTANT_Utf8_info(Attribute.ModuleResolution));
 268         ConstantPool newCP = new ConstantPool(cpData.toArray(new CPInfo[0]));
 269         ModuleResolution_attribute res = new ModuleResolution_attribute(newCP, resolution_flags);
 270         Map<String, Attribute> newAttributeMap = new HashMap<>(attrs.map);
 271         newAttributeMap.put(Attribute.ModuleResolution, res);
 272         Attributes newAttrs = new Attributes(newAttributeMap);
 273         ClassFile newCF = new ClassFile(cf.magic,
 274                                         cf.minor_version,
 275                                         cf.major_version,
 276                                         newCP,
 277                                         cf.access_flags,
 278                                         cf.this_class,
 279                                         cf.super_class,
 280                                         cf.interfaces,
 281                                         cf.fields,
 282                                         cf.methods,
 283                                         newAttrs);
 284         try (OutputStream out = Files.newOutputStream(classfile)) {
 285             new ClassWriter().write(newCF, out);
 286         }
 287     }
 288 }