diff --git a/java/java-tests/testData/compileServer/incremental/classModifiers/becameSealed.log b/java/java-tests/testData/compileServer/incremental/classModifiers/becameSealed.log index 274913b03d78..ab323964b659 100644 --- a/java/java-tests/testData/compileServer/incremental/classModifiers/becameSealed.log +++ b/java/java-tests/testData/compileServer/incremental/classModifiers/becameSealed.log @@ -7,10 +7,8 @@ src/A.java src/B.java End of files Cleaning output files: -out/production/BecameSealed/C.class out/production/BecameSealed/D.class End of files Compiling files: -src/C.java src/D.java End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission.log b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission.log new file mode 100644 index 000000000000..a1eb0194e607 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission.log @@ -0,0 +1,12 @@ +Cleaning output files: +out/production/DeleteSealedPermission/B.class +End of files +Compiling files: +src/B.java +End of files +Cleaning output files: +out/production/DeleteSealedPermission/A3.class +End of files +Compiling files: +src/A3.java +End of files diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A1.java b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A1.java new file mode 100644 index 000000000000..f8b7ee799ace --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A1.java @@ -0,0 +1,2 @@ +public final class A1 extends B { +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A2.java b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A2.java new file mode 100644 index 000000000000..4f369cf59914 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A2.java @@ -0,0 +1,2 @@ +public final class A2 extends B { +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A3.java b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A3.java new file mode 100644 index 000000000000..0be8088b112c --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/A3.java @@ -0,0 +1,2 @@ +public final class A3 extends B { +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java new file mode 100644 index 000000000000..1852d41ff4a2 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java @@ -0,0 +1,3 @@ +public sealed class B permits A1, A2, A3 { + void foo(A3 param) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java.new b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java.new new file mode 100644 index 000000000000..c31c7daeecc3 --- /dev/null +++ b/java/java-tests/testData/compileServer/incremental/common/deleteSealedPermission/src/B.java.new @@ -0,0 +1,3 @@ +public sealed class B permits A1, A2 { + void foo(A3 param) {} +} \ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassPermitsUsage.java b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassPermitsUsage.java new file mode 100644 index 000000000000..5a11a6868b5d --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassPermitsUsage.java @@ -0,0 +1,27 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.jps.dependency.java; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.dependency.GraphDataInput; + +import java.io.IOException; + +public final class ClassPermitsUsage extends ClassUsage { + + public ClassPermitsUsage(@NotNull String className) { + super(className); + } + + public ClassPermitsUsage(@NotNull JvmNodeReferenceID clsId) { + super(clsId); + } + + public ClassPermitsUsage(GraphDataInput in) throws IOException { + super(in); + } + + @Override + public int hashCode() { + return super.hashCode() + 4; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassUsage.java b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassUsage.java index 65dea1d53d34..78056a7614a8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassUsage.java +++ b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/ClassUsage.java @@ -1,4 +1,4 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.jps.dependency.java; import org.jetbrains.annotations.NotNull; @@ -24,4 +24,8 @@ public class ClassUsage extends JvmElementUsage { return getElementOwner().getNodeName(); } + @Override + public int hashCode() { + return super.hashCode() + 10; + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JavaDifferentiateStrategy.java b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JavaDifferentiateStrategy.java index 488054d824a2..3716d4fb8c5f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JavaDifferentiateStrategy.java +++ b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JavaDifferentiateStrategy.java @@ -99,7 +99,7 @@ public final class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImp JvmClass.Diff classDiff = change.getDiff(); debug("Processing changed class ", changedClass.getName()); - if (classDiff.superClassChanged() || classDiff.signatureChanged() || classDiff.becameSealed() || !classDiff.interfaces().unchanged()) { + if (classDiff.superClassChanged() || classDiff.signatureChanged() || !classDiff.interfaces().unchanged()) { boolean extendsChanged = classDiff.superClassChanged() && !classDiff.extendsAdded(); boolean affectUsages = classDiff.signatureChanged() || extendsChanged || !isEmpty(classDiff.interfaces().removed()); affectSubclasses(context, future, change.getNow().getReferenceID(), affectUsages); @@ -125,6 +125,14 @@ public final class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImp } } } + else if (change.getNow().isSealed()) { + ReferenceID fromClass = change.getNow().getReferenceID(); + Set permitted = collect(map(filter(change.getNow().getUsages(), u -> u instanceof ClassPermitsUsage), Usage::getElementOwner), new SmartHashSet<>()); + debug("Affecting non-permitted subclasses of a sealed class: ", fromClass); + for (ReferenceID cl : filter(future.allSubclasses(fromClass), c -> !permitted.contains(c))) { + affectNodeSources(context, cl, "Affecting source file of a non-permitted subclass: ", future); + } + } JVMFlags addedFlags = classDiff.getAddedFlags(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClass.java b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClass.java index 84bfea47358e..10ac229c14db 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClass.java +++ b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClass.java @@ -212,10 +212,6 @@ public final class JvmClass extends JVMClassNode { return (currentSuper.isEmpty() || OBJECT_CLASS_NAME.equals(currentSuper)) && superClassChanged(); } - public boolean becameSealed() { - return getAddedFlags().isSealed(); - } - public boolean outerClassChanged() { return !Objects.equals(myPast.getOuterFqName(), getOuterFqName()); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClassNodeBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClassNodeBuilder.java index 732385896c6a..0e23d5a200f4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClassNodeBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/dependency/java/JvmClassNodeBuilder.java @@ -911,6 +911,7 @@ public final class JvmClassNodeBuilder extends ClassVisitor implements NodeBuild public void visitPermittedSubclass(String permittedSubclass) { mySealedClassFlag.set(true); addUsage(new ClassUsage(permittedSubclass)); + addUsage(new ClassPermitsUsage(permittedSubclass)); } private class BaseSignatureVisitor extends SignatureVisitor { diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/ClassModifierTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/ClassModifierTest.java index 90386764a207..102642587c7c 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/ClassModifierTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/ClassModifierTest.java @@ -49,7 +49,7 @@ public class ClassModifierTest extends IncrementalTestCase { } public void testBecameSealed() { - doTest(); + doTest().assertFailed(); } public void testChangeInnerClassModifiers() { diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java index 35bb35ef5940..483abcbf4ab7 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java @@ -13,7 +13,7 @@ import org.jetbrains.jps.model.module.JpsModule; import java.util.Set; public class CommonTest extends IncrementalTestCase { - private static final Set GRAPH_ONLY_TESTS = Set.of("addClassHidingImportedClass", "addClassHidingImportedClass2", "deletePermittedClass"); + private static final Set GRAPH_ONLY_TESTS = Set.of("addClassHidingImportedClass", "addClassHidingImportedClass2", "deletePermittedClass", "deleteSealedPermission"); public CommonTest() { super("common"); @@ -107,7 +107,11 @@ public class CommonTest extends IncrementalTestCase { } public void testDeletePermittedClass() { - doTest(); + doTest().assertFailed(); + } + + public void testDeleteSealedPermission() { + doTest().assertFailed(); } public void testDeleteInnerClass() {