affect lambda instantiations when an abstract method in a SAM interface becomes non-abstract (IDEA-263109)

GitOrigin-RevId: 882c034d0cd6afbc297dca1e59e940dd90f8c5a4
This commit is contained in:
Eugene Zhuravlev
2021-02-27 14:52:58 +00:00
committed by intellij-monorepo-bot
parent b7ff0afe73
commit adfe648216
24 changed files with 169 additions and 32 deletions
@@ -0,0 +1,14 @@
Cleaning output files:
out/production/DeleteSAMInterfaceMethod/Service.class
End of files
Compiling files:
src/Service.java
End of files
Cleaning output files:
out/production/DeleteSAMInterfaceMethod/DerivedServiceClient.class
out/production/DeleteSAMInterfaceMethod/ServiceClient.class
End of files
Compiling files:
src/DerivedServiceClient.java
src/ServiceClient.java
End of files
@@ -0,0 +1,2 @@
interface DerivedService extends Service {
}
@@ -0,0 +1,5 @@
public class DerivedServiceClient {
public void execute() {
Util.invokeDerivedService(() -> System.out.println("Hello2"));
}
}
@@ -0,0 +1,5 @@
public class ServiceClient {
public void execute() {
Util.invokeService(() -> System.out.println("Hello"));
}
}
@@ -0,0 +1,7 @@
public class Util {
public static void invokeService(Service s) {
}
public static void invokeDerivedService(DerivedService s) {
}
}
@@ -0,0 +1,16 @@
Cleaning output files:
out/production/RenameSAMInterfaceMethod/Service.class
End of files
Compiling files:
src/Service.java
End of files
Cleaning output files:
out/production/RenameSAMInterfaceMethod/DerivedService.class
out/production/RenameSAMInterfaceMethod/DerivedServiceClient.class
out/production/RenameSAMInterfaceMethod/ServiceClient.class
End of files
Compiling files:
src/DerivedService.java
src/DerivedServiceClient.java
src/ServiceClient.java
End of files
@@ -0,0 +1,2 @@
interface DerivedService extends Service {
}
@@ -0,0 +1,5 @@
public class DerivedServiceClient {
public void execute() {
Util.invokeDerivedService(() -> System.out.println("Hello2"));
}
}
@@ -0,0 +1,3 @@
interface Service {
void foo();
}
@@ -0,0 +1,3 @@
interface Service {
void bar();
}
@@ -0,0 +1,5 @@
public class ServiceClient {
public void execute() {
Util.invokeService(() -> System.out.println("Hello"));
}
}
@@ -0,0 +1,7 @@
public class Util {
public static void invokeService(Service s) {
}
public static void invokeDerivedService(DerivedService s) {
}
}
@@ -0,0 +1,14 @@
Cleaning output files:
out/production/UnsetAbstractForSAMInterface/Service.class
End of files
Compiling files:
src/Service.java
End of files
Cleaning output files:
out/production/UnsetAbstractForSAMInterface/DerivedServiceClient.class
out/production/UnsetAbstractForSAMInterface/ServiceClient.class
End of files
Compiling files:
src/DerivedServiceClient.java
src/ServiceClient.java
End of files
@@ -0,0 +1,2 @@
interface DerivedService extends Service {
}
@@ -0,0 +1,5 @@
public class DerivedServiceClient {
public void execute() {
Util.invokeDerivedService(() -> System.out.println("Hello2"));
}
}
@@ -0,0 +1,5 @@
interface Service {
void foo();
default void bar() {}
}
@@ -0,0 +1,5 @@
interface Service {
default void foo() {}
default void bar() {}
}
@@ -0,0 +1,5 @@
public class ServiceClient {
public void execute() {
Util.invokeService(() -> System.out.println("Hello"));
}
}
@@ -0,0 +1,9 @@
public class Util {
public static void invokeService(Service s) {
s.foo();
}
public static void invokeDerivedService(DerivedService s) {
s.foo();
}
}
@@ -552,8 +552,10 @@ public class Mappings {
return false;
}
boolean isSAMInterface(ClassRepr cls) {
if (!cls.isInterface()) {
// test if a ClassRepr is a SAM interface
boolean isLambdaTarget(int name) {
final ClassRepr cls = classReprByName(name);
if (cls == null || !cls.isInterface()) {
return false;
}
int amFound = 0;
@@ -847,6 +849,18 @@ public class Mappings {
}.perform(moduleName);
}
void affectLambdaInstantiations(Differential.DiffState state, final int className) {
getAllSubclasses(className).forEach(name -> {
if (isLambdaTarget(name)) {
debug("The interface could be not a SAM interface anymore or lambda target method name has changed => affecting lambda instantiations for ", name);
if (state.myAffectedUsages.add(UsageRepr.createClassNewUsage(myContext, name))) {
appendDependents(name, state.myDependants);
}
}
return true;
});
}
public class FileFilterConstraint implements UsageConstraint {
@NotNull
private final DependentFilesFilter myFilter;
@@ -1201,8 +1215,8 @@ public class Mappings {
if (it.isInterface()) {
for (final MethodRepr m : added) {
if (!m.isPrivate() && m.isAbstract()) {
debug("Method: ", m.name);
affectLambdaInstantiations(state, it.name);
debug("Added non-private abstract method: ", m.name);
myPresent.affectLambdaInstantiations(state, it.name);
break;
}
}
@@ -1310,20 +1324,6 @@ public class Mappings {
debug("End of added methods processing");
}
private void affectLambdaInstantiations(DiffState state, final int className) {
assert myPresent != null;
getAllSubclasses(className).forEach(name -> {
@SuppressWarnings("ConstantConditions")
final ClassRepr cr = myPresent.classReprByName(name);
if (cr != null && myPresent.isSAMInterface(cr)) {
debug("Abstract method added to a SAM interface => affecting lambda instantiations for ", name);
state.myAffectedUsages.add(UsageRepr.createClassNewUsage(myContext, name));
}
return true;
});
}
private void processRemovedMethods(final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) {
final Collection<MethodRepr> removed = diff.methods().removed();
if (removed.isEmpty()) {
@@ -1445,9 +1445,20 @@ public class Mappings {
}
debug("Processing changed methods:");
assert myPresent != null;
assert myFuture != null;
assert myAffectedFiles != null;
if (it.isInterface()) {
for (final Pair<MethodRepr, MethodRepr.Diff> mr : changed) {
if ((mr.second.removedModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
debug("Method became non-abstract: ", mr.first.name);
myPresent.affectLambdaInstantiations(state, it.name);
break;
}
}
}
for (final Pair<MethodRepr, MethodRepr.Diff> mr : changed) {
final MethodRepr m = mr.first;
final MethodRepr.Diff d = mr.second;
@@ -1550,7 +1561,7 @@ public class Mappings {
debug("Added final, public or abstract specifier --- affecting subclasses");
myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null);
if (it.isInterface() && (d.addedModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
affectLambdaInstantiations(state, it.name);
myPresent.affectLambdaInstantiations(state, it.name);
}
}
@@ -3036,18 +3047,13 @@ public class Mappings {
}
}
private static boolean addAll(final TIntHashSet whereToAdd, TIntHashSet whatToAdd) {
if (whatToAdd.isEmpty()) {
return false;
private static void addAll(final TIntHashSet whereToAdd, TIntHashSet whatToAdd) {
if (!whatToAdd.isEmpty()) {
whatToAdd.forEach(value -> {
whereToAdd.add(value);
return true;
});
}
final Ref<Boolean> changed = new Ref<>(Boolean.FALSE);
whatToAdd.forEach(value -> {
if (whereToAdd.add(value)) {
changed.set(Boolean.TRUE);
}
return true;
});
return changed.get();
}
private static void addAllKeys(final TIntHashSet whereToAdd, final IntIntMultiMaplet maplet) {
@@ -31,6 +31,14 @@ public class MemberChangeTest extends IncrementalTestCase {
doTest();
}
public void testDeleteSAMInterfaceMethod() {
doTest();
}
public void testRenameSAMInterfaceMethod() {
doTest();
}
public void testAddSAMInterfaceAbstractMethod() {
doTest();
}
@@ -51,11 +51,14 @@ public class MethodModifierTest extends IncrementalTestCase {
doTest();
}
public void testUnsetFinal() {
doTest();
}
public void testUnsetAbstractForSAMInterface() {
doTest();
}
public void testUnsetStatic() {
doTest();
}