java make: correctly recompile usages of method that has been modified and overridden in one of subclasses in the same change

GitOrigin-RevId: 3bf4d1fea91c892b64db71510d9efc0bf535873f
This commit is contained in:
Eugene Zhuravlev
2019-11-26 21:01:53 +01:00
committed by intellij-monorepo-bot
parent 0c1b3cddf4
commit 096910239f
9 changed files with 110 additions and 60 deletions

View File

@@ -0,0 +1,16 @@
Cleaning output files:
out/production/AddOverridingMethodAndChangeReturnType/ppp/BaseA.class
out/production/AddOverridingMethodAndChangeReturnType/ppp/BaseB.class
End of files
Compiling files:
src/ppp/BaseA.java
src/ppp/BaseB.java
End of files
Cleaning output files:
out/production/AddOverridingMethodAndChangeReturnType/ppp/BaseB.class
out/production/AddOverridingMethodAndChangeReturnType/ppp/Client.class
End of files
Compiling files:
src/ppp/BaseB.java
src/ppp/Client.java
End of files

View File

@@ -0,0 +1,7 @@
package ppp;
public class BaseA {
public Boolean process(int a) {
return Boolean.TRUE;
}
}

View File

@@ -0,0 +1,7 @@
package ppp;
public class BaseA {
public boolean process(int a) {
return Boolean.TRUE;
}
}

View File

@@ -0,0 +1,4 @@
package ppp;
public class BaseB extends BaseA{
}

View File

@@ -0,0 +1,7 @@
package ppp;
public class BaseB extends BaseA {
public boolean process(int a) {
return super.process(a);
}
}

View File

@@ -0,0 +1,4 @@
package ppp;
public class BaseC extends BaseB {
}

View File

@@ -0,0 +1,7 @@
package ppp;
public class Client {
public static void process(BaseC model) {
model.process(10);
}
}

View File

@@ -26,6 +26,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Supplier;
/**
* @author: db
@@ -346,13 +347,7 @@ public class Mappings {
}
TIntHashSet propagateMethodAccess(final MethodRepr m, final int className) {
return propagateMemberAccess(false, member -> {
if (member instanceof MethodRepr) {
final MethodRepr memberMethod = (MethodRepr)member;
return memberMethod.name == m.name && Arrays.equals(memberMethod.myArgumentTypes, m.myArgumentTypes);
}
return false;
}, className);
return propagateMemberAccess(false, member -> m.equals(member), className);
}
MethodRepr.Predicate lessSpecific(final MethodRepr than) {
@@ -1251,7 +1246,7 @@ public class Mappings {
assert myPresent != null;
assert myAffectedFiles != null;
Ref<ClassRepr> oldItRef = null;
final Supplier<ClassRepr> oldClassRepr = lazy(() -> getClassReprByName(null, it.name));
for (final MethodRepr m : added) {
debug("Method: ", m.name);
if (!m.isPrivate() && (it.isInterface() || it.isAbstract() || m.isAbstract())) {
@@ -1259,48 +1254,38 @@ public class Mappings {
myFuture.affectSubclasses(it.name, myAffectedFiles, state.myAffectedUsages, state.myDependants, false, myCompiledFiles, null);
}
TIntHashSet propagated = null;
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateMethodAccess(m, it.name));
if (!m.isPrivate()) {
if (oldItRef == null) {
oldItRef = new Ref<>(getClassReprByName(null, it.name)); // lazy init
}
final ClassRepr oldIt = oldItRef.get();
if (oldIt == null || !myPresent.hasOverriddenMethods(oldIt, MethodRepr.equalByJavaRules(m), null)) {
final ClassRepr oldRepr = oldClassRepr.get();
if (oldRepr == null || !myPresent.hasOverriddenMethods(oldRepr, MethodRepr.equalByJavaRules(m), null)) {
if (m.myArgumentTypes.length > 0) {
if (m.name != myInitName) {
// do not propagate constructors access, since constructors are always concrete and not accessible via references to subclasses
propagated = myFuture.propagateMethodAccess(m, it.name);
}
debug("Conservative case on overriding methods, affecting method usages");
myFuture.affectMethodUsages(m, propagated, m.createMetaUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
// do not propagate constructors access, since constructors are always concrete and not accessible via references to subclasses
myFuture.affectMethodUsages(m, m.name == myInitName? null : propagated.get(), m.createMetaUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
}
}
if (!m.isPrivate()) {
final Collection<Pair<MethodRepr, ClassRepr>> affectedMethods = myFuture.findAllMethodsBySpecificity(m, it);
final MethodRepr.Predicate overrides = MethodRepr.equalByJavaRules(m);
if (propagated == null) {
propagated = myFuture.propagateMethodAccess(m, it.name);
}
if (m.isStatic()) {
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
final Collection<MethodRepr> lessSpecific = it.findMethods(myFuture.lessSpecific(m));
final Collection<MethodRepr> removed = diff.methods().removed();
for (final MethodRepr mm : lessSpecific) {
if (!mm.equals(m)) {
if (!mm.equals(m) && !removed.contains(mm)) {
debug("Found less specific method, affecting method usages");
myFuture.affectMethodUsages(mm, propagated, mm.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
myFuture.affectMethodUsages(mm, propagated.get(), mm.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
}
debug("Processing affected by specificity methods");
final Collection<Pair<MethodRepr, ClassRepr>> affectedMethods = myFuture.findAllMethodsBySpecificity(m, it);
final MethodRepr.Predicate overrides = MethodRepr.equalByJavaRules(m);
for (final Pair<MethodRepr, ClassRepr> pair : affectedMethods) {
final MethodRepr method = pair.first;
final ClassRepr methodClass = pair.second;
@@ -1338,13 +1323,11 @@ public class Mappings {
addAll(state.myDependants, deps);
}
myFuture.affectMethodUsages(method, yetPropagated, method.createUsage(myContext, methodClass.name), state.myAffectedUsages,
state.myDependants);
myFuture.affectMethodUsages(method, yetPropagated, method.createUsage(myContext, methodClass.name), state.myAffectedUsages, state.myDependants);
}
debug("Affecting method usages for that found");
myFuture.affectMethodUsages(method, yetPropagated, method.createUsage(myContext, it.name), state.myAffectedUsages,
state.myDependants);
myFuture.affectMethodUsages(method, yetPropagated, method.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
}
@@ -1380,6 +1363,7 @@ public class Mappings {
return;
}
assert myFuture != null;
assert myPresent != null;
assert myAffectedFiles != null;
assert myCompiledFiles != null;
@@ -1387,23 +1371,23 @@ public class Mappings {
for (final MethodRepr m : removed) {
debug("Method ", m.name);
final Collection<Pair<MethodRepr, ClassRepr>> overridenMethods = myFuture.findOverriddenMethods(m, it);
final TIntHashSet propagated = myFuture.propagateMethodAccess(m, it.name);
final Collection<Pair<MethodRepr, ClassRepr>> overriddenMethods = myFuture.findOverriddenMethods(m, it);
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateMethodAccess(m, it.name));
if (!m.isPrivate() && m.isStatic()) {
debug("The method was static --- affecting static method import usages");
myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
if (overridenMethods.size() == 0) {
if (overriddenMethods.size() == 0) {
debug("No overridden methods found, affecting method usages");
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
else {
boolean clear = true;
loop:
for (final Pair<MethodRepr, ClassRepr> overriden : overridenMethods) {
for (final Pair<MethodRepr, ClassRepr> overriden : overriddenMethods) {
final MethodRepr mm = overriden.first;
if (mm == MOCK_METHOD || !mm.myType.equals(m.myType) || !isEmpty(mm.signature) || !isEmpty(m.signature) || m.isMoreAccessibleThan(mm)) {
@@ -1414,7 +1398,7 @@ public class Mappings {
if (!clear) {
debug("No clearly overridden methods found, affecting method usages");
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
}
@@ -1433,14 +1417,14 @@ public class Mappings {
}
if (!m.isAbstract() && !m.isStatic()) {
propagated.forEach(p -> {
propagated.get().forEach(p -> {
if (p != it.name) {
final ClassRepr s = myFuture.classReprByName(p);
if (s != null) {
final Collection<Pair<MethodRepr, ClassRepr>> overridenInS = myFuture.findOverriddenMethods(m, s);
overridenInS.addAll(overridenMethods);
overridenInS.addAll(overriddenMethods);
boolean allAbstract = true;
boolean visited = false;
@@ -1513,7 +1497,7 @@ public class Mappings {
}
}
else if (d.base() != Difference.NONE || throwsChanged) {
final TIntHashSet propagated = myFuture.propagateMethodAccess(m, it.name);
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateMethodAccess(m, it.name));
boolean affected = false;
boolean constrained = false;
@@ -1522,7 +1506,7 @@ public class Mappings {
if (d.packageLocalOn()) {
debug("Method became package-private, affecting method usages outside the package");
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants);
for (final UsageRepr.Usage usage : usages) {
state.myUsageConstraints.put(usage, myFuture.new PackageConstraint(it.getPackageName()));
@@ -1536,7 +1520,7 @@ public class Mappings {
if ((d.base() & Difference.TYPE) != 0 || (d.base() & Difference.SIGNATURE) != 0 || throwsChanged) {
if (!affected) {
debug("Return type, throws list or signature changed --- affecting method usages");
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants);
final List<Pair<MethodRepr, ClassRepr>> overridingMethods = new LinkedList<>();
@@ -1569,7 +1553,7 @@ public class Mappings {
if (!affected) {
debug("Added {static | private | synthetic | bridge} specifier or removed static specifier --- affecting method usages");
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
affected = true;
}
@@ -1580,13 +1564,13 @@ public class Mappings {
if (!m.isPrivate()) {
debug("Added static modifier --- affecting static member on-demand import usages");
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
}
else if ((d.removedModifiers() & Opcodes.ACC_STATIC) != 0) {
if (!m.isPrivate()) {
debug("Removed static modifier --- affecting static method import usages");
myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberImportUsages(m.name, it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
}
}
@@ -1602,7 +1586,7 @@ public class Mappings {
if (!constrained) {
debug("Added public or package-private method became protected --- affect method usages with protected constraint");
if (!affected) {
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
affected = true;
}
@@ -1633,7 +1617,7 @@ public class Mappings {
}
if (toRecompile.contains(AnnotationsChangeTracker.Recompile.USAGES)) {
myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectMethodUsages(m, propagated.get(), m.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
if (constrained) {
// remove any constraints so that all usages of this method are recompiled
@@ -1827,12 +1811,12 @@ public class Mappings {
}
if (d.base() != Difference.NONE) {
final TIntHashSet propagated = myFuture.propagateFieldAccess(field.name, it.name);
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateFieldAccess(field.name, it.name));
if ((d.base() & Difference.TYPE) != 0 || (d.base() & Difference.SIGNATURE) != 0) {
debug("Type or signature changed --- affecting field usages");
myFuture.affectFieldUsages(
field, propagated, field.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants
field, propagated.get(), field.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants
);
}
else if ((d.base() & Difference.ACCESS) != 0) {
@@ -1842,16 +1826,16 @@ public class Mappings {
(d.addedModifiers() & Opcodes.ACC_VOLATILE) != 0) {
debug("Added/removed static modifier or added private/volatile modifier --- affecting field usages");
myFuture.affectFieldUsages(
field, propagated, field.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants
field, propagated.get(), field.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants
);
if (!field.isPrivate()) {
if ((d.addedModifiers() & Opcodes.ACC_STATIC) != 0) {
debug("Added static modifier --- affecting static member on-demand import usages");
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberOnDemandUsages(it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
else if ((d.removedModifiers() & Opcodes.ACC_STATIC) != 0) {
debug("Removed static modifier --- affecting static field import usages");
myFuture.affectStaticMemberImportUsages(field.name, it.name, propagated, state.myAffectedUsages, state.myDependants);
myFuture.affectStaticMemberImportUsages(field.name, it.name, propagated.get(), state.myAffectedUsages, state.myDependants);
}
}
}
@@ -1860,13 +1844,13 @@ public class Mappings {
if ((d.addedModifiers() & Opcodes.ACC_FINAL) != 0) {
debug("Added final modifier --- affecting field assign usages");
myFuture.affectFieldUsages(field, propagated, field.createAssignUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectFieldUsages(field, propagated.get(), field.createAssignUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
}
if ((d.removedModifiers() & Opcodes.ACC_PUBLIC) != 0) {
debug("Removed public modifier, affecting field usages with appropriate constraint");
myFuture.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectFieldUsages(field, propagated.get(), field.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
for (final UsageRepr.Usage usage : usages) {
@@ -1880,7 +1864,7 @@ public class Mappings {
}
else if ((d.removedModifiers() & Opcodes.ACC_PROTECTED) != 0 && d.accessRestricted()) {
debug("Removed protected modifier and the field became less accessible, affecting field usages with package constraint");
myFuture.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectFieldUsages(field, propagated.get(), field.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
for (final UsageRepr.Usage usage : usages) {
@@ -1907,7 +1891,7 @@ public class Mappings {
}
if (toRecompile.contains(AnnotationsChangeTracker.Recompile.USAGES)) {
final Set<UsageRepr.Usage> usages = new THashSet<>();
myFuture.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, state.myDependants);
myFuture.affectFieldUsages(field, propagated.get(), field.createUsage(myContext, it.name), usages, state.myDependants);
state.myAffectedUsages.addAll(usages);
// remove any constraints to ensure all field usages are recompiled
for (UsageRepr.Usage usage : usages) {
@@ -3151,6 +3135,16 @@ public class Mappings {
}
}
private static <T> Supplier<T> lazy(Supplier<T> calculation) {
return new Supplier<T>() {
Ref<T> calculated;
@Override
public T get() {
return (calculated != null? calculated : (calculated = new Ref<>(calculation.get()))).get();
}
};
}
public void toStream(File outputRoot) {
final Streamable[] data = {
myClassToSubclasses,

View File

@@ -211,6 +211,10 @@ public class MemberChangeTest extends IncrementalTestCase {
doTest();
}
public void testAddOverridingMethodAndChangeReturnType() {
doTest();
}
public void testAddOverloadingConstructor() {
doTest();
}