JPS mappings for incremental compilation refactoring: check affection among those nodes only, that depend on affected usages' owners; explicit way to specify affection calculation scope when adding a usage query predicate

GitOrigin-RevId: 8d03716c595b0a1111b5becfd1fa088fd3d94d0e
This commit is contained in:
Eugene Zhuravlev
2023-12-05 21:36:14 +01:00
committed by intellij-monorepo-bot
parent 1b01a71fc0
commit a66230a4da
7 changed files with 30 additions and 31 deletions

View File

@@ -26,7 +26,7 @@ public interface DifferentiateContext {
void affectUsage(@NotNull Usage usage, @NotNull Predicate<Node<?, ?>> constraint);
void affectUsage(@NotNull BiPredicate<Node<?, ?>, Usage> usageQuery);
void affectUsage(Iterable<? extends ReferenceID> affectionScopeNodes, @NotNull BiPredicate<Node<?, ?>, Usage> usageQuery);
void affectNodeSource(@NotNull NodeSource source);

View File

@@ -35,8 +35,11 @@ public interface MultiMaplet<K, V> extends BaseMaplet<K> {
boolean beforeEmpty = Iterators.isEmpty(dataBefore);
boolean afterEmpty = Iterators.isEmpty(dataAfter);
if (beforeEmpty || afterEmpty) {
if (!beforeEmpty || !afterEmpty) {
put(key, dataAfter);
if (!afterEmpty) { // => beforeEmpty
appendValues(key, dataAfter);
}
else if (!beforeEmpty) {
remove(key);
}
}
else {

View File

@@ -120,7 +120,10 @@ public final class DependencyGraphImpl extends GraphImpl implements DependencyGr
}
@Override
public void affectUsage(@NotNull BiPredicate<Node<?, ?>, Usage> usageQuery) {
public void affectUsage(Iterable<? extends ReferenceID> affectionScopeNodes, @NotNull BiPredicate<Node<?, ?>, Usage> usageQuery) {
for (Usage u : map(affectionScopeNodes, AffectionScopeMetaUsage::new)) {
affectUsage(u);
}
usageQueries.add(usageQuery);
}
@@ -169,10 +172,10 @@ public final class DependencyGraphImpl extends GraphImpl implements DependencyGr
return Boolean.TRUE;
});
Iterable<ReferenceID> scopeNodes = unique(flat(map(nodesAfter, Node::getReferenceID), map(diffContext.affectedUsages.keySet(), Usage::getElementOwner)));
Iterable<ReferenceID> scopeNodes = unique(map(diffContext.affectedUsages.keySet(), Usage::getElementOwner));
Set<ReferenceID> candidates = collect(filter(flat(map(scopeNodes, this::getDependingNodes)), id -> !dependingOnDeleted.contains(id)), new HashSet<>());
for (NodeSource depSrc : unique(flat(map(candidates, dependent -> getSources(dependent))))) {
for (NodeSource depSrc : unique(flat(map(candidates, this::getSources)))) {
if (!affectedSources.contains(depSrc) && !diffContext.affectedSources.contains(depSrc) && !allProcessedSources.contains(depSrc) && params.affectionFilter().test(depSrc)) {
boolean affectSource = false;
for (var depNode : filter(getNodes(depSrc), n -> candidates.contains(n.getReferenceID()))) {

View File

@@ -194,7 +194,7 @@ public class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImpl {
if (!removedTargets.isEmpty()) {
debug("Removed some annotation targets, adding annotation query");
TypeRepr.ClassType classType = new TypeRepr.ClassType(changedClass.getName());
context.affectUsage((node, usage) -> {
context.affectUsage(asIterable(changedClass.getReferenceID()), (node, usage) -> {
if (usage instanceof AnnotationUsage) {
AnnotationUsage annotUsage = (AnnotationUsage)usage;
if (classType.equals(annotUsage.getClassType())) {
@@ -334,7 +334,7 @@ public class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImpl {
debug("Class is annotation, default value is removed => adding annotation query");
String argName = changedMethod.getName();
TypeRepr.ClassType annotType = new TypeRepr.ClassType(changedClass.getName());
context.affectUsage((node, usage) -> {
context.affectUsage(asIterable(changedClass.getReferenceID()), (node, usage) -> {
if (usage instanceof AnnotationUsage) {
// need to find annotation usages that do not use arguments this annotation uses
AnnotationUsage au = (AnnotationUsage)usage;
@@ -572,11 +572,10 @@ public class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImpl {
if (!isEmpty(addedMethod.getArgTypes()) && !present.hasOverriddenMethods(changedClass, addedMethod)) {
debug("Conservative case on overriding methods, affecting method usages");
context.affectUsage(addedMethod.createUsageQuery(changedClass.getReferenceID()));
context.affectUsage(asIterable(changedClass.getReferenceID()), addedMethod.createUsageQuery(changedClass.getReferenceID()));
if (!addedMethod.isConstructor()) { // do not propagate constructors access, since constructors are always concrete and not accessible via references to subclasses
for (JvmNodeReferenceID id : propagated) {
context.affectUsage(new AffectionScopeMetaUsage(id));
context.affectUsage(addedMethod.createUsageQuery(id));
context.affectUsage(asIterable(id), addedMethod.createUsageQuery(id));
}
}
}
@@ -687,13 +686,9 @@ public class JavaDifferentiateStrategy extends JvmDifferentiateStrategyImpl {
if (!addedField.isPrivate() && addedField.isStatic()) {
affectStaticMemberOnDemandUsages(context, subClass, Collections.emptyList());
}
else {
// ensure analysis scope includes classes that depend on the subclass
context.affectUsage(new AffectionScopeMetaUsage(subClass));
}
}
context.affectUsage((n, u) -> {
context.affectUsage(changedClassWithSubclasses, (n, u) -> {
// affect all clients that access fields with the same name via subclasses,
// if the added field is not visible to the client
if (!(u instanceof FieldUsage) || !(n instanceof JvmClass)) {

View File

@@ -116,12 +116,13 @@ public final class Utils {
}
public Iterable<JvmNodeReferenceID> allDirectSupertypes(JvmNodeReferenceID classId) {
return unique(flat(map(getNodes(classId, JvmClass.class), cl -> map(cl.getSuperTypes(), st -> new JvmNodeReferenceID(st)))));
// return only those direct supertypes that exist in the graph as Nodes
return unique(flat(map(getNodes(classId, JvmClass.class), cl -> flat(map(cl.getSuperTypes(), st -> map(getNodes(new JvmNodeReferenceID(st), JvmClass.class), JvmClass::getReferenceID))))));
}
public Iterable<JvmNodeReferenceID> allSupertypes(JvmNodeReferenceID classId) {
//return Iterators.recurseDepth(className, s -> allDirectSupertypes(s), false);
return recurse(classId, s -> allDirectSupertypes(s), false);
//return recurseDepth(className, this::allDirectSupertypes, false);
return recurse(classId, this::allDirectSupertypes, false);
}
@NotNull

View File

@@ -4,7 +4,6 @@ package org.jetbrains.jps.dependency.kotlin;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.dependency.AffectionScopeMetaUsage;
import org.jetbrains.jps.dependency.DifferentiateContext;
import org.jetbrains.jps.dependency.diff.Difference;
import org.jetbrains.jps.dependency.java.*;
@@ -70,22 +69,20 @@ public final class KotlinAwareJavaDifferentiateStrategy extends JvmDifferentiate
@Override
public boolean processAddedMethod(DifferentiateContext context, JvmClass changedClass, JvmMethod addedMethod, Utils future, Utils present) {
// any added method may conflict with an extension method to this class, defined elsewhere
if (!addedMethod.isPrivate()) {
affectConflictingExtensionMethods(context, changedClass, addedMethod, null, future);
}
affectConflictingExtensionMethods(context, changedClass, addedMethod, null, future);
return true;
}
private static void affectConflictingExtensionMethods(DifferentiateContext context, JvmClass cls, JvmMethod clsMethod, @Nullable TypeRepr.ClassType samType, Utils utils) {
if (clsMethod.isPrivate() || clsMethod.isConstructor()) {
return;
}
// the first arg is always the class being extended
Set<String> firstArgTypes = collect(
map(flat(utils.allSupertypes(cls.getReferenceID()), utils.collectSubclassesWithoutMethod(cls.getReferenceID(), clsMethod)), id -> id.getNodeName()), new HashSet<>()
);
firstArgTypes.add(cls.getName());
for (String clsName : firstArgTypes) {
context.affectUsage(new AffectionScopeMetaUsage(new JvmNodeReferenceID(clsName)));
}
context.affectUsage((n, u) -> {
context.affectUsage(map(firstArgTypes, JvmNodeReferenceID::new), (n, u) -> {
if (!(u instanceof MethodUsage) || !(n instanceof JvmClass)) {
return false;
}

View File

@@ -206,7 +206,7 @@ public final class BuildDataManager {
if (deleteExisting) {
FileUtil.delete(mappingsRoot);
}
myDepGraph = asSynchronizableGraph(new DependencyGraphImpl(Containers.createPersistentContainerFactory(mappingsRoot.getAbsolutePath())));
myDepGraph = asSynchronizedGraph(new DependencyGraphImpl(Containers.createPersistentContainerFactory(mappingsRoot.getAbsolutePath())));
}
else {
try {
@@ -216,7 +216,7 @@ public final class BuildDataManager {
if (deleteExisting) {
FileUtil.delete(mappingsRoot);
}
myDepGraph = asSynchronizableGraph(new DependencyGraphImpl(Containers.createPersistentContainerFactory(mappingsRoot.getAbsolutePath())));
myDepGraph = asSynchronizedGraph(new DependencyGraphImpl(Containers.createPersistentContainerFactory(mappingsRoot.getAbsolutePath())));
}
}
}
@@ -485,7 +485,7 @@ public final class BuildDataManager {
};
}
private static DependencyGraph asSynchronizableGraph(DependencyGraph graph) {
private static DependencyGraph asSynchronizedGraph(DependencyGraph graph) {
//noinspection IOResourceOpenedButNotSafelyClosed
DependencyGraph delegate = new LoggingDependencyGraph(graph, msg -> LOG.info(msg));
return new DependencyGraph() {