cfdc64fcb4
Reviewed-by: liach, redestad, vromero
732 lines
27 KiB
Java
732 lines
27 KiB
Java
/*
|
|
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test 8247352 8293348
|
|
* @summary test different configurations of sealed classes, same compilation unit, diff pkg or mdl, etc
|
|
* @library /tools/lib
|
|
* @enablePreview
|
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
|
* jdk.compiler/com.sun.tools.javac.main
|
|
* jdk.compiler/com.sun.tools.javac.util
|
|
* jdk.compiler/com.sun.tools.javac.code
|
|
* java.base/jdk.internal.classfile.impl
|
|
* @build toolbox.ToolBox toolbox.JavacTask
|
|
* @run main SealedDiffConfigurationsTest
|
|
*/
|
|
|
|
import java.util.*;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.stream.IntStream;
|
|
|
|
import java.lang.classfile.*;
|
|
import java.lang.classfile.attribute.PermittedSubclassesAttribute;
|
|
import java.lang.classfile.constantpool.ClassEntry;
|
|
import java.lang.classfile.constantpool.ConstantPoolException;
|
|
import com.sun.tools.javac.code.Flags;
|
|
import com.sun.tools.javac.util.Assert;
|
|
import toolbox.TestRunner;
|
|
import toolbox.ToolBox;
|
|
import toolbox.JavacTask;
|
|
import toolbox.Task;
|
|
import toolbox.Task.OutputKind;
|
|
|
|
public class SealedDiffConfigurationsTest extends TestRunner {
|
|
ToolBox tb;
|
|
|
|
SealedDiffConfigurationsTest() {
|
|
super(System.err);
|
|
tb = new ToolBox();
|
|
}
|
|
|
|
protected void runTests() throws Exception {
|
|
runTests(m -> new Object[] { Paths.get(m.getName()) });
|
|
}
|
|
|
|
public static void main(String... args) throws Exception {
|
|
SealedDiffConfigurationsTest t = new SealedDiffConfigurationsTest();
|
|
t.runTests();
|
|
}
|
|
|
|
Path[] findJavaFiles(Path... paths) throws IOException {
|
|
return tb.findJavaFiles(paths);
|
|
}
|
|
|
|
@Test
|
|
public void testSameCompilationUnitPos(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path test = src.resolve("Test");
|
|
|
|
tb.writeJavaFiles(test,
|
|
"class Test {\n" +
|
|
" sealed class Sealed permits Sub1, Sub2 {}\n" +
|
|
" final class Sub1 extends Sealed {}\n" +
|
|
" final class Sub2 extends Sealed {}\n" +
|
|
"}");
|
|
|
|
Path out = base.resolve("out");
|
|
|
|
Files.createDirectories(out);
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.files(findJavaFiles(test))
|
|
.run()
|
|
.writeAll();
|
|
|
|
checkSealedClassFile(out, "Test$Sealed.class", List.of("Test$Sub1", "Test$Sub2"));
|
|
checkSubtypeClassFile(out, "Test$Sub1.class", "Test$Sealed", true);
|
|
checkSubtypeClassFile(out, "Test$Sub2.class", "Test$Sealed", true);
|
|
}
|
|
|
|
@Test
|
|
public void testSameCompilationUnitPos2(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path test = src.resolve("Test");
|
|
|
|
tb.writeJavaFiles(test,
|
|
"class Test {\n" +
|
|
" sealed class Sealed {}\n" +
|
|
" final class Sub1 extends Sealed {}\n" +
|
|
" final class Sub2 extends Sealed {}\n" +
|
|
"}");
|
|
|
|
Path out = base.resolve("out");
|
|
|
|
Files.createDirectories(out);
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.files(findJavaFiles(test))
|
|
.run()
|
|
.writeAll();
|
|
|
|
checkSealedClassFile(out, "Test$Sealed.class", List.of("Test$Sub1", "Test$Sub2"));
|
|
checkSubtypeClassFile(out, "Test$Sub1.class", "Test$Sealed", true);
|
|
checkSubtypeClassFile(out, "Test$Sub2.class", "Test$Sealed", true);
|
|
}
|
|
|
|
private void checkSealedClassFile(Path out, String cfName, List<String> expectedSubTypeNames) throws ConstantPoolException, Exception {
|
|
ClassModel sealedCF = ClassFile.of().parse(out.resolve(cfName));
|
|
Assert.check((sealedCF.flags().flagsMask() & ClassFile.ACC_FINAL) == 0, String.format("class at file %s must not be final", cfName));
|
|
PermittedSubclassesAttribute permittedSubclasses = sealedCF.findAttribute(Attributes.permittedSubclasses()).orElseThrow();
|
|
Assert.check(permittedSubclasses.permittedSubclasses().size() == expectedSubTypeNames.size());
|
|
List<String> subtypeNames = new ArrayList<>();
|
|
permittedSubclasses.permittedSubclasses().forEach(i -> {
|
|
try {
|
|
subtypeNames.add(i.name().stringValue());
|
|
} catch (ConstantPoolException ex) {
|
|
}
|
|
});
|
|
for (int i = 0; i < expectedSubTypeNames.size(); i++) {
|
|
Assert.check(expectedSubTypeNames.get(0).equals(subtypeNames.get(0)));
|
|
}
|
|
}
|
|
|
|
private void checkSubtypeClassFile(Path out, String cfName, String superClassName, boolean shouldBeFinal) throws Exception {
|
|
ClassModel subCF1 = ClassFile.of().parse(out.resolve(cfName));
|
|
if (shouldBeFinal) {
|
|
Assert.check((subCF1.flags().flagsMask() & ClassFile.ACC_FINAL) != 0, String.format("class at file %s must be final", cfName));
|
|
}
|
|
Assert.checkNull(subCF1.findAttribute(Attributes.permittedSubclasses()).orElse(null));
|
|
Assert.check(subCF1.superclass().orElseThrow().name().equalsString(superClassName));
|
|
}
|
|
|
|
@Test
|
|
public void testSamePackagePos(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg = src.resolve("pkg");
|
|
Path sealed = pkg.resolve("Sealed");
|
|
Path sub1 = pkg.resolve("Sub1");
|
|
Path sub2 = pkg.resolve("Sub2");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"sealed class Sealed permits Sub1, Sub2 {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"final class Sub1 extends Sealed {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub2,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"final class Sub2 extends Sealed {\n" +
|
|
"}");
|
|
|
|
Path out = base.resolve("out");
|
|
|
|
Files.createDirectories(out);
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.files(findJavaFiles(pkg))
|
|
.run()
|
|
.writeAll();
|
|
|
|
checkSealedClassFile(out.resolve("pkg"), "Sealed.class", List.of("pkg/Sub1", "pkg/Sub1"));
|
|
checkSubtypeClassFile(out.resolve("pkg"), "Sub1.class", "pkg/Sealed", true);
|
|
checkSubtypeClassFile(out.resolve("pkg"), "Sub2.class", "pkg/Sealed", true);
|
|
}
|
|
|
|
@Test
|
|
public void testSameCompilationUnitNeg(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path test = src.resolve("Test");
|
|
|
|
tb.writeJavaFiles(test,
|
|
"class Test {\n" +
|
|
" sealed class Sealed permits Sub1 {}\n" +
|
|
" final class Sub1 extends Sealed {}\n" +
|
|
" class Sub2 extends Sealed {}\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(test))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Test.java:4:5: compiler.err.cant.inherit.from.sealed: Test.Sealed",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSameCompilationUnitNeg2(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path test = src.resolve("Test");
|
|
|
|
tb.writeJavaFiles(test,
|
|
"class Test {\n" +
|
|
" sealed class Sealed permits Sub1 {}\n" +
|
|
" class Sub1 extends Sealed {}\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(test))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Test.java:3:5: compiler.err.non.sealed.sealed.or.final.expected",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSamePackageNeg(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg = src.resolve("pkg");
|
|
Path sealed = pkg.resolve("Sealed");
|
|
Path sub1 = pkg.resolve("Sub1");
|
|
Path sub2 = pkg.resolve("Sub2");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"sealed class Sealed permits Sub1 {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"final class Sub1 extends Sealed {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub2,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"class Sub2 extends Sealed {\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sub2.java:3:1: compiler.err.cant.inherit.from.sealed: pkg.Sealed",
|
|
"1 error"
|
|
);
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSamePackageNeg2(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg = src.resolve("pkg");
|
|
Path sealed = pkg.resolve("Sealed");
|
|
Path sub1 = pkg.resolve("Sub1");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"final class Sealed {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"class Sub1 extends Sealed {\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sub1.java:3:20: compiler.err.cant.inherit.from.final: pkg.Sealed",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSamePackageNeg3(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg = src.resolve("pkg");
|
|
Path sealed = pkg.resolve("Sealed");
|
|
Path sub1 = pkg.resolve("Sub1");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"sealed class Sealed permits Sub1{\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg;\n" +
|
|
"\n" +
|
|
"class Sub1 extends Sealed {\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sub1.java:3:1: compiler.err.non.sealed.sealed.or.final.expected",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDiffPackageNeg(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg1 = src.resolve("pkg1");
|
|
Path pkg2 = src.resolve("pkg2");
|
|
Path sealed = pkg1.resolve("Sealed");
|
|
Path sub1 = pkg2.resolve("Sub1");
|
|
Path sub2 = pkg2.resolve("Sub2");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg1;\n" +
|
|
"import pkg2.*;\n" +
|
|
"public sealed class Sealed permits pkg2.Sub1 {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg2;\n" +
|
|
"import pkg1.*;\n" +
|
|
"public final class Sub1 extends pkg1.Sealed {\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg1, pkg2))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sealed.java:3:40: compiler.err.class.in.unnamed.module.cant.extend.sealed.in.diff.package: pkg1.Sealed",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDiffPackageNeg2(Path base) throws Exception {
|
|
// test that the compiler rejects a subtype that is not accessible to the sealed class
|
|
Path src = base.resolve("src");
|
|
Path pkg1 = src.resolve("pkg1");
|
|
Path pkg2 = src.resolve("pkg2");
|
|
Path sealed = pkg1.resolve("Sealed");
|
|
Path sub1 = pkg2.resolve("Sub1");
|
|
Path sub2 = pkg2.resolve("Sub2");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg1;\n" +
|
|
"import pkg2.*;\n" +
|
|
"public sealed class Sealed permits pkg2.Sub1 {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg2;\n" +
|
|
"import pkg1.*;\n" +
|
|
"final class Sub1 extends pkg1.Sealed {\n" +
|
|
"}");
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg1, pkg2))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sealed.java:3:40: compiler.err.not.def.public.cant.access: pkg2.Sub1, pkg2",
|
|
"Sub1.java:3:7: compiler.err.cant.inherit.from.sealed: pkg1.Sealed",
|
|
"2 errors");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Expected: " + expected);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDiffPackageNeg3(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path pkg1 = src.resolve("pkg1");
|
|
Path pkg2 = src.resolve("pkg2");
|
|
Path sealed = pkg1.resolve("Sealed");
|
|
Path sub1 = pkg2.resolve("Sub1");
|
|
Path sub2 = pkg2.resolve("Sub2");
|
|
|
|
tb.writeJavaFiles(sealed,
|
|
"package pkg1;\n" +
|
|
"import pkg2.*;\n" +
|
|
"public sealed class Sealed permits pkg2.Sub1 {\n" +
|
|
"}");
|
|
tb.writeJavaFiles(sub1,
|
|
"package pkg2;\n" +
|
|
"import pkg1.*;\n" +
|
|
"public final class Sub1 extends pkg1.Sealed {\n" +
|
|
"}");
|
|
|
|
Path out = base.resolve("out");
|
|
|
|
Files.createDirectories(out);
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics")
|
|
.files(findJavaFiles(pkg1, pkg2))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sealed.java:3:40: compiler.err.class.in.unnamed.module.cant.extend.sealed.in.diff.package: pkg1.Sealed",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Found: " + error);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSameModuleSamePkgPos(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path src_m1 = src.resolve("mSealed");
|
|
tb.writeJavaFiles(src_m1,
|
|
"module mSealed {}",
|
|
"package pkg; public sealed class Sealed permits pkg.Sub{}",
|
|
"package pkg; public final class Sub extends pkg.Sealed{}");
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
new JavacTask(tb)
|
|
.options("--module-source-path", src.toString())
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src))
|
|
.run()
|
|
.writeAll();
|
|
}
|
|
|
|
@Test
|
|
public void testSameModuleDiffPkgPos(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path src_m1 = src.resolve("mSealed");
|
|
tb.writeJavaFiles(src_m1,
|
|
"module mSealed {}",
|
|
"package pkg1; public sealed class Sealed permits pkg2.Sub{}",
|
|
"package pkg2; public final class Sub extends pkg1.Sealed{}");
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
new JavacTask(tb)
|
|
.options("--module-source-path", src.toString())
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src))
|
|
.run()
|
|
.writeAll();
|
|
}
|
|
|
|
@Test
|
|
public void testSameModuleSamePkgNeg1(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path src_m1 = src.resolve("mSealed");
|
|
// subclass doesn't extend super class
|
|
tb.writeJavaFiles(src_m1,
|
|
"module mSealed {}",
|
|
"package pkg; public sealed class Sealed permits pkg.Sub {}",
|
|
"package pkg; public final class Sub {}");
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString())
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sealed.java:1:52: compiler.err.invalid.permits.clause: (compiler.misc.doesnt.extend.sealed: pkg.Sub)",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Found: " + error);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSameModuleSamePkgNeg2(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path src_m1 = src.resolve("mSealed");
|
|
// subclass doesn't extend super class
|
|
tb.writeJavaFiles(src_m1,
|
|
"module mSealed {}",
|
|
"package pkg; public sealed interface Sealed permits pkg.Sub1, pkg.Sub2 {}",
|
|
"package pkg; public sealed class Sub1 implements Sealed permits Sub2 {}",
|
|
"package pkg; public final class Sub2 extends Sub1 {}");
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
List<String> error = new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString())
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Sealed.java:1:66: compiler.err.invalid.permits.clause: (compiler.misc.doesnt.extend.sealed: pkg.Sub2)",
|
|
"1 error");
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Found: " + error);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testDifferentModuleNeg(Path base) throws Exception {
|
|
// check that a subclass in one module can't extend a sealed class in another module
|
|
Path src = base.resolve("src");
|
|
Path src_m1 = src.resolve("mSealed");
|
|
tb.writeJavaFiles(src_m1,
|
|
"module mSealed { exports a; }",
|
|
"package a; public sealed class Base permits b.Impl {}"
|
|
);
|
|
|
|
Path src_m2 = src.resolve("mSub");
|
|
tb.writeJavaFiles(src_m2,
|
|
"module mSub { exports b; requires mSealed; }",
|
|
"package b; public final class Impl extends a.Base {}"
|
|
);
|
|
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
|
|
List<String> error =
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics",
|
|
"--module-source-path", src.toString(),
|
|
"--add-reads", "mSealed=mSub")
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src))
|
|
.run(Task.Expect.FAIL)
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
List<String> expected = List.of(
|
|
"Base.java:1:46: compiler.err.class.in.module.cant.extend.sealed.in.diff.module: a.Base, mSealed",
|
|
"1 error"
|
|
);
|
|
if (!error.containsAll(expected)) {
|
|
throw new AssertionError("Expected output not found. Found: " + error);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testSeparateCompilation(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path src_m = src.resolve("m");
|
|
tb.writeJavaFiles(src_m,
|
|
"module m {}",
|
|
"package pkg.a; public sealed interface Sealed permits pkg.b.Sub {}",
|
|
"package pkg.b; public final class Sub implements pkg.a.Sealed {}");
|
|
Path classes = base.resolve("classes");
|
|
tb.createDirectories(classes);
|
|
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString())
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src_m))
|
|
.run()
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString(), "-doe")
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src_m.resolve("pkg").resolve("a")))
|
|
.run()
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString(), "-doe")
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src_m.resolve("pkg").resolve("b")))
|
|
.run()
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
tb.cleanDirectory(classes);
|
|
|
|
//implicit compilations:
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString(), "-doe")
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src_m.resolve("pkg").resolve("a")))
|
|
.run()
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
|
|
tb.cleanDirectory(classes);
|
|
|
|
new JavacTask(tb)
|
|
.options("-XDrawDiagnostics", "--module-source-path",
|
|
src.toString(), "-doe")
|
|
.outdir(classes)
|
|
.files(findJavaFiles(src_m.resolve("pkg").resolve("b")))
|
|
.run()
|
|
.writeAll()
|
|
.getOutputLines(OutputKind.DIRECT);
|
|
}
|
|
|
|
@Test //JDK-8293348
|
|
public void testSupertypePermitsLoop(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
|
|
tb.writeJavaFiles(src,
|
|
"class Main implements T2 {}",
|
|
"non-sealed interface T2 extends T {}",
|
|
"sealed interface T permits T2 {}");
|
|
|
|
Path out = base.resolve("out");
|
|
|
|
Files.createDirectories(out);
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.files(findJavaFiles(src))
|
|
.run()
|
|
.writeAll();
|
|
|
|
Files.delete(out.resolve("Main.class"));
|
|
Files.delete(out.resolve("T.class"));
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.options("-cp", out.toString(),
|
|
"-sourcepath", src.toString())
|
|
.files(src.resolve("Main.java"))
|
|
.run()
|
|
.writeAll();
|
|
}
|
|
|
|
@Test
|
|
public void testClientSwapsPermittedSubclassesOrder(Path base) throws Exception {
|
|
Path src = base.resolve("src");
|
|
Path foo = src.resolve("Foo.java");
|
|
Path fooUser = src.resolve("FooUser.java");
|
|
|
|
tb.writeFile(foo,
|
|
"""
|
|
public sealed interface Foo {
|
|
record R1() implements Foo {}
|
|
record R2() implements Foo {}
|
|
}
|
|
""");
|
|
|
|
tb.writeFile(fooUser,
|
|
"""
|
|
public class FooUser {
|
|
// see that the order of arguments differ from the order of subclasses of Foo in the source above
|
|
// we need to check that the order of permitted subclasses of Foo in the class file corresponds to the
|
|
// original order in the source code
|
|
public void blah(Foo.R2 a, Foo.R1 b) {}
|
|
}
|
|
""");
|
|
|
|
Path out = base.resolve("out");
|
|
Files.createDirectories(out);
|
|
|
|
new JavacTask(tb)
|
|
.outdir(out)
|
|
.files(fooUser, foo)
|
|
.run();
|
|
checkSealedClassFile(out, "Foo.class", List.of("Foo$R1", "Foo$R2"));
|
|
}
|
|
}
|