1 /*
   2  * Copyright (c) 2018, 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 different configurations of sealed classes, same compilation unit, diff pkg or mdl, etc
  27  * @library /tools/lib
  28  * @modules jdk.compiler/com.sun.tools.javac.api
  29  *          jdk.compiler/com.sun.tools.javac.main
  30  *          jdk.compiler/com.sun.tools.javac.util
  31  *          jdk.compiler/com.sun.tools.javac.code
  32  *          jdk.jdeps/com.sun.tools.classfile
  33  * @build toolbox.ToolBox toolbox.JavacTask
  34  * @run main SealedDiffConfigurationsTest
  35  */
  36 
  37 import java.util.*;
  38 
  39 import java.io.IOException;
  40 import java.nio.file.Files;
  41 import java.nio.file.Path;
  42 import java.nio.file.Paths;
  43 import java.util.stream.IntStream;
  44 
  45 import com.sun.tools.classfile.*;
  46 import com.sun.tools.javac.code.Flags;
  47 import com.sun.tools.javac.util.Assert;
  48 import toolbox.TestRunner;
  49 import toolbox.ToolBox;
  50 import toolbox.JavacTask;
  51 import toolbox.Task;
  52 import toolbox.Task.OutputKind;
  53 
  54 import static com.sun.tools.classfile.ConstantPool.*;
  55 
  56 public class SealedDiffConfigurationsTest extends TestRunner {
  57     ToolBox tb;
  58 
  59     SealedDiffConfigurationsTest() {
  60         super(System.err);
  61         tb = new ToolBox();
  62     }
  63 
  64     protected void runTests() throws Exception {
  65         runTests(m -> new Object[] { Paths.get(m.getName()) });
  66     }
  67 
  68     public static void main(String... args) throws Exception {
  69         SealedDiffConfigurationsTest t = new SealedDiffConfigurationsTest();
  70         t.runTests();
  71     }
  72 
  73     Path[] findJavaFiles(Path... paths) throws IOException {
  74         return tb.findJavaFiles(paths);
  75     }
  76 
  77     @Test
  78     public void testSameCompilationUnitPos(Path base) throws Exception {
  79         Path src = base.resolve("src");
  80         Path test = src.resolve("Test");
  81 
  82         tb.writeJavaFiles(test,
  83                           "class Test {\n" +
  84                            "    sealed class Sealed permits Sub1, Sub2 {}\n" +
  85                            "    class Sub1 extends Sealed {}\n" +
  86                            "    class Sub2 extends Sealed {}\n" +
  87                            "}");
  88 
  89         Path out = base.resolve("out");
  90 
  91         Files.createDirectories(out);
  92 
  93         new JavacTask(tb)
  94                 .outdir(out)
  95                 .files(findJavaFiles(test))
  96                 .run()
  97                 .writeAll();
  98 
  99         checkSealedClassFile(out, "Test$Sealed.class", List.of("Test$Sub1", "Test$Sub2"));
 100         checkSubtypeClassFile(out, "Test$Sub1.class", "Test$Sealed");
 101         checkSubtypeClassFile(out, "Test$Sub2.class", "Test$Sealed");
 102     }
 103 
 104     @Test
 105     public void testSameCompilationUnitPos2(Path base) throws Exception {
 106         Path src = base.resolve("src");
 107         Path test = src.resolve("Test");
 108 
 109         tb.writeJavaFiles(test,
 110                 "class Test {\n" +
 111                         "    sealed class Sealed {}\n" +
 112                         "    class Sub1 extends Sealed {}\n" +
 113                         "    class Sub2 extends Sealed {}\n" +
 114                         "}");
 115 
 116         Path out = base.resolve("out");
 117 
 118         Files.createDirectories(out);
 119 
 120         new JavacTask(tb)
 121                 .outdir(out)
 122                 .files(findJavaFiles(test))
 123                 .run()
 124                 .writeAll();
 125 
 126         checkSealedClassFile(out, "Test$Sealed.class", List.of("Test$Sub1", "Test$Sub2"));
 127         checkSubtypeClassFile(out, "Test$Sub1.class", "Test$Sealed");
 128         checkSubtypeClassFile(out, "Test$Sub2.class", "Test$Sealed");
 129     }
 130 
 131     private void checkSealedClassFile(Path out, String cfName, List<String> expectedSubTypeNames) throws ConstantPoolException, Exception {
 132         ClassFile sealedCF = ClassFile.read(out.resolve(cfName));
 133         Assert.check((sealedCF.access_flags.flags & Flags.FINAL) != 0, String.format("class at file %s must be final", cfName));
 134         PermittedSubtypes_attribute permittedSubtypes = (PermittedSubtypes_attribute)sealedCF.attributes.get("PermittedSubtypes");
 135         Assert.check(permittedSubtypes.subtypes.length == expectedSubTypeNames.size());
 136         List<String> subtypeNames = new ArrayList<>();
 137         IntStream.of(permittedSubtypes.subtypes).forEach(i -> {
 138             try {
 139                 subtypeNames.add(((CONSTANT_Class_info)sealedCF.constant_pool.get(i)).getName());
 140             } catch (ConstantPoolException ex) {
 141             }
 142         });
 143         subtypeNames.sort((s1, s2) -> s1.compareTo(s2));
 144         for (int i = 0; i < expectedSubTypeNames.size(); i++) {
 145             Assert.check(expectedSubTypeNames.get(0).equals(subtypeNames.get(0)));
 146         }
 147     }
 148 
 149     private void checkSubtypeClassFile(Path out, String cfName, String superClassName) throws Exception {
 150         ClassFile subCF1 = ClassFile.read(out.resolve(cfName));
 151         Assert.check((subCF1.access_flags.flags & Flags.FINAL) != 0, String.format("class at file %s must be final", cfName));
 152         Assert.checkNull((PermittedSubtypes_attribute)subCF1.attributes.get("PermittedSubtypes"));
 153         Assert.check(((CONSTANT_Class_info)subCF1.constant_pool.get(subCF1.super_class)).getName().equals(superClassName));
 154     }
 155 
 156     @Test
 157     public void testSamePackagePos(Path base) throws Exception {
 158         Path src = base.resolve("src");
 159         Path pkg = src.resolve("pkg");
 160         Path sealed = pkg.resolve("Sealed");
 161         Path sub1 = pkg.resolve("Sub1");
 162         Path sub2 = pkg.resolve("Sub2");
 163 
 164         tb.writeJavaFiles(sealed,
 165                           "package pkg;\n" +
 166                           "\n" +
 167                           "sealed class Sealed permits Sub1, Sub2 {\n" +
 168                           "}");
 169         tb.writeJavaFiles(sub1,
 170                           "package pkg;\n" +
 171                           "\n" +
 172                           "class Sub1 extends Sealed {\n" +
 173                           "}");
 174         tb.writeJavaFiles(sub2,
 175                           "package pkg;\n" +
 176                           "\n" +
 177                           "class Sub2 extends Sealed {\n" +
 178                           "}");
 179 
 180         Path out = base.resolve("out");
 181 
 182         Files.createDirectories(out);
 183 
 184         new JavacTask(tb)
 185                 .outdir(out)
 186                 .files(findJavaFiles(pkg))
 187                 .run()
 188                 .writeAll();
 189 
 190         checkSealedClassFile(out.resolve("pkg"), "Sealed.class", List.of("pkg/Sub1", "pkg/Sub1"));
 191         checkSubtypeClassFile(out.resolve("pkg"), "Sub1.class", "pkg/Sealed");
 192         checkSubtypeClassFile(out.resolve("pkg"), "Sub2.class", "pkg/Sealed");
 193     }
 194 
 195     @Test
 196     public void testDiffPackagePos(Path base) throws Exception {
 197         Path src = base.resolve("src");
 198         Path pkg1 = src.resolve("pkg1");
 199         Path pkg2 = src.resolve("pkg2");
 200         Path sealed = pkg1.resolve("Sealed");
 201         Path sub1 = pkg2.resolve("Sub1");
 202         Path sub2 = pkg2.resolve("Sub2");
 203 
 204         tb.writeJavaFiles(sealed,
 205                           "package pkg1;\n" +
 206                           "import pkg2.*;\n" +
 207                           "public sealed class Sealed permits pkg2.Sub1, pkg2.Sub2 {\n" +
 208                           "}");
 209         tb.writeJavaFiles(sub1,
 210                           "package pkg2;\n" +
 211                           "import pkg1.*;\n" +
 212                           "public class Sub1 extends pkg1.Sealed {\n" +
 213                           "}");
 214         tb.writeJavaFiles(sub2,
 215                           "package pkg2;\n" +
 216                           "import pkg1.*;\n" +
 217                           "public class Sub2 extends pkg1.Sealed {\n" +
 218                           "}");
 219 
 220         Path out = base.resolve("out");
 221 
 222         Files.createDirectories(out);
 223 
 224         new JavacTask(tb)
 225                 .outdir(out)
 226                 .files(findJavaFiles(pkg1, pkg2))
 227                 .run()
 228                 .writeAll();
 229 
 230         checkSealedClassFile(out.resolve("pkg1"), "Sealed.class", List.of("pkg2/Sub1", "pkg2/Sub1"));
 231         checkSubtypeClassFile(out.resolve("pkg2"), "Sub1.class", "pkg1/Sealed");
 232         checkSubtypeClassFile(out.resolve("pkg2"), "Sub2.class", "pkg1/Sealed");
 233     }
 234 
 235     @Test
 236     public void testSameCompilationUnitNeg(Path base) throws Exception {
 237         Path src = base.resolve("src");
 238         Path test = src.resolve("Test");
 239 
 240         tb.writeJavaFiles(test,
 241                           "class Test {\n" +
 242                            "    sealed class Sealed permits Sub1 {}\n" +
 243                            "    class Sub1 extends Sealed {}\n" +
 244                            "    class Sub2 extends Sealed {}\n" +
 245                            "}");
 246 
 247         List<String> error = new JavacTask(tb)
 248                 .options("-XDrawDiagnostics")
 249                 .files(findJavaFiles(test))
 250                 .run(Task.Expect.FAIL)
 251                 .writeAll()
 252                 .getOutputLines(OutputKind.DIRECT);
 253 
 254         List<String> expected = List.of(
 255                 "Test.java:4:24: compiler.err.cant.inherit.from.sealed: Test.Sealed",
 256                 "1 error");
 257         if (!error.containsAll(expected)) {
 258             throw new AssertionError("Expected output not found. Expected: " + expected);
 259         }
 260     }
 261 
 262     @Test
 263     public void testSamePackageNeg(Path base) throws Exception {
 264         Path src = base.resolve("src");
 265         Path pkg = src.resolve("pkg");
 266         Path sealed = pkg.resolve("Sealed");
 267         Path sub1 = pkg.resolve("Sub1");
 268         Path sub2 = pkg.resolve("Sub2");
 269 
 270         tb.writeJavaFiles(sealed,
 271                           "package pkg;\n" +
 272                           "\n" +
 273                           "sealed class Sealed permits Sub1 {\n" +
 274                           "}");
 275         tb.writeJavaFiles(sub1,
 276                           "package pkg;\n" +
 277                           "\n" +
 278                           "class Sub1 extends Sealed {\n" +
 279                           "}");
 280         tb.writeJavaFiles(sub2,
 281                           "package pkg;\n" +
 282                           "\n" +
 283                           "class Sub2 extends Sealed {\n" +
 284                           "}");
 285 
 286         List<String> error = new JavacTask(tb)
 287                 .options("-XDrawDiagnostics")
 288                 .files(findJavaFiles(pkg))
 289                 .run(Task.Expect.FAIL)
 290                 .writeAll()
 291                 .getOutputLines(OutputKind.DIRECT);
 292 
 293         List<String> expected = List.of(
 294                 "Sub2.java:3:20: compiler.err.cant.inherit.from.sealed: pkg.Sealed",
 295                 "1 error");
 296         if (!error.containsAll(expected)) {
 297             throw new AssertionError("Expected output not found. Expected: " + expected);
 298         }
 299     }
 300 
 301     @Test
 302     public void testSamePackageNeg2(Path base) throws Exception {
 303         Path src = base.resolve("src");
 304         Path pkg = src.resolve("pkg");
 305         Path sealed = pkg.resolve("Sealed");
 306         Path sub1 = pkg.resolve("Sub1");
 307 
 308         tb.writeJavaFiles(sealed,
 309                 "package pkg;\n" +
 310                         "\n" +
 311                         "final class Sealed {\n" +
 312                         "}");
 313         tb.writeJavaFiles(sub1,
 314                 "package pkg;\n" +
 315                         "\n" +
 316                         "class Sub1 extends Sealed {\n" +
 317                         "}");
 318 
 319         List<String> error = new JavacTask(tb)
 320                 .options("-XDrawDiagnostics")
 321                 .files(findJavaFiles(pkg))
 322                 .run(Task.Expect.FAIL)
 323                 .writeAll()
 324                 .getOutputLines(OutputKind.DIRECT);
 325 
 326         List<String> expected = List.of(
 327                 "Sub1.java:3:20: compiler.err.cant.inherit.from.final: pkg.Sealed",
 328                 "1 error");
 329         if (!error.containsAll(expected)) {
 330             throw new AssertionError("Expected output not found. Expected: " + expected);
 331         }
 332     }
 333 }