From 9096fada47231c1e6a5a52e1baefb5f3f0d3f5c0 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 24 Jan 2024 18:55:36 +0100 Subject: [PATCH 1/6] Include `transient` when retrieving field modifiers --- japicmp/src/main/java/japicmp/model/JApiField.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/japicmp/src/main/java/japicmp/model/JApiField.java b/japicmp/src/main/java/japicmp/model/JApiField.java index 0ca2966c2..c8de409ec 100644 --- a/japicmp/src/main/java/japicmp/model/JApiField.java +++ b/japicmp/src/main/java/japicmp/model/JApiField.java @@ -270,7 +270,7 @@ public Optional getNewFieldOptional() { @XmlElementWrapper(name = "modifiers") @XmlElement(name = "modifier") public List>>> getModifiers() { - return Arrays.asList(this.accessModifier, this.staticModifier, this.finalModifier, this.syntheticModifier); + return Arrays.asList(this.accessModifier, this.staticModifier, this.finalModifier, this.transientModifier, this.syntheticModifier); } @XmlTransient From 8226e106088b37546e2f76b833d843d0927c8fd3 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 24 Jan 2024 18:55:49 +0100 Subject: [PATCH 2/6] Treat changes to the `transient` modifier of a field as PATCH changes --- .../main/java/japicmp/compat/CompatibilityChanges.java | 8 ++++++-- .../main/java/japicmp/model/JApiCompatibilityChange.java | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java index 12f586d8f..b0bcba63f 100755 --- a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java +++ b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java @@ -178,8 +178,12 @@ public Integer callback(JApiClass superclass, Map classMap, J addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NO_LONGER_STATIC); } } - if (isNotPrivate(field) && field.getType().hasChanged()) { - addCompatibilityChange(field, JApiCompatibilityChange.FIELD_TYPE_CHANGED); + // section 13.4.11 of "Java Language Specification" SE7 + if (field.getTransientModifier().hasChangedFromTo(TransientModifier.NON_TRANSIENT, TransientModifier.TRANSIENT)) { + addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NOW_TRANSIENT); + } + if (field.getTransientModifier().hasChangedFromTo(TransientModifier.TRANSIENT, TransientModifier.NON_TRANSIENT)) { + addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NO_LONGER_TRANSIENT); } checkIfAnnotationDeprecatedAdded(field); checkIfFieldGenericsChanged(field); diff --git a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java index b62071a09..2991b4c70 100644 --- a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java +++ b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java @@ -51,7 +51,9 @@ public enum JApiCompatibilityChange { FIELD_STATIC_AND_OVERRIDES_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_LESS_ACCESSIBLE_THAN_IN_SUPERCLASS(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_NOW_FINAL(false, false, JApiSemanticVersionLevel.MAJOR), + FIELD_NOW_TRANSIENT(true, true, JApiSemanticVersionLevel.PATCH), FIELD_NOW_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), + FIELD_NO_LONGER_TRANSIENT(true, true, JApiSemanticVersionLevel.PATCH), FIELD_NO_LONGER_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_TYPE_CHANGED(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_REMOVED(false, false, JApiSemanticVersionLevel.MAJOR), From 2e5d68219c1bba46513647479ae664b8b751d0da Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 24 Jan 2024 18:55:55 +0100 Subject: [PATCH 3/6] Update standard output generator to take into account `transient` --- .../java/japicmp/output/stdout/StdoutOutputGenerator.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java index 4e9c43366..6efddc9ee 100644 --- a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java @@ -426,7 +426,8 @@ private void processFieldChanges(StringBuilder sb, JApiClass jApiClass) { for (JApiField jApiField : jApiFields) { sb.append(tabs(1)).append(signs(jApiField)).append(" ").append(jApiField.getChangeStatus()).append(" FIELD: ") .append(accessModifierAsString(jApiField)).append(staticModifierAsString(jApiField)) - .append(finalModifierAsString(jApiField)).append(syntheticModifierAsString(jApiField)) + .append(finalModifierAsString(jApiField)).append(transientModifierAsString(jApiField)) + .append(syntheticModifierAsString(jApiField)) .append(fieldTypeChangeAsString(jApiField)); appendGenericTypes(sb, jApiField); sb.append(" ").append(jApiField.getName()).append('\n'); @@ -444,6 +445,11 @@ private String finalModifierAsString(JApiHasFinalModifier hasFinalModifier) { return modifierAsString(modifier, FinalModifier.NON_FINAL); } + private String transientModifierAsString(JApiHasTransientModifier hasTransientModifier) { + JApiModifier modifier = hasTransientModifier.getTransientModifier(); + return modifierAsString(modifier, TransientModifier.NON_TRANSIENT); + } + private String staticModifierAsString(JApiHasStaticModifier hasStaticModifier) { JApiModifier modifier = hasStaticModifier.getStaticModifier(); return modifierAsString(modifier, StaticModifier.NON_STATIC); From d2805b395fc6bac7e052d6a917d077e81c7c7058 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 24 Jan 2024 18:56:03 +0100 Subject: [PATCH 4/6] Add tests for new `transient` compatibility changes --- .../compat/CompatibilityChangesTest.java | 60 +++++++++++++++++++ .../java/japicmp/util/CtFieldBuilder.java | 5 ++ 2 files changed, 65 insertions(+) diff --git a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java index ed0af88e1..c055f2205 100755 --- a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java +++ b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java @@ -797,6 +797,36 @@ public List createNewClasses(ClassPool classPool) throws Exception { assertThat(jApiField.isBinaryCompatible(), is(false)); } + @Test + public void testFieldNowTransient() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + options.setAccessModifier(AccessModifier.PRIVATE); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().transientAccess().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + }); + JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test"); + assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); + assertThat(jApiClass.isBinaryCompatible(), is(true)); + assertThat(jApiClass.isSourceCompatible(), is(true)); + JApiField jApiField = getJApiField(jApiClass.getFields(), "field"); + assertThat(jApiField.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.FIELD_NOW_TRANSIENT)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + } + @Test public void testFieldNowStatic() throws Exception { JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); @@ -825,6 +855,36 @@ public List createNewClasses(ClassPool classPool) throws Exception { assertThat(jApiField.isBinaryCompatible(), is(false)); } + @Test + public void testFieldNoLongerTransient() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + options.setAccessModifier(AccessModifier.PRIVATE); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().transientAccess().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + }); + JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test"); + assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); + assertThat(jApiClass.isBinaryCompatible(), is(true)); + assertThat(jApiClass.isSourceCompatible(), is(true)); + JApiField jApiField = getJApiField(jApiClass.getFields(), "field"); + assertThat(jApiField.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.FIELD_NO_LONGER_TRANSIENT)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + } + @Test public void testFieldNoLongerStatic() throws Exception { JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); diff --git a/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java b/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java index c4784c75f..a16d837cc 100644 --- a/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java +++ b/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java @@ -94,6 +94,11 @@ public CtFieldBuilder protectedAccess() { return this; } + public CtFieldBuilder transientAccess() { + this.modifier = this.modifier | Modifier.TRANSIENT; + return this; + } + public CtFieldBuilder finalAccess() { this.modifier = this.modifier | Modifier.FINAL; return this; From 358fbd71fedd76bd62d4a675c92c8823be91e9a5 Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Wed, 24 Jan 2024 21:54:11 +0100 Subject: [PATCH 5/6] Update XSLT to take into account `transient` --- japicmp/src/main/resources/html.xslt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/japicmp/src/main/resources/html.xslt b/japicmp/src/main/resources/html.xslt index ce15221bb..1d38c9f74 100644 --- a/japicmp/src/main/resources/html.xslt +++ b/japicmp/src/main/resources/html.xslt @@ -803,6 +803,9 @@ not_final + + not_transient + not_static From f819fd64a78b215cf99ea0600aae0630286486da Mon Sep 17 00:00:00 2001 From: Guillermo Calvo Date: Thu, 15 Feb 2024 18:19:08 +0100 Subject: [PATCH 6/6] Restore code that was removed by mistake --- japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java index b0bcba63f..f137f16cf 100755 --- a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java +++ b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java @@ -185,6 +185,9 @@ public Integer callback(JApiClass superclass, Map classMap, J if (field.getTransientModifier().hasChangedFromTo(TransientModifier.TRANSIENT, TransientModifier.NON_TRANSIENT)) { addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NO_LONGER_TRANSIENT); } + if (isNotPrivate(field) && field.getType().hasChanged()) { + addCompatibilityChange(field, JApiCompatibilityChange.FIELD_TYPE_CHANGED); + } checkIfAnnotationDeprecatedAdded(field); checkIfFieldGenericsChanged(field); }