incremental build: recompile usages of annotations containing TYPE_USE target on any target set change (IDEA-180438)

This commit is contained in:
Eugene Zhuravlev
2017-10-26 16:46:19 +02:00
parent b1353d4aab
commit 4d80dd88ef
19 changed files with 171 additions and 34 deletions
@@ -0,0 +1,14 @@
Cleaning output files:
out/production/AddAnnotationTargetTypeUse/A.class
End of files
Compiling files:
src/A.java
End of files
Cleaning output files:
out/production/AddAnnotationTargetTypeUse/C.class
out/production/AddAnnotationTargetTypeUse/C2.class
End of files
Compiling files:
src/C.java
src/C2.java
End of files
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, TYPE_USE})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, TYPE_USE})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,3 @@
@A(10)
public class C {
}
@@ -0,0 +1,5 @@
public class C2 {
@A(30)
int field;
}
@@ -0,0 +1,14 @@
Cleaning output files:
out/production/AddTypeUseAnnotationTarget/A.class
End of files
Compiling files:
src/A.java
End of files
Cleaning output files:
out/production/AddTypeUseAnnotationTarget/C.class
out/production/AddTypeUseAnnotationTarget/C2.class
End of files
Compiling files:
src/C.java
src/C2.java
End of files
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, TYPE_USE})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,3 @@
@A(10)
public class C {
}
@@ -0,0 +1,5 @@
public class C2 {
@A(30)
int field;
}
@@ -0,0 +1,14 @@
Cleaning output files:
out/production/RemoveTypeUseAnnotationTarget/A.class
End of files
Compiling files:
src/A.java
End of files
Cleaning output files:
out/production/RemoveTypeUseAnnotationTarget/C.class
out/production/RemoveTypeUseAnnotationTarget/C2.class
End of files
Compiling files:
src/C.java
src/C2.java
End of files
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,7 @@
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, TYPE_USE})
public @interface A {
int value() default 3;
}
@@ -0,0 +1,3 @@
@A(10)
public class C {
}
@@ -0,0 +1,5 @@
public class C2 {
@A(30)
int field;
}
@@ -72,6 +72,11 @@ public class ClassRepr extends ClassFileRepr {
return myRetentionPolicy;
}
public Set<ElemType> getAnnotationTargets() {
final Set<ElemType> targets = myAnnotationTargets;
return targets != null ? Collections.unmodifiableSet(targets) : Collections.emptySet();
}
public boolean isInterface() {
return (access & Opcodes.ACC_INTERFACE) != 0;
}
@@ -94,6 +99,8 @@ public class ClassRepr extends ClassFileRepr {
public abstract boolean extendsAdded();
public abstract boolean targetAttributeCategoryMightChange();
public boolean no() {
return base() == NONE &&
interfaces().unchanged() &&
@@ -155,6 +162,17 @@ public class ClassRepr extends ClassFileRepr {
(myRetentionPolicy == pastClass.myRetentionPolicy));
}
@Override
public boolean targetAttributeCategoryMightChange() {
final Specifier<ElemType, Difference> targetsDiff = targets();
if (!targetsDiff.unchanged()) {
return targetsDiff.added().contains(ElemType.TYPE_USE) ||
targetsDiff.removed().contains(ElemType.TYPE_USE) ||
pastClass.getAnnotationTargets().contains(ElemType.TYPE_USE);
}
return false;
}
@Override
public int base() {
return d;
@@ -1942,6 +1942,10 @@ public class Mappings {
debug("Retention policy change detected, adding class usage to affected usages");
state.myAffectedUsages.add(changedClass.createUsage());
}
else if (diff.targetAttributeCategoryMightChange()) {
debug("Annotation's attribute category in bytecode might be affected because of TYPE_USE target, adding class usage to affected usages");
state.myAffectedUsages.add(changedClass.createUsage());
}
else {
final Collection<ElemType> removedtargets = diff.targets().removed();
@@ -1955,9 +1959,9 @@ public class Mappings {
if (!removedtargets.isEmpty()) {
debug("Removed some annotation targets, adding annotation query");
final UsageRepr.AnnotationUsage annotationUsage = (UsageRepr.AnnotationUsage)UsageRepr
.createAnnotationUsage(myContext, TypeRepr.createClassType(myContext, changedClass.name), null, EnumSet.copyOf(removedtargets));
state.myAnnotationQuery.add(annotationUsage);
state.myAnnotationQuery.add((UsageRepr.AnnotationUsage)UsageRepr.createAnnotationUsage(
myContext, TypeRepr.createClassType(myContext, changedClass.name), null, EnumSet.copyOf(removedtargets)
));
}
for (final MethodRepr m : diff.methods().added()) {
@@ -2186,8 +2190,9 @@ public class Mappings {
for (UsageRepr.Usage usage : depUsages) {
if (usage instanceof UsageRepr.AnnotationUsage) {
final UsageRepr.AnnotationUsage annotationUsage = (UsageRepr.AnnotationUsage)usage;
for (final UsageRepr.AnnotationUsage query : state.myAnnotationQuery) {
if (query.satisfies(usage)) {
if (query.satisfies(annotationUsage)) {
debug("Added file due to annotation query");
myAffectedFiles.add(depFile);
return;
@@ -546,38 +546,32 @@ class UsageRepr {
final TIntHashSet myUsedArguments;
final Set<ElemType> myUsedTargets;
public boolean satisfies(final Usage usage) {
if (usage instanceof AnnotationUsage) {
final AnnotationUsage annotationUsage = (AnnotationUsage)usage;
if (!myType.equals(annotationUsage.myType)) {
return false;
}
boolean argumentsSatisfy = false;
if (myUsedArguments != null) {
final TIntHashSet arguments = new TIntHashSet(myUsedArguments.toArray());
arguments.removeAll(annotationUsage.myUsedArguments.toArray());
argumentsSatisfy = !arguments.isEmpty();
}
boolean targetsSatisfy = false;
if (myUsedTargets != null) {
final Collection<ElemType> targets = EnumSet.copyOf(myUsedTargets);
targets.retainAll(annotationUsage.myUsedTargets);
targetsSatisfy = !targets.isEmpty();
}
return argumentsSatisfy || targetsSatisfy;
public boolean satisfies(final AnnotationUsage annotationUsage) {
if (!myType.equals(annotationUsage.myType)) {
return false;
}
return false;
boolean argumentsSatisfy = false;
if (myUsedArguments != null) {
final TIntHashSet arguments = new TIntHashSet(myUsedArguments.toArray());
arguments.removeAll(annotationUsage.myUsedArguments.toArray());
argumentsSatisfy = !arguments.isEmpty();
}
boolean targetsSatisfy = false;
if (myUsedTargets != null) {
final Collection<ElemType> targets = EnumSet.copyOf(myUsedTargets);
targets.retainAll(annotationUsage.myUsedTargets);
targetsSatisfy = !targets.isEmpty();
}
return argumentsSatisfy || targetsSatisfy;
}
private AnnotationUsage(final TypeRepr.ClassType type, final TIntHashSet usedArguments, final Set<ElemType> targets) {
@@ -28,6 +28,14 @@ public class AnnotationTest extends IncrementalTestCase {
doTest();
}
public void testAddAnnotationTargetTypeUse() {
doTest();
}
public void testAddTypeUseAnnotationTarget() {
doTest();
}
public void testAddAnnotationTypeMemberWithDefaultValue() {
doTest();
}
@@ -97,6 +105,10 @@ public class AnnotationTest extends IncrementalTestCase {
doTest();
}
public void testRemoveTypeUseAnnotationTarget() {
doTest();
}
public void testRemoveAnnotationTypeMember() {
doTest();
}