JPS mappings for incremental compilation refactoring: fixes in persistence; update back-deps indexes correctly; added class short name index; rules for added classes

GitOrigin-RevId: da6760219c22d1043ce1b9ac542a59bfee151780
This commit is contained in:
Eugene Zhuravlev
2023-10-26 22:56:30 +02:00
committed by intellij-monorepo-bot
parent 0d94a90f56
commit 893bdd3eec
9 changed files with 153 additions and 61 deletions

View File

@@ -1,15 +1,16 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
public interface BackDependencyIndex {
@NotNull String getName();
Iterable<ReferenceID> getKeys();
Iterable<ReferenceID> getDependencies(@NotNull ReferenceID id);
void indexNode(@NotNull Node<?, ?> node);
void integrate(Iterable<Node<?, ?>> deletedNodes, Iterable<Node<?, ?>> updatedNodes, Iterable<Pair<ReferenceID, Iterable<ReferenceID>>> delta);
void integrate(Iterable<Node<?, ?>> deletedNodes, Iterable<Node<?, ?>> updatedNodes, BackDependencyIndex deltaIndex);
}

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.dependency.*;
import org.jetbrains.jps.javac.Iterators;
@@ -28,6 +27,11 @@ public abstract class BackDependencyIndexImpl implements BackDependencyIndex {
return myName;
}
@Override
public Iterable<ReferenceID> getKeys() {
return myMap.getKeys();
}
@Override
public @NotNull Iterable<ReferenceID> getDependencies(@NotNull ReferenceID id) {
Iterable<ReferenceID> nodes = myMap.get(id);
@@ -43,28 +47,30 @@ public abstract class BackDependencyIndexImpl implements BackDependencyIndex {
}
@Override
public void integrate(Iterable<Node<?, ?>> deletedNodes, Iterable<Node<?, ?>> updatedNodes, Iterable<Pair<ReferenceID, Iterable<ReferenceID>>> indexDelta) {
public void integrate(Iterable<Node<?, ?>> deletedNodes, Iterable<Node<?, ?>> updatedNodes, BackDependencyIndex deltaIndex) {
Map<ReferenceID, Set<ReferenceID>> depsToRemove = new HashMap<>();
for (var node : deletedNodes) {
cleanupDependencies(node, depsToRemove);
myMap.remove(node.getReferenceID());
}
for (var node : updatedNodes) {
cleanupDependencies(node, depsToRemove);
}
for (Pair<ReferenceID, Iterable<ReferenceID>> p : indexDelta) {
ReferenceID nodeID = p.getFirst();
Set<ReferenceID> deps = Iterators.collect(getDependencies(nodeID), new HashSet<>());
Iterable<ReferenceID> toRemove = depsToRemove.get(nodeID);
if (toRemove != null) {
for (ReferenceID d : toRemove) {
deps.remove(d);
for (ReferenceID id : Iterators.unique(Iterators.flat(deltaIndex.getKeys(), depsToRemove.keySet()))) {
Set<ReferenceID> toRemove = depsToRemove.get(id);
if (!Iterators.isEmpty(toRemove)) {
Set<ReferenceID> deps = Iterators.collect(getDependencies(id), new HashSet<>());
deps.removeAll(toRemove);
myMap.put(id, Iterators.collect(deltaIndex.getDependencies(id), deps));
}
else {
Iterable<ReferenceID> toAdd = deltaIndex.getDependencies(id);
if (!Iterators.isEmpty(toAdd)) {
myMap.appendValues(id, toAdd);
}
}
myMap.put(nodeID, Iterators.collect(p.getSecond(), deps));
}
}

View File

@@ -3,6 +3,7 @@ package org.jetbrains.jps.dependency.impl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.dependency.*;
import org.jetbrains.jps.dependency.java.ClassShortNameIndex;
import org.jetbrains.jps.dependency.java.SubclassesIndex;
import org.jetbrains.jps.javac.Iterators;
@@ -28,6 +29,7 @@ public final class DeltaImpl extends GraphImpl implements Delta {
public DeltaImpl(Set<NodeSource> baseSources, Iterable<NodeSource> deletedSources) {
super(Containers.MEMORY_CONTAINER_FACTORY);
addIndex(new SubclassesIndex(Containers.MEMORY_CONTAINER_FACTORY));
addIndex(new ClassShortNameIndex(Containers.MEMORY_CONTAINER_FACTORY));
myBaseSources = Collections.unmodifiableSet(baseSources);
myDeletedSources = Collections.unmodifiableSet(Iterators.collect(deletedSources, new HashSet<>()));
}

View File

@@ -1,10 +1,10 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.dependency.*;
import org.jetbrains.jps.dependency.diff.DiffCapable;
import org.jetbrains.jps.dependency.java.ClassShortNameIndex;
import org.jetbrains.jps.dependency.java.JavaDifferentiateStrategy;
import org.jetbrains.jps.dependency.java.SubclassesIndex;
import org.jetbrains.jps.javac.Iterators;
@@ -21,6 +21,7 @@ public final class DependencyGraphImpl extends GraphImpl implements DependencyGr
public DependencyGraphImpl(MapletFactory containerFactory) {
super(containerFactory);
addIndex(new SubclassesIndex(containerFactory));
addIndex(new ClassShortNameIndex(containerFactory));
}
@Override
@@ -207,15 +208,14 @@ public final class DependencyGraphImpl extends GraphImpl implements DependencyGr
}
}
Set<ReferenceID> deltaNodes = Iterators.collect(Iterators.map(Iterators.flat(Iterators.map(delta.getSources(), s -> delta.getNodes(s))), node -> node.getReferenceID()), new HashSet<>());
var updatedNodes = Iterators.collect(Iterators.flat(Iterators.map(delta.getSources(), s -> getNodes(s))), new HashSet<>());
for (BackDependencyIndex index : getIndices()) {
BackDependencyIndex deltaIndex = delta.getIndex(index.getName());
assert deltaIndex != null;
index.integrate(diffResult.getDeletedNodes(), updatedNodes, Iterators.map(deltaNodes, id -> Pair.create(id, deltaIndex.getDependencies(id))));
index.integrate(diffResult.getDeletedNodes(), updatedNodes, deltaIndex);
}
var deltaNodes = Iterators.map(Iterators.flat(Iterators.map(delta.getSources(), s -> delta.getNodes(s))), node -> node.getReferenceID());
for (ReferenceID nodeID : deltaNodes) {
Set<NodeSource> sources = Iterators.collect(myNodeToSourcesMap.get(nodeID), new HashSet<>());
sources.removeAll(delta.getBaseSources());

View File

@@ -22,6 +22,7 @@ import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
public final class PersistentSetMultiMaplet<K extends SerializableGraphElement, V extends SerializableGraphElement>
@@ -30,8 +31,8 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
private static final Collection<?> NULL_COLLECTION = Collections.emptyList();
private static final int CACHE_SIZE = 128;
private final PersistentHashMap<K, Collection<V>> map;
private final LoadingCache<K, Collection<V>> cache;
private final PersistentHashMap<K, Collection<V>> myMap;
private final LoadingCache<K, Collection<V>> myCache;
public PersistentSetMultiMaplet(Path mapFile) {
try {
@@ -39,15 +40,15 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
KeyDescriptor<K> keyDescriptor = NodeKeyDescriptorImpl.getInstance();
DataExternalizer<Collection<V>> valueExternalizer =
new CollectionDataExternalizer<>(NodeKeyDescriptorImpl.getInstance(), fileCollectionFactory);
map = new PersistentHashMap<>(mapFile, keyDescriptor, valueExternalizer);
myMap = new PersistentHashMap<>(mapFile, keyDescriptor, valueExternalizer);
}
catch (IOException e) {
throw new RuntimeException(e);
}
cache = Caffeine.newBuilder().maximumSize(CACHE_SIZE).build(key -> {
myCache = Caffeine.newBuilder().maximumSize(CACHE_SIZE).build(key -> {
try {
Collection<V> collection = map.get(key);
Collection<V> collection = myMap.get(key);
//noinspection unchecked
return collection == null ? (Collection<V>)NULL_COLLECTION : collection;
}
@@ -60,7 +61,7 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public boolean containsKey(K key) {
try {
return map.containsMapping(key);
return myMap.containsMapping(key);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
@@ -69,15 +70,21 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public @Nullable Iterable<V> get(K key) {
final Collection<V> collection = cache.get(key);
final Collection<V> collection = myCache.get(key);
return collection == NULL_COLLECTION ? null : collection;
}
@Override
public void put(K key, Iterable<? extends V> values) {
try {
cache.invalidate(key);
map.put(key, Iterators.collect(values, new HashSet<>()));
myCache.invalidate(key);
Set<V> data = Iterators.collect(values, new HashSet<>());
if (data.isEmpty()) {
myMap.remove(key);
}
else {
myMap.put(key, data);
}
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
@@ -87,8 +94,8 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public void remove(K key) {
try {
cache.invalidate(key);
map.remove(key);
myCache.invalidate(key);
myMap.remove(key);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
@@ -97,11 +104,19 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public void appendValue(K key, V value) {
appendValues(key, Collections.singleton(value));
}
@Override
public void appendValues(K key, Iterable<? extends V> values) {
try {
map.appendData(key, new AppendablePersistentMap.ValueDataAppender() {
myMap.appendData(key, new AppendablePersistentMap.ValueDataAppender() {
@Override
public void append(final @NotNull DataOutput out) throws IOException {
NodeKeyDescriptorImpl.getInstance().save(out, value);
var descriptor = NodeKeyDescriptorImpl.getInstance();
for (V value : values) {
descriptor.save(out, value);
}
}
});
}
@@ -112,16 +127,25 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public void removeValue(K key, V value) {
removeValues(key, Collections.singleton(value));
}
@Override
public void removeValues(K key, Iterable<? extends V> values) {
try {
final Collection<V> collection = cache.get(key);
if (collection != NULL_COLLECTION) {
if (collection.remove(value)) {
cache.invalidate(key);
final Collection<V> collection = myCache.get(key);
if (!collection.isEmpty()) {
boolean changes = false;
for (V value : values) {
changes |= collection.remove(value);
}
if (changes) {
myCache.invalidate(key);
if (collection.isEmpty()) {
map.remove(key);
myMap.remove(key);
}
else {
map.put(key, collection);
myMap.put(key, collection);
}
}
}
@@ -134,7 +158,7 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
@Override
public Iterable<K> getKeys() {
try {
return map.getAllKeysWithExistingMapping();
return myMap.getAllKeysWithExistingMapping();
}
catch (IOException e) {
throw new RuntimeException(e);
@@ -143,8 +167,8 @@ public final class PersistentSetMultiMaplet<K extends SerializableGraphElement,
public void close() {
try {
cache.invalidateAll();
map.close();
myCache.invalidateAll();
myMap.close();
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);

View File

@@ -498,15 +498,17 @@ public final class SerializerUtil {
}
static FieldAssignUsage readFieldAssignUsage(DataInput in) throws IOException {
FieldUsage fieldUsage = readFieldUsage(in);
return (FieldAssignUsage)fieldUsage;
String owner = in.readUTF();
String name = in.readUTF();
String descriptor = in.readUTF();
return new FieldAssignUsage(owner, name, descriptor);
}
static FieldUsage readFieldUsage(DataInput in) throws IOException {
String refId = in.readUTF();
String owner = in.readUTF();
String name = in.readUTF();
String descriptor = in.readUTF();
return new FieldUsage(refId, name, descriptor);
return new FieldUsage(owner, name, descriptor);
}
static JvmClass readJvmClass(DataInput in) throws IOException {

View File

@@ -0,0 +1,30 @@
// Copyright 2000-2023 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.MapletFactory;
import org.jetbrains.jps.dependency.Node;
import org.jetbrains.jps.dependency.ReferenceID;
import org.jetbrains.jps.dependency.impl.BackDependencyIndexImpl;
import org.jetbrains.jps.javac.Iterators;
import java.util.Collections;
public final class ClassShortNameIndex extends BackDependencyIndexImpl {
public static final String NAME = "class-short-names";
public ClassShortNameIndex(@NotNull MapletFactory cFactory) {
super(NAME, cFactory);
}
@Override
public Iterable<ReferenceID> getIndexedDependencies(@NotNull Node<?, ?> node) {
if (node instanceof JvmClass) {
JvmClass cls = (JvmClass)node;
if (!cls.isAnonymous() && !cls.isLocal()) {
return Iterators.asIterable(new JvmNodeReferenceID(cls.getShortName()));
}
}
return Collections.emptyList();
}
}

View File

@@ -37,23 +37,36 @@ public final class JavaDifferentiateStrategy implements DifferentiateStrategy {
Graph.getNodesOfType(nodesBefore, JvmClass.class), Graph.getNodesOfType(nodesAfter, JvmClass.class)
);
debug("Processing removed classes:");
for (JvmClass removed : classesDiff.removed()) {
if (!processRemovedClass(context, removed, future, present)) {
return false;
debug("Adding usages of class " + removed.getName());
context.affectUsage(new ClassUsage(removed.getName()));
}
debug("End of removed classes processing.");
debug("Processing added classes:");
try {
for (JvmClass added : classesDiff.added()) {
if (!processAddedClass(context, added, future, present)) {
return false;
}
}
}
for (JvmClass added : classesDiff.added()) {
if (!processAddedClass(context, added, future, present)) {
return false;
}
finally {
debug("End of added classes processing.");
}
for (Difference.Change<JvmClass, JvmClass.Diff> change : classesDiff.changed()) {
if (!processChangedClass(context, change, future, present)) {
return false;
debug("Processing changed classes:");
try {
for (Difference.Change<JvmClass, JvmClass.Diff> change : classesDiff.changed()) {
if (!processChangedClass(context, change, future, present)) {
return false;
}
}
}
finally {
debug("End of changed classes processing");
}
Difference.Specifier<JvmModule, JvmModule.Diff> modulesDiff = Difference.deepDiff(
Graph.getNodesOfType(nodesBefore, JvmModule.class), Graph.getNodesOfType(nodesAfter, JvmModule.class)
@@ -80,13 +93,21 @@ public final class JavaDifferentiateStrategy implements DifferentiateStrategy {
return true;
}
public boolean processRemovedClass(DifferentiateContext context, JvmClass removedClass, Utils future, Utils present) {
// todo
return true;
}
public boolean processAddedClass(DifferentiateContext context, JvmClass addedClass, Utils future, Utils present) {
// todo
// todo: consider if we need to perform a duplucate-class check, when the newly added class is of the same name with the existing one in the same module chunk
if (!addedClass.isAnonymous() && !addedClass.isLocal()) {
BackDependencyIndex index = context.getGraph().getIndex(ClassShortNameIndex.NAME);
if (index != null) {
// affecting dependencies on all other classes with the same short name
Set<ReferenceID> affectedNodes = Iterators.collect(index.getDependencies(new JvmNodeReferenceID(addedClass.getShortName())), new HashSet<>());
affectedNodes.add(addedClass.getReferenceID());
for (ReferenceID id : affectedNodes) {
debug("Adding usages of class with the same short name: " + id);
context.affectUsage(new AffectionScopeMetaUsage(id));
}
context.affectUsage((n, u) -> affectedNodes.contains(u.getElementOwner()));
}
}
return true;
}
@@ -116,7 +137,7 @@ public final class JavaDifferentiateStrategy implements DifferentiateStrategy {
parents.removeAll(Iterators.collect(future.allSupertypes(changedClass.getReferenceID()), new HashSet<>()));
for (JvmNodeReferenceID parent : parents) {
debug("Affecting usages in generic type parameter bounds of class: " + parent);
context.affectUsage(new ClassAsGenericBoundUsage(parent.getNodeName())); // todo: need support of usage constraint?
context.affectUsage(new ClassAsGenericBoundUsage(parent.getNodeName())); // todo: need support of file-filter usage constraint?
}
}
}
@@ -222,7 +243,7 @@ public final class JavaDifferentiateStrategy implements DifferentiateStrategy {
return true;
}
private <T> boolean processMethodChanges(DifferentiateContext context, Difference.Change<JvmClass, JvmClass.Diff> classChange, Utils future, Utils present) {
private boolean processMethodChanges(DifferentiateContext context, Difference.Change<JvmClass, JvmClass.Diff> classChange, Utils future, Utils present) {
JvmClass changedClass = classChange.getPast();
if (changedClass.isAnnotation()) {
debug("Class is annotation, skipping method analysis");

View File

@@ -45,6 +45,12 @@ public final class JvmClass extends JVMClassNode<JvmClass, JvmClass.Diff> {
return getPackageName(getName());
}
public @NotNull String getShortName() {
String jvmClassName = getName();
int index = jvmClassName.lastIndexOf('/');
return index >= 0? jvmClassName.substring(index + 1) : jvmClassName;
}
@NotNull
public static String getPackageName(@NotNull String jvmClassName) {
int index = jvmClassName.lastIndexOf('/');