This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_5_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_5_0_X by this push:
new d61ed6956c GROOVY-11775: add test case
d61ed6956c is described below
commit d61ed6956cb1f4fcbe620ac02a5aef4376fb0817
Author: Eric Milles <[email protected]>
AuthorDate: Wed Nov 19 09:52:08 2025 -0600
GROOVY-11775: add test case
---
.../groovy/reflection/MixinInMetaClass.java | 96 +++----
src/test/groovy/groovy/lang/MixinTest.groovy | 296 ++++++++++++---------
2 files changed, 214 insertions(+), 178 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
index 90fc73d1d2..8bb1c12c03 100644
--- a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
@@ -34,33 +34,33 @@ import
org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaProperty;
import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
+
+import static java.util.Objects.hash;
public class MixinInMetaClass {
final ExpandoMetaClass emc;
final CachedClass mixinClass;
final CachedConstructor constructor;
- private final ManagedIdentityConcurrentMap managedIdentityConcurrentMap =
- new
ManagedIdentityConcurrentMap(ManagedIdentityConcurrentMap.ReferenceType.SOFT);
+ private final Map<Object, Object> managedIdentityConcurrentMap =
+ new
ManagedIdentityConcurrentMap<>(ManagedIdentityConcurrentMap.ReferenceType.SOFT);
public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) {
this.emc = emc;
this.mixinClass = mixinClass;
-
this.constructor = findDefaultConstructor(mixinClass);
+
emc.addMixinClass(this);
}
private static CachedConstructor findDefaultConstructor(CachedClass
mixinClass) {
- for (CachedConstructor constr : mixinClass.getConstructors()) {
- if (!constr.isPublic())
- continue;
-
- CachedClass[] classes = constr.getParameterTypes();
- if (classes.length == 0)
- return constr;
+ for (CachedConstructor cc : mixinClass.getConstructors()) {
+ if (cc.isPublic() && cc.getParameterTypes().length == 0) {
+ return cc;
+ }
}
throw new GroovyRuntimeException("No default constructor for class " +
mixinClass.getName() + "! Can't be mixed in.");
@@ -92,8 +92,8 @@ public class MixinInMetaClass {
return mixinClass;
}
- public static void mixinClassesToMetaClass(MetaClass self, List<Class>
categoryClasses) {
- final Class selfClass = self.getTheClass();
+ public static void mixinClassesToMetaClass(MetaClass self, final
List<Class> categoryClasses) {
+ final Class<?> selfClass = self.getTheClass();
if (self instanceof HandleMetaClass) {
self = (MetaClass) ((HandleMetaClass) self).replaceDelegate();
@@ -107,26 +107,23 @@ public class MixinInMetaClass {
}
}
- ExpandoMetaClass mc = (ExpandoMetaClass) self;
-
- List<MetaMethod> arr = new ArrayList<>();
- for (Class categoryClass : categoryClasses) {
-
+ ExpandoMetaClass emc = (ExpandoMetaClass) self;
+ List<MetaMethod> toRegister = new ArrayList<>();
+ for (Class<?> categoryClass : categoryClasses) {
final CachedClass cachedCategoryClass =
ReflectionCache.getCachedClass(categoryClass);
- final MixinInMetaClass mixin = new MixinInMetaClass(mc,
cachedCategoryClass);
+ final MixinInMetaClass mixin = new MixinInMetaClass(emc,
cachedCategoryClass);
final MetaClass metaClass =
GroovySystem.getMetaClassRegistry().getMetaClass(categoryClass);
- final List<MetaProperty> propList = metaClass.getProperties();
- for (MetaProperty prop : propList)
- if (self.getMetaProperty(prop.getName()) == null) {
- mc.registerBeanProperty(prop.getName(), new
MixinInstanceMetaProperty(prop, mixin));
+ for (MetaProperty mp : metaClass.getProperties()) {
+ if (emc.getMetaProperty(mp.getName()) == null) {
+ emc.registerBeanProperty(mp.getName(), new
MixinInstanceMetaProperty(mp, mixin));
}
-
- for (MetaProperty prop : cachedCategoryClass.getFields())
- if (self.getMetaProperty(prop.getName()) == null) {
- mc.registerBeanProperty(prop.getName(), new
MixinInstanceMetaProperty(prop, mixin));
+ }
+ for (MetaProperty mp : cachedCategoryClass.getFields()) {
+ if (emc.getMetaProperty(mp.getName()) == null) {
+ emc.registerBeanProperty(mp.getName(), new
MixinInstanceMetaProperty(mp, mixin));
}
-
+ }
for (MetaMethod method : metaClass.getMethods()) {
if (!method.isPublic())
continue;
@@ -139,30 +136,29 @@ public class MixinInMetaClass {
if (method.isStatic()) {
if (method instanceof CachedMethod)
- staticMethod(self, arr, (CachedMethod) method);
+ staticMethod(self, toRegister, (CachedMethod) method);
} else if (method.getDeclaringClass().getTheClass() !=
Object.class || "toString".equals(method.getName())) {
- //if (self.pickMethod(method.getName(),
method.getNativeParameterTypes()) == null) {
- arr.add(new MixinInstanceMetaMethod(method, mixin));
+ //if (emc.pickMethod(method.getName(),
method.getNativeParameterTypes()) == null) {
+ toRegister.add(new MixinInstanceMetaMethod(method,
mixin));
//}
}
}
}
- for (MetaMethod res : arr) {
- final MetaMethod metaMethod = res;
- if (metaMethod.getDeclaringClass().isAssignableFrom(selfClass))
- mc.registerInstanceMethod(metaMethod);
- else {
- mc.registerSubclassInstanceMethod(metaMethod);
+ for (MetaMethod mm : toRegister) {
+ if (mm.getDeclaringClass().isAssignableFrom(selfClass)) {
+ emc.registerInstanceMethod(mm);
+ } else {
+ emc.registerSubclassInstanceMethod(mm);
}
}
}
- private static boolean hasAnnotation(CachedMethod method, Class<Internal>
annotationClass) {
+ private static boolean hasAnnotation(final CachedMethod method, final
Class<? extends Annotation> annotationClass) {
return method.getAnnotation(annotationClass) != null;
}
- private static void staticMethod(final MetaClass self, List<MetaMethod>
arr, final CachedMethod method) {
+ private static void staticMethod(final MetaClass self, final
List<MetaMethod> arr, final CachedMethod method) {
CachedClass[] paramTypes = method.getParameterTypes();
if (paramTypes.length == 0)
@@ -189,24 +185,16 @@ public class MixinInMetaClass {
}
@Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof MixinInMetaClass)) return false;
- if (!super.equals(o)) return false;
-
- MixinInMetaClass that = (MixinInMetaClass) o;
-
- if (!Objects.equals(mixinClass, that.mixinClass)) return false;
-
- return true;
+ public boolean equals(final Object that) {
+ return (that == this)
+ || (that instanceof MixinInMetaClass
+ && emc.equals(((MixinInMetaClass) that).emc)
+ && mixinClass.equals(((MixinInMetaClass) that).mixinClass)
+ && constructor.equals(((MixinInMetaClass) that).constructor));
}
@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 + (constructor != null ? constructor.hashCode() :
0);
- return result;
+ return hash(emc, mixinClass, constructor);
}
}
diff --git a/src/test/groovy/groovy/lang/MixinTest.groovy
b/src/test/groovy/groovy/lang/MixinTest.groovy
index 3694da2252..7746767962 100644
--- a/src/test/groovy/groovy/lang/MixinTest.groovy
+++ b/src/test/groovy/groovy/lang/MixinTest.groovy
@@ -18,24 +18,34 @@
*/
package groovy.lang
-import groovy.test.GroovyTestCase
+import groovy.test.NotYetImplemented
+import groovy.transform.CompileStatic
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
import java.util.concurrent.locks.ReentrantLock
-import org.codehaus.groovy.reflection.ClassInfo
-class MixinTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertTrue
- @groovy.transform.CompileStatic
- protected void setUp() {
- ClassInfo.clearModifiedExpandos()
+final class MixinTest {
+
+ @BeforeEach
+ @CompileStatic
+ void setUp() {
+ org.codehaus.groovy.reflection.ClassInfo.clearModifiedExpandos()
}
- protected void tearDown() {
+ @AfterEach
+ void tearDown() {
ArrayList.metaClass = null
- List.metaClass = null
+ List.metaClass = null
ObjToTest.metaClass = null
}
+ @Test
void testOneClass() {
List.mixin ListExt
ArrayList.mixin ArrayListExt
@@ -45,6 +55,7 @@ class MixinTest extends GroovyTestCase {
assertEquals 1, [0, 1].swap().unswap()[1]
}
+ @Test
void testWithList() {
ArrayList.mixin ArrayListExt, ListExt
assertEquals 1, [0, 1].swap()[0]
@@ -53,6 +64,7 @@ class MixinTest extends GroovyTestCase {
assertEquals 1, [0, 1].swap().unswap()[1]
}
+ @Test
void testCombined() {
ArrayList.mixin Combined
assertEquals 1, [0, 1].swap()[0]
@@ -61,6 +73,7 @@ class MixinTest extends GroovyTestCase {
assertEquals 1, [0, 1].swap().unswap()[1]
}
+ @Test
void testWithEmc() {
ArrayList.metaClass.unswap = {
[delegate[1], delegate[0]]
@@ -72,25 +85,28 @@ class MixinTest extends GroovyTestCase {
assertEquals 1, [0, 1].swap().unswap()[1]
}
+ @Test
void testGroovyObject() {
def obj = new ObjToTest()
- assertEquals "original", obj.value
+ assertEquals 'original', obj.value
obj.metaClass.mixin ObjToTestCategory
- assertEquals "changed by category", obj.value
- assertEquals "original", new ObjToTest().value
+ assertEquals 'changed by category', obj.value
+ assertEquals 'original', new ObjToTest().value
}
+ @Test
void testGroovyObjectWithEmc() {
ObjToTest.metaClass.getValue = {->
- "emc changed"
+ 'emc changed'
}
ObjToTest obj = new ObjToTest()
- assertEquals "emc changed", obj.getValue()
+ assertEquals 'emc changed', obj.getValue()
obj.metaClass.mixin ObjToTestCategory
- assertEquals "changed by category", obj.value
- assertEquals "emc changed", new ObjToTest().value
+ assertEquals 'changed by category', obj.value
+ assertEquals 'emc changed', new ObjToTest().value
}
+ @Test
void testFlatten() {
Object.metaClass.mixin DeepFlattenToCategory
assertEquals([8, 9, 3, 2, 1, 4], [[8, 9] as Object[], [3, 2, [2: 1, 3:
4]], [2, 3]].flattenTo() as List)
@@ -110,11 +126,11 @@ class MixinTest extends GroovyTestCase {
return set
}
Object.metaClass.flattenTo(ArrayList) {Set set ->
- set << "oops"
- return Collection.metaClass.invokeMethod(delegate, "flattenTo",
set)
+ set << 'oops'
+ return Collection.metaClass.invokeMethod(delegate, 'flattenTo',
set)
}
ArrayList.metaClass = null
- assertEquals(["oops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
+ assertEquals(['oops', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
ArrayList.metaClass = null
Object.metaClass {
@@ -125,15 +141,15 @@ class MixinTest extends GroovyTestCase {
}
flattenTo(ArrayList) {Set set ->
- set << "oopsssss"
- return Collection.metaClass.invokeMethod(delegate,
"flattenTo", set)
+ set << 'oopsssss'
+ return Collection.metaClass.invokeMethod(delegate,
'flattenTo', set)
}
asList {->
delegate as List
}
}
- assertEquals(["oopsssss", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
+ assertEquals(['oopsssss', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
ArrayList.metaClass = null
Object.metaClass {
@@ -145,8 +161,8 @@ class MixinTest extends GroovyTestCase {
}
flattenTo {Set set ->
- set << "ssoops"
- return Collection.metaClass.invokeMethod(delegate,
"flattenTo", set)
+ set << 'ssoops'
+ return Collection.metaClass.invokeMethod(delegate,
'flattenTo', set)
}
}
@@ -154,14 +170,15 @@ class MixinTest extends GroovyTestCase {
delegate as List
}
}
- assertEquals(["ssoops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
+ assertEquals(['ssoops', -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as
Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
Object.metaClass = null
}
+ @Test
void testMixingLockable() {
Object.metaClass.mixin ReentrantLock
- def name = "abcdef"
+ def name = 'abcdef'
name.lock()
try {
assertTrue name.isLocked()
@@ -172,6 +189,7 @@ class MixinTest extends GroovyTestCase {
Object.metaClass = null
}
+ @Test
void testConcurrentQueue() {
ReentrantLock.metaClass {
withLock {Closure operation ->
@@ -196,6 +214,7 @@ class MixinTest extends GroovyTestCase {
ReentrantLock.metaClass = null
}
+ @Test
void testDynamicConcurrentQueue() {
ReentrantLock.metaClass {
withLock {Closure operation ->
@@ -264,6 +283,7 @@ class MixinTest extends GroovyTestCase {
ReentrantLock.metaClass = null
}
+ @Test
void testNoDupCollection() {
def list = new Object()
list.metaClass {
@@ -287,6 +307,7 @@ class MixinTest extends GroovyTestCase {
assertEquals 3, list[2]
}
+ @Test
void testList() {
def u = []
u.metaClass {
@@ -311,8 +332,8 @@ class MixinTest extends GroovyTestCase {
assertEquals 2, ((Set) u).size()
}
+ @Test
void testWPM() {
-
new WPM_B().foo()
WPM_C.metaClass { mixin WPM_B }
@@ -322,6 +343,7 @@ class MixinTest extends GroovyTestCase {
c.foobar()
}
+ @Test
void testStackOverflow() {
Overflow_B.metaClass {
mixin Overflow_A
@@ -338,15 +360,17 @@ class MixinTest extends GroovyTestCase {
b.foo()
}
+ // GROOVY-3474
+ @Test
void testStackOverflowErrorWithMixinsAndClosure() {
- assertScript """
+ assertScript '''
class Groovy3474A {
int counter = 1
protected final foo() {
bar { counter }
}
- private final String bar(Closure code) {
- return "Bar " + code()
+ private final String bar(Closure code) {
+ return "Bar " + code()
}
}
@@ -357,11 +381,12 @@ class MixinTest extends GroovyTestCase {
def c = new Groovy3474C()
assert c.foo() == 'Bar 1'
println "testStackOverflowErrorWithMixinsAndClosure() Done"
- """
+ '''
}
+ @Test
void testMixinWithVarargs() {
- assertScript """
+ assertScript '''
class Dsl {
static novarargs(java.util.List s) { "novarargs" + s.size() }
static plainVarargs(Object... s) { "plainVarargs" + s.size() }
@@ -371,143 +396,166 @@ class MixinTest extends GroovyTestCase {
assert novarargs(["a", "b"]) == "novarargs2"
assert plainVarargs("a", "b", 35) == "plainVarargs3"
assert mixedVarargs(3, "a", "b", "c", "d") == "mixedVarargs4"
- """
+ '''
}
-}
-class ArrayListExt {
- static def swap(ArrayList self) {
- [self[1], self[0]]
+ // GROOVY-11775
+ @NotYetImplemented @Test
+ void testRepeatMixing() {
+ assertScript '''
+ class Foo {
+ int value = new Random().nextInt()
+ }
+ class Bar {
+ }
+ Bar.mixin(Foo)
+ def bar = new Bar()
+ int val = bar.value
+ assert bar.value == val
+ Bar.mixin(Foo)
+ assert bar.value == val
+ assert bar.value == val
+ '''
}
-}
-class ListExt {
- static def unswap(List self) {
- [self[1], self[0]]
- }
-}
+
//--------------------------------------------------------------------------
-class Combined {
- static def swap(ArrayList self) {
- [self[1], self[0]]
+ static class ArrayListExt {
+ static def swap(ArrayList self) {
+ [self[1], self[0]]
+ }
}
- static def unswap(List self) {
- [self[1], self[0]]
+ static class ListExt {
+ static def unswap(List self) {
+ [self[1], self[0]]
+ }
}
-}
-class ObjToTest {
- def getValue() {
- "original"
- }
-}
+ static class Combined {
+ static def swap(ArrayList self) {
+ [self[1], self[0]]
+ }
-class ObjToTestCategory {
- static getValue(ObjToTest self) {
- "changed by category"
+ static def unswap(List self) {
+ [self[1], self[0]]
+ }
}
-}
-class DeepFlattenToCategory {
- static Set flattenTo(element) {
- LinkedHashSet set = new LinkedHashSet()
- element.flattenTo(set)
- return set
+ static class ObjToTest {
+ def getValue() {
+ 'original'
+ }
}
- // Object - put to result set
- static void flattenTo(element, Set addTo) {
- addTo << element
+ static class ObjToTestCategory {
+ static getValue(ObjToTest self) {
+ 'changed by category'
+ }
}
- // Collection - flatten each element
- static void flattenTo(Collection elements, Set addTo) {
- elements.each {element ->
- element.flattenTo(addTo)
+ static class DeepFlattenToCategory {
+ static Set flattenTo(element) {
+ LinkedHashSet set = new LinkedHashSet()
+ element.flattenTo(set)
+ return set
}
- }
- // Map - flatten each value
- static void flattenTo(Map elements, Set addTo) {
- elements.values().flattenTo(addTo)
- }
+ // Object - put to result set
+ static void flattenTo(element, Set addTo) {
+ addTo << element
+ }
- // Array - flatten each element
- static void flattenTo(Object[] elements, Set addTo) {
- elements.each {element ->
- element.flattenTo(addTo)
+ // Collection - flatten each element
+ static void flattenTo(Collection elements, Set addTo) {
+ elements.each {element ->
+ element.flattenTo(addTo)
+ }
}
- }
-}
-class NoFlattenArrayListCategory {
- // Object - put to result set
+ // Map - flatten each value
+ static void flattenTo(Map elements, Set addTo) {
+ elements.values().flattenTo(addTo)
+ }
- static void flattenTo(ArrayList element, Set addTo) {
- addTo << element
+ // Array - flatten each element
+ static void flattenTo(Object[] elements, Set addTo) {
+ elements.each {element ->
+ element.flattenTo(addTo)
+ }
+ }
}
-}
-class ConcurrentQueue {
- static {
- ConcurrentQueue.metaClass.mixin LinkedList, ReentrantLock
- }
+ static class NoFlattenArrayListCategory {
+ // Object - put to result set
- def get() {
- withLock {
- removeFirst()
+ static void flattenTo(ArrayList element, Set addTo) {
+ addTo << element
}
}
- void put(def obj) {
- withLock {
- addLast(obj)
+ static class ConcurrentQueue {
+ static {
+ ConcurrentQueue.metaClass.mixin LinkedList, ReentrantLock
}
- }
-}
-class NoDuplicateCollection {
- void put(def obj) {
- def clone = find {
- it == obj
+ def get() {
+ withLock {
+ removeFirst()
+ }
}
- if (!clone)
- add obj
+ void put(def obj) {
+ withLock {
+ addLast(obj)
+ }
+ }
}
-}
-class WPM_A {
+ static class NoDuplicateCollection {
+ void put(def obj) {
+ def clone = find {
+ it == obj
+ }
- final foo() {
- bar()
+ if (!clone)
+ add obj
+ }
}
- private final String bar() { return "Bar" }
-}
+ static class WPM_A {
+ final foo() {
+ bar()
+ }
-class WPM_B extends WPM_A {
- def foobar() {
- super.foo()
+ private final String bar() {
+ return 'Bar'
+ }
}
-}
-class WPM_C {}
+ static class WPM_B extends WPM_A {
+ def foobar() {
+ super.foo()
+ }
+ }
-class Overflow_A {
- public void foo() {
- println 'Original foo ' + receive('')
+ static class WPM_C {
}
- protected Object receive() {
- return "Message"
+ static class Overflow_A {
+ public void foo() {
+ println 'Original foo ' + receive('')
+ }
+
+ protected Object receive() {
+ return 'Message'
+ }
+
+ protected Object receive(param) {
+ receive() + param
+ }
}
- protected Object receive(Object param) {
- receive() + param
+ static class Overflow_B {
}
}
-
-class Overflow_B {}
-