From b8f5bde005bb8f95fff603de78280b91df1df22a Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 25 Jul 2016 15:10:59 +0200 Subject: [PATCH] IDEA-158203 Allow JPS plugins to be notified when annotations on method return type or parameter types are changed --- .../java/dependencyView/ClassRepr.java | 16 ++-- .../dependencyView/ClassfileAnalyzer.java | 11 ++- .../java/dependencyView/Difference.java | 22 ++--- .../java/dependencyView/FieldRepr.java | 3 +- .../java/dependencyView/Mappings.java | 82 +++++++++++++++---- .../MemberAnnotationsChangeTracker.java | 36 ++++++++ .../java/dependencyView/MethodRepr.java | 51 ++++++++---- .../java/dependencyView/ProtoMember.java | 29 +++++-- 8 files changed, 188 insertions(+), 62 deletions(-) create mode 100644 jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MemberAnnotationsChangeTracker.java diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java index 2cbfe0592f3a..91aae557b0c5 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassRepr.java @@ -88,13 +88,13 @@ public class ClassRepr extends Proto { } public abstract static class Diff extends Difference { - public abstract Specifier interfaces(); + public abstract Specifier interfaces(); - public abstract Specifier fields(); + public abstract Specifier fields(); - public abstract Specifier methods(); + public abstract Specifier methods(); - public abstract Specifier targets(); + public abstract Specifier targets(); public abstract boolean retentionChanged(); @@ -150,22 +150,22 @@ public class ClassRepr extends Proto { } @Override - public Difference.Specifier interfaces() { + public Difference.Specifier interfaces() { return Difference.make(pastClass.myInterfaces, myInterfaces); } @Override - public Difference.Specifier fields() { + public Difference.Specifier fields() { return Difference.make(pastClass.myFields, myFields); } @Override - public Difference.Specifier methods() { + public Difference.Specifier methods() { return Difference.make(pastClass.myMethods, myMethods); } @Override - public Specifier targets() { + public Specifier targets() { return Difference.make(pastClass.myAnnotationTargets, myAnnotationTargets); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java index f324ed416012..7fa64aff47b4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ClassfileAnalyzer.java @@ -29,7 +29,10 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Array; -import java.util.*; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * @author: db @@ -385,7 +388,7 @@ class ClassfileAnalyzer { processSignature(signature); return new FieldVisitor(ASM_API_VERSION) { - final List annotations = new SmartList(); + final Set annotations = new THashSet(); @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { @@ -413,8 +416,8 @@ class ClassfileAnalyzer { @Override public MethodVisitor visitMethod(final int access, final String n, final String desc, final String signature, final String[] exceptions) { final Ref defaultValue = Ref.create(); - final Collection annotations = new SmartList(); - final Collection paramAnnotations = new SmartList(); + final Set annotations = new THashSet(); + final Set paramAnnotations = new THashSet(); processSignature(signature); return new MethodVisitor(ASM_API_VERSION) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java index 4004474e8563..25fe0a4ec631 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Difference.java @@ -43,21 +43,22 @@ abstract class Difference { public static final int SIGNATURE = 8; public static final int SUPERCLASS = 16; public static final int USAGES = 32; + public static final int ANNOTATIONS = 64; - public interface Specifier { + public interface Specifier { Collection added(); Collection removed(); - Collection> changed(); + Collection> changed(); boolean unchanged(); } - public static Specifier make(final Set past, final Set now) { + public static Specifier make(final Set past, final Set now) { if (past == null) { final Collection _now = Collections.unmodifiableCollection(now); - return new Specifier() { + return new Specifier() { public Collection added() { return _now; } @@ -66,7 +67,7 @@ abstract class Difference { return Collections.emptyList(); } - public Collection> changed() { + public Collection> changed() { return Collections.emptyList(); } @@ -84,9 +85,9 @@ abstract class Difference { removed.removeAll(now); - final Set> changed; + final Set> changed; if (canContainChangedElements(past, now)) { - changed = new HashSet>(); + changed = new HashSet>(); final Set intersect = new HashSet(past); final Map nowMap = new HashMap(); @@ -101,7 +102,8 @@ abstract class Difference { for (T x : intersect) { final Proto px = (Proto)x; final Proto py = (Proto)nowMap.get(x); - final Difference diff = py.difference(px); + //noinspection unchecked + final D diff = (D)py.difference(px); if (!diff.no()) { changed.add(Pair.create(x, diff)); @@ -112,7 +114,7 @@ abstract class Difference { changed = Collections.emptySet(); } - return new Specifier() { + return new Specifier() { public Collection added() { return added; } @@ -121,7 +123,7 @@ abstract class Difference { return removed; } - public Collection> changed() { + public Collection> changed() { return changed; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java index 45b02a995306..ee3a4cadd536 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/FieldRepr.java @@ -21,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.util.List; import java.util.Set; /** @@ -39,7 +38,7 @@ class FieldRepr extends ProtoMember { final int descriptor, final int signature, @NotNull - final List annotations, final Object value) { + final Set annotations, final Object value) { super(access, signature, name, TypeRepr.getType(context, descriptor), annotations, value); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java index 431088265d7e..8dd8b0555ac4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/Mappings.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; import org.jetbrains.jps.incremental.storage.FileKeyDescriptor; +import org.jetbrains.jps.service.JpsServiceManager; import org.jetbrains.org.objectweb.asm.ClassReader; import org.jetbrains.org.objectweb.asm.Opcodes; @@ -954,6 +955,9 @@ public class Mappings { final boolean myEasyMode; // true means: no need to search for affected files, only preprocess data for integrate + private final Iterable myAnnotationChangeTracker = + JpsServiceManager.getInstance().getExtensions(MemberAnnotationsChangeTracker.class); + private class DelayedWorks { class Triple { final int owner; @@ -1055,9 +1059,9 @@ public class Mappings { final public Set myAnnotationQuery = new HashSet(); final public Map myUsageConstraints = new HashMap(); - final Difference.Specifier myClassDiff; + final Difference.Specifier myClassDiff; - private DiffState(Difference.Specifier classDiff) { + private DiffState(Difference.Specifier classDiff) { this.myClassDiff = classDiff; } } @@ -1395,7 +1399,7 @@ public class Mappings { } private void processChangedMethods(final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { - final Collection> changed = diff.methods().changed(); + final Collection> changed = diff.methods().changed(); if (changed.isEmpty()) { return; } @@ -1404,9 +1408,9 @@ public class Mappings { assert myFuture != null; assert myAffectedFiles != null; - for (final Pair mr : changed) { + for (final Pair mr : changed) { final MethodRepr m = mr.first; - final MethodRepr.Diff d = (MethodRepr.Diff)mr.second; + final MethodRepr.Diff d = mr.second; final boolean throwsChanged = !d.exceptions().unchanged(); debug("Method: ", m.name); @@ -1427,7 +1431,7 @@ public class Mappings { boolean affected = false; boolean constrained = false; - final Set usages = new HashSet(); + final Set usages = new THashSet(); if (d.packageLocalOn()) { debug("Method became package-private, affecting method usages outside the package"); @@ -1463,6 +1467,7 @@ public class Mappings { } state.myAffectedUsages.addAll(usages); + affected = true; } } else if ((d.base() & Difference.ACCESS) > 0) { @@ -1473,6 +1478,7 @@ public class Mappings { debug("Added static or private specifier or removed static specifier --- affecting method usages"); myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); + affected = true; } if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0) { @@ -1494,15 +1500,38 @@ public class Mappings { if (!affected) { myFuture.affectMethodUsages(m, propagated, m.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); + affected = true; } for (final UsageRepr.Usage usage : usages) { state.myUsageConstraints.put(usage, myFuture.new InheritanceConstraint(it.name)); } + constrained = true; } } } } + + if ((!affected || constrained) && (d.base() & Difference.ANNOTATIONS) > 0) { + final Difference.Specifier annotationsDiff = d.annotations(); + final Difference.Specifier paramAnnotationsDiff = d.parameterAnnotations(); + for (MemberAnnotationsChangeTracker extension : myAnnotationChangeTracker) { + final MemberAnnotationsChangeTracker.Action action = extension.methodAnnotationsChanged(m, annotationsDiff, paramAnnotationsDiff); + if (action == MemberAnnotationsChangeTracker.Action.RECOMPILE_USAGES) { + debug("Extension "+extension.getClass().getName()+" requested recompilation because of changes in annotations list --- affecting method usages"); + myFuture.affectMethodUsages(m, propagated, 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 + for (UsageRepr.Usage usage : usages) { + state.myUsageConstraints.remove(usage); + } + } + affected = true; + break; + } + } + } } } debug("End of changed methods processing"); @@ -1646,15 +1675,15 @@ public class Mappings { } private boolean processChangedFields(final DiffState state, final ClassRepr.Diff diff, final ClassRepr it) { - final Collection> changed = diff.fields().changed(); + final Collection> changed = diff.fields().changed(); if (changed.isEmpty()) { return true; } debug("Processing changed fields:"); assert myFuture != null; - for (final Pair f : changed) { - final Difference d = f.second; + for (final Pair f : changed) { + final FieldRepr.Diff d = f.second; final FieldRepr field = f.first; debug("Field: ", field.name); @@ -1684,11 +1713,13 @@ public class Mappings { if (d.base() != Difference.NONE) { final TIntHashSet propagated = myFuture.propagateFieldAccess(field.name, it.name); + boolean affected = false; 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); + affected = true; } else if ((d.base() & Difference.ACCESS) > 0) { if ((d.addedModifiers() & Opcodes.ACC_STATIC) > 0 || @@ -1698,10 +1729,10 @@ public class Mappings { 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); + affected = true; } else { - boolean affected = false; - final Set usages = new HashSet(); + final Set usages = new THashSet(); if ((d.addedModifiers() & Opcodes.ACC_FINAL) > 0) { debug("Added final modifier --- affecting field assign usages"); @@ -1715,6 +1746,7 @@ public class Mappings { if (!affected) { myFuture.affectFieldUsages(field, propagated, field.createUsage(myContext, it.name), usages, state.myDependants); state.myAffectedUsages.addAll(usages); + affected = true; } for (final UsageRepr.Usage usage : usages) { @@ -1728,6 +1760,26 @@ public class Mappings { } } } + + if (!affected && (d.base() & Difference.ANNOTATIONS) > 0) { + final Difference.Specifier annotationsDiff = d.annotations(); + for (MemberAnnotationsChangeTracker extension : myAnnotationChangeTracker) { + final MemberAnnotationsChangeTracker.Action action = extension.fieldAnnotationsChanged(field, annotationsDiff); + if (action == MemberAnnotationsChangeTracker.Action.RECOMPILE_USAGES) { + debug("Extension "+extension.getClass().getName()+" requested recompilation because of changes in annotations list --- affecting field usages"); + final Set usages = new THashSet(); + myFuture.affectFieldUsages(field, propagated, 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) { + state.myUsageConstraints.remove(usage); + } + affected = true; + break; + } + } + } + } } debug("End of changed fields processing"); @@ -1736,7 +1788,7 @@ public class Mappings { } private boolean processChangedClasses(final DiffState state) { - final Collection> changedClasses = state.myClassDiff.changed(); + final Collection> changedClasses = state.myClassDiff.changed(); if (!changedClasses.isEmpty()) { debug("Processing changed classes:"); assert myFuture != null; @@ -1744,9 +1796,9 @@ public class Mappings { final Util.FileFilterConstraint fileFilterConstraint = myFilter != null? myPresent.new FileFilterConstraint(myFilter) : null; - for (final Pair changed : changedClasses) { + for (final Pair changed : changedClasses) { final ClassRepr changedClass = changed.first; - final ClassRepr.Diff diff = (ClassRepr.Diff)changed.second; + final ClassRepr.Diff diff = changed.second; myDelta.addChangedClass(changedClass.name); @@ -2154,7 +2206,7 @@ public class Mappings { final File fileName = compiledFile.myFileName; final Set classes = compiledFile.myFileClasses; final Set pastClasses = (Set)mySourceFileToClasses.get(fileName); - final DiffState state = new DiffState(Difference.make(pastClasses, classes)); + final DiffState state = new DiffState(Difference.make(pastClasses, classes)); if (!processChangedClasses(state)) { if (!myEasyMode) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MemberAnnotationsChangeTracker.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MemberAnnotationsChangeTracker.java new file mode 100644 index 000000000000..f6dc79af1916 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MemberAnnotationsChangeTracker.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jps.builders.java.dependencyView; + +/** + * @author Eugene Zhuravlev + * Date: 25-Jul-16 + */ +public abstract class MemberAnnotationsChangeTracker { + + public enum Action { + NO_ACTION, RECOMPILE_USAGES; + } + + public abstract Action methodAnnotationsChanged( + MethodRepr method, + Difference.Specifier annotationsDiff, + Difference.Specifier paramAnnotationsDiff + ); + + public abstract Action fieldAnnotationsChanged(FieldRepr field, Difference.Specifier annotationsDiff); + +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java index 73a155ef2f0f..fa993ebd86f7 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/MethodRepr.java @@ -15,7 +15,6 @@ */ package org.jetbrains.jps.builders.java.dependencyView; -import com.intellij.util.SmartList; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.DataInputOutputUtil; import gnu.trove.THashSet; @@ -27,7 +26,10 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.PrintStream; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Set; /** * @author: db @@ -39,12 +41,15 @@ class MethodRepr extends ProtoMember { boolean satisfy(MethodRepr m); } - public final Collection myParameterAnnotations; + public final Set myParameterAnnotations; public final TypeRepr.AbstractType[] myArgumentTypes; public final Set myExceptions; - public abstract class Diff extends Difference { - public abstract Specifier exceptions(); + public static abstract class Diff extends ProtoMember.Diff { + + public abstract Specifier parameterAnnotations(); + + public abstract Specifier exceptions(); public abstract boolean defaultAdded(); @@ -52,11 +57,24 @@ class MethodRepr extends ProtoMember { } @Override - public Difference difference(final Proto past) { + public Diff difference(final Proto past) { + final MethodRepr m = (MethodRepr)past; final Difference diff = super.difference(past); - final Difference.Specifier excs = Difference.make(((MethodRepr)past).myExceptions, myExceptions); + final Difference.Specifier excs = Difference.make(m.myExceptions, myExceptions); + final Difference.Specifier annotations = Difference.make(m.myAnnotations, myAnnotations); + final Difference.Specifier paramAnnotations = Difference.make(m.myParameterAnnotations, myParameterAnnotations); return new Diff() { + @Override + public Specifier annotations() { + return annotations; + } + + @Override + public Specifier parameterAnnotations() { + return paramAnnotations; + } + @Override public int addedModifiers() { return diff.addedModifiers(); @@ -69,27 +87,28 @@ class MethodRepr extends ProtoMember { @Override public boolean no() { - return base() == NONE && !defaultAdded() && !defaultRemoved() && excs.unchanged(); + return base() == NONE && !defaultAdded() && !defaultRemoved() && excs.unchanged() && paramAnnotations.unchanged(); } @Override public boolean defaultAdded() { - return hasValue() && !((MethodRepr)past).hasValue(); + return hasValue() && !m.hasValue(); } @Override public boolean defaultRemoved() { - return !hasValue() && ((MethodRepr)past).hasValue(); + return !hasValue() && m.hasValue(); } @Override - public Specifier exceptions() { + public Specifier exceptions() { return excs; } @Override public int base() { - return diff.base(); + final int base = diff.base(); + return paramAnnotations.unchanged()? base : base | Difference.ANNOTATIONS; } @Override @@ -99,7 +118,7 @@ class MethodRepr extends ProtoMember { @Override public boolean hadValue() { - return ((MethodRepr)past).hasValue(); + return m.hasValue(); } @Override @@ -128,7 +147,7 @@ class MethodRepr extends ProtoMember { final int name, final int signature, final String descriptor, - final Collection annotations, Collection parameterAnnotations, final String[] exceptions, + final Set annotations, Set parameterAnnotations, final String[] exceptions, final Object defaultValue) { super(accessFlags, signature, name, TypeRepr.getType(context, Type.getReturnType(descriptor)), annotations, defaultValue); myParameterAnnotations = parameterAnnotations; @@ -149,7 +168,7 @@ class MethodRepr extends ProtoMember { myExceptions = (Set)RW.read(externalizer, new THashSet(0), in); final DataExternalizer clsTypeExternalizer = TypeRepr.classTypeExternalizer(context); - myParameterAnnotations = RW.read(new DataExternalizer() { + myParameterAnnotations = (Set)RW.read(new DataExternalizer() { @Override public void save(@NotNull DataOutput out, ParamAnnotation value) throws IOException { value.save(out); @@ -158,7 +177,7 @@ class MethodRepr extends ProtoMember { public ParamAnnotation read(@NotNull DataInput in) throws IOException { return new ParamAnnotation(clsTypeExternalizer, in); } - }, new SmartList(), in); + }, new THashSet(), in); } catch (IOException e) { throw new BuildDataCorruptedException(e); diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java index ff999f6c7e2f..f930a559d6e4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/dependencyView/ProtoMember.java @@ -15,8 +15,8 @@ */ package org.jetbrains.jps.builders.java.dependencyView; -import com.intellij.util.SmartList; import com.intellij.util.io.DataInputOutputUtil; +import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.storage.BuildDataCorruptedException; @@ -27,7 +27,7 @@ import java.io.DataOutput; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Array; -import java.util.Collection; +import java.util.Set; /** * @author: db @@ -38,7 +38,7 @@ abstract class ProtoMember extends Proto { @NotNull public final TypeRepr.AbstractType myType; @NotNull - public final Collection myAnnotations; + public final Set myAnnotations; public final Object myValue; private static abstract class DataDescriptor { @@ -201,7 +201,7 @@ abstract class ProtoMember extends Proto { @NotNull final TypeRepr.AbstractType t, @NotNull - Collection annotations, + Set annotations, final Object value) { super(access, signature, name); myType = t; @@ -214,7 +214,7 @@ abstract class ProtoMember extends Proto { try { myType = TypeRepr.externalizer(context).read(in); myValue = loadTyped(in); - myAnnotations = RW.read(TypeRepr.classTypeExternalizer(context), new SmartList(), in); + myAnnotations = (Set)RW.read(TypeRepr.classTypeExternalizer(context), new THashSet(), in); } catch (IOException e) { throw new BuildDataCorruptedException(e); @@ -278,7 +278,11 @@ abstract class ProtoMember extends Proto { RW.save(myAnnotations, out); } - public Difference difference(final Proto past) { + public abstract static class Diff extends Difference { + public abstract Specifier annotations(); + } + + public Diff difference(final Proto past) { final ProtoMember m = (ProtoMember)past; final Difference diff = super.difference(past); int base = diff.base(); @@ -287,6 +291,12 @@ abstract class ProtoMember extends Proto { base |= Difference.TYPE; } + final Difference.Specifier annotations = Difference.make(m.myAnnotations, myAnnotations); + + if (!annotations.unchanged()) { + base |= Difference.ANNOTATIONS; + } + switch ((myValue == null ? 0 : 1) + (m.myValue == null ? 0 : 2)) { case 3: if (!myValue.equals(m.myValue)) { @@ -308,7 +318,12 @@ abstract class ProtoMember extends Proto { final int newBase = base; - return new Difference() { + return new Diff() { + @Override + public Specifier annotations() { + return annotations; + } + @Override public int base() { return newBase;