This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11775 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 961370ce436194cd97053715ef1dfed3eda9fb4d Author: Eric Milles <[email protected]> AuthorDate: Fri Nov 14 14:09:39 2025 -0600 GROOVY-11775: `ExpandoMetaClass`: prevent mixin metaclass duplication --- build.gradle | 1 + src/main/java/groovy/lang/ExpandoMetaClass.java | 9 ++++++-- .../groovy/reflection/MixinInMetaClass.java | 25 ++++++++++------------ 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index 8b0ba28ec5..65abf18929 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,7 @@ groovyCore { } bridgedClasses *supportClasses, + 'groovy.lang.ExpandoMetaClass', 'org.codehaus.groovy.classgen.Verifier', 'org.codehaus.groovy.ast.tools.GeneralUtils', 'org.codehaus.groovy.ast.stmt.TryCatchStatement', diff --git a/src/main/java/groovy/lang/ExpandoMetaClass.java b/src/main/java/groovy/lang/ExpandoMetaClass.java index d6e8d9ed0f..fa3c51d51f 100644 --- a/src/main/java/groovy/lang/ExpandoMetaClass.java +++ b/src/main/java/groovy/lang/ExpandoMetaClass.java @@ -448,8 +448,13 @@ public class ExpandoMetaClass extends MetaClassImpl implements GroovyObject { } } - public void addMixinClass(MixinInMetaClass mixin) { - mixinClasses.add(mixin); + @Deprecated + public void addMixinClass$$bridge(MixinInMetaClass mixin) { + addMixinClass(mixin); + } + + public boolean addMixinClass(MixinInMetaClass mixin) { + return mixinClasses.add(mixin); } public Object castToMixedType(Object obj, Class type) { diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java index 649fdce11e..b74ea2d502 100644 --- a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java +++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java @@ -38,9 +38,9 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Objects; import static java.util.Arrays.stream; +import static java.util.Objects.requireNonNull; public class MixinInMetaClass { @@ -50,13 +50,11 @@ public class MixinInMetaClass { private final Map<Object, Object> mixinAssociations = new ManagedIdentityConcurrentMap<>(ManagedIdentityConcurrentMap.ReferenceType.SOFT); - public MixinInMetaClass(final ExpandoMetaClass emc, final CachedClass mixinClass) { - this.emc = emc; - this.mixinClass = mixinClass; + private MixinInMetaClass(final ExpandoMetaClass emc, final CachedClass mixinClass) { + this.emc = requireNonNull(emc); + this.mixinClass = requireNonNull(mixinClass); this.mixinConstructor = stream(mixinClass.getConstructors()).filter(it -> it.isPublic() && it.getParameterTypes().length == 0).findFirst() .orElseThrow(() -> new GroovyRuntimeException("No default constructor for class " + mixinClass.getName() + "! Can't be mixed in.")); - - emc.addMixinClass(this); } public synchronized Object getMixinInstance(final Object object) { @@ -103,6 +101,9 @@ public class MixinInMetaClass { for (Class<?> categoryClass : categoryClasses) { final CachedClass cachedCategoryClass = ReflectionCache.getCachedClass(categoryClass); final MixinInMetaClass mixin = new MixinInMetaClass(emc, cachedCategoryClass); + if (!emc.addMixinClass(mixin)) { + continue; // GROOVY-11775 + } final MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(categoryClass); for (MetaProperty mp : metaClass.getProperties()) { @@ -172,17 +173,13 @@ public class MixinInMetaClass { @Override public boolean equals(final Object that) { - return that == this - || (that instanceof MixinInMetaClass mc - && Objects.equals(mixinClass, mc.mixinClass)); + return (that == this) + || (that instanceof MixinInMetaClass mmc + && emc.equals(mmc.emc) && mixinClass.equals(mmc.mixinClass)); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (emc != null ? emc.hashCode() : 0); - result = 31 * result + (mixinClass != null ? mixinClass.hashCode() : 0); - result = 31 * result + (mixinConstructor != null ? mixinConstructor.hashCode() : 0); - return result; + return mixinClass.hashCode(); // GROOVY-11775 } }
