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 28b19fbdc9 GROOVY-11813: check category method declaring class against
`theClass`
28b19fbdc9 is described below
commit 28b19fbdc98e595dc6a194d86673cf37df17fb95
Author: Eric Milles <[email protected]>
AuthorDate: Tue Dec 30 15:03:25 2025 -0600
GROOVY-11813: check category method declaring class against `theClass`
5_0_X backport
---
src/main/java/groovy/lang/MetaClassImpl.java | 24 +-
src/spec/test/metaprogramming/CategoryTest.groovy | 32 +-
src/test/groovy/groovy/CategoryTest.groovy | 557 +++++++++++++---------
src/test/groovy/groovy/MetaClassCreator.java | 34 ++
4 files changed, 386 insertions(+), 261 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java
b/src/main/java/groovy/lang/MetaClassImpl.java
index 36a43be020..1f90450e74 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -694,21 +694,21 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
if (answer == null) answer = FastArray.EMPTY_LIST;
if (!isCallToSuper) {
- List<CategoryMethod> used =
GroovyCategorySupport.getCategoryMethods(name);
- if (used != null) {
- FastArray arr;
- if (answer instanceof MetaMethod) {
- arr = new FastArray();
- arr.add(answer);
+ List<CategoryMethod> methods =
GroovyCategorySupport.getCategoryMethods(name);
+ if (methods != null) {
+ FastArray array;
+ if (answer instanceof FastArray) {
+ array = ((FastArray) answer).copy();
} else {
- arr = ((FastArray) answer).copy();
+ array = new FastArray();
+ array.add(answer);
}
- for (CategoryMethod cm : used) {
- if
(!cm.getDeclaringClass().getTheClass().isAssignableFrom(sender))
- continue;
- filterMatchingMethodForCategory(arr, cm);
+ for (CategoryMethod cm : methods) {
+ Class<?> cmdc = cm.getDeclaringClass().getTheClass();
+ if (cmdc.isAssignableFrom(theClass)) // GROOVY-11813: not
sender
+ filterMatchingMethodForCategory(array, cm);
}
- answer = arr;
+ answer = array;
}
}
return answer;
diff --git a/src/spec/test/metaprogramming/CategoryTest.groovy
b/src/spec/test/metaprogramming/CategoryTest.groovy
index 1d528c6bbc..6a19dd106a 100644
--- a/src/spec/test/metaprogramming/CategoryTest.groovy
+++ b/src/spec/test/metaprogramming/CategoryTest.groovy
@@ -18,14 +18,17 @@
*/
package metaprogramming
-import groovy.test.GroovyTestCase
import groovy.time.TimeCategory
+import org.junit.jupiter.api.Test
-class CategoryTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+final class CategoryTest {
+
+ @Test
void testApplyTimeCategory() {
// tag::time_category[]
- use(TimeCategory) {
+ use(TimeCategory) {
println 1.minute.from.now // <1>
println 10.hours.ago
@@ -35,6 +38,7 @@ class CategoryTest extends GroovyTestCase {
// end::time_category[]
}
+ @Test
void testCategoryAnnotation() {
assertScript '''
// tag::time_category_anno[]
@@ -50,30 +54,10 @@ class CategoryTest extends GroovyTestCase {
}
}
- use (NumberCategory) {
+ use(NumberCategory) {
assert 42.meters.toString() == '42m'
}
// end::time_category_anno[]
'''
}
-
- // GROOVY-8433
- void testCategoryAnnotationAndAIC() {
- assertScript '''
- @Category(Number)
- class NumberCategory {
- def m() {
- String variable = 'works'
- new Object() { // "Cannot cast object '1' with class
'java.lang.Integer' to class 'NumberCategory'" due to implicit "this"
- String toString() { variable }
- }
- }
- }
-
- use (NumberCategory) {
- String result = 1.m()
- assert result == 'works'
- }
- '''
- }
}
diff --git a/src/test/groovy/groovy/CategoryTest.groovy
b/src/test/groovy/groovy/CategoryTest.groovy
index 0723180c13..45dc573c11 100644
--- a/src/test/groovy/groovy/CategoryTest.groovy
+++ b/src/test/groovy/groovy/CategoryTest.groovy
@@ -18,71 +18,95 @@
*/
package groovy
-import groovy.test.GroovyTestCase
+import org.junit.jupiter.api.Test
-final class CategoryTest extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+import static org.junit.jupiter.api.Assertions.assertThrows
- @Override
- protected void setUp() {
- def dummy = null
- CategoryTestPropertyCategory.setSomething(dummy, 'hello')
- CategoryTestHelperPropertyReplacer.setaProperty(dummy, 'anotherValue')
+final class CategoryTest {
+
+ static class StringCategory {
+ static String lower(String s) {
+ s.toLowerCase()
+ }
}
- void testCategories() {
- use (StringCategory) {
- assert "Sam".lower() == "sam";
- use (IntegerCategory.class) {
- assert "Sam".lower() == "sam";
- assert 1.inc() == 2;
+ static class IntegerCategory {
+ static Integer inc(Integer i) {
+ i + 1
+ }
+ }
+
+ @Test
+ void testNestingOfCategories() {
+ use(StringCategory) {
+ assert 'Sam'.lower() == 'sam'
+ use(IntegerCategory) {
+ assert 9.inc() == 10
+ assert 'Sam'.lower() == 'sam'
}
- shouldFail(MissingMethodException, { 1.inc() });
+ assertThrows(MissingMethodException) { 1.inc() }
}
- shouldFail(MissingMethodException, { "Sam".lower() });
+ assertThrows(MissingMethodException) { 'Sam'.lower() }
}
+ @Test
void testReturnValueWithUseClass() {
def returnValue = use(StringCategory) {
- "Green Eggs And Ham".lower()
+ 'Green Eggs And Ham'.lower()
}
- assert "green eggs and ham" == returnValue
+ assert returnValue == 'green eggs and ham'
}
- void testReturnValueWithUseList() {
+ @Test
+ void testReturnValueWithUseClassList() {
def returnValue = use([StringCategory, IntegerCategory]) {
- "Green Eggs And Ham".lower() + 5.inc()
+ 'Green Eggs And Ham'.lower() + 5.inc()
}
- assert "green eggs and ham6" == returnValue
+ assert returnValue == 'green eggs and ham6'
}
+
//--------------------------------------------------------------------------
+
+ @Test
void testCategoryDefinedProperties() {
- use(CategoryTestPropertyCategory) {
- assert getSomething() == "hello"
- assert something == "hello"
- something = "nihao"
- assert something == "nihao"
- }
+ assertScript '''
+ class CategoryTestPropertyCategory {
+ private static aVal = 'hello'
+ static getSomething(Object self) { return aVal }
+ static void setSomething(Object self, newValue) { aVal =
newValue }
+ }
- // test the new value again in a new block
- use(CategoryTestPropertyCategory) {
- assert something == "nihao"
- }
+ use(CategoryTestPropertyCategory) {
+ assert getSomething() == 'hello'
+ assert something == 'hello'
+ something = 'nihao'
+ assert something == 'nihao'
+ }
+ // test the new value again in a new block
+ use(CategoryTestPropertyCategory) {
+ assert something == 'nihao'
+ }
+ '''
}
// GROOVY-10133
+ @Test
void testCategoryDefinedProperties2() {
assertScript '''
class Cat {
static boolean isAbc(self) { true }
static boolean getAbc(self) { false }
}
- use (Cat) {
- assert abc // should select "isAbc()"
+
+ use(Cat) {
+ assert abc // should select isAbc()
}
'''
}
// GROOVY-5245
+ @Test
void testCategoryDefinedProperties3() {
assertScript '''
class Isser {
@@ -92,7 +116,8 @@ final class CategoryTest extends GroovyTestCase {
static boolean getWorking2(Isser b) { true }
static boolean isNotWorking(Isser b) { true }
}
- use (IsserCat) {
+
+ use(IsserCat) {
assert new Isser().working
assert new Isser().working2
assert new Isser().notWorking // MissingPropertyException
@@ -100,161 +125,225 @@ final class CategoryTest extends GroovyTestCase {
'''
}
- void testCategoryReplacedPropertyAccessMethod() {
- def cth = new CategoryTestHelper()
- cth.aProperty = "aValue"
- assert cth.aProperty == "aValue"
- use (CategoryTestHelperPropertyReplacer) {
- assert cth.aProperty == "anotherValue"
- cth.aProperty = "this is boring"
- assert cth.aProperty == "this is boring"
- }
- assert cth.aProperty == "aValue"
+ @Test
+ void testCategoryMethodReplacesPropertyAccessMethod() {
+ assertScript '''
+ class CategoryTestHelper {
+ def aProperty = 'aValue'
+ }
+ class CategoryTestHelperPropertyReplacer {
+ private static aVal = 'anotherValue'
+ static getaProperty(CategoryTestHelper self) { return aVal }
+ static void setaProperty(CategoryTestHelper self, newValue) {
aVal = newValue }
+ }
+
+ def cth = new CategoryTestHelper()
+ cth.aProperty = 'aValue'
+ assert cth.aProperty == 'aValue'
+ use(CategoryTestHelperPropertyReplacer) {
+ assert cth.aProperty == 'anotherValue'
+ cth.aProperty = 'this is boring'
+ assert cth.aProperty == 'this is boring'
+ }
+ assert cth.aProperty == 'aValue'
+ '''
}
+ // GROOVY-11820
+ @Test
+ void testCategoryMethodHiddenByPropertyAccessMethod() {
+ assertScript '''import java.lang.reflect.Field
+ class Pogo {
+ public float field
+ }
+
+ Field field = Pogo.fields.first()
+ assert field.getType() == float
+ assert field.type == float
+
+ @Category(Field)
+ class FieldCat {
+ def getType() { 'override' }
+ }
+
+ use(FieldCat) {
+ assert field.getType() == 'override'
+ assert field.type == 'override'
+ }
+
+ @Category(Object)
+ class ObjectCat {
+ def getType() { 'override' }
+ }
+
+ use(ObjectCat) { // class method is closer than category method
+ assert field.getType() == float
+ //assert field.type == float TODO
+ }
+ '''
+ }
+
+ @Test
void testCategoryHiddenByClassMethod() {
- assertScript """
- class A{}
- class B extends A{def m(){1}}
- class Category{ static m(A a) {2}}
- def b = new B()
- use (Category) {
- assert b.m() == 1
- }
- """
+ assertScript '''
+ class A {
+ }
+ class B extends A {
+ def m() { 1 }
+ }
+ class C {
+ static m(A a) { 2 }
+ }
+
+ def b = new B()
+ use(C) {
+ assert b.m() == 1
+ }
+ '''
}
+ @Test
void testCategoryOverridingClassMethod() {
- assertScript """
- class A {def m(){1}}
- class Category{ static m(A a) {2}}
- def a = new A()
- use (Category) {
- assert a.m() == 2
- }
- """
- assertScript """
- class A {def m(){1}}
- class B extends A{}
- class Category{ static m(A a) {2}}
- def a = new B()
- use (Category) {
- assert a.m() == 2
- }
- """
+ assertScript '''
+ class A {
+ def m() { 1 }
+ }
+ class C {
+ static m(A a) { 2 }
+ }
+
+ def a = new A()
+ use(C) {
+ assert a.m() == 2
+ }
+ '''
+ }
+
+ @Test
+ void testCategoryOverridingClassMethod2() {
+ assertScript '''
+ class A {
+ def m() { 1 }
+ }
+ class B extends A {
+ }
+ class C {
+ static m(A a) { 2 }
+ }
+
+ def a = new B()
+ use(C) {
+ assert a.m() == 2
+ }
+ '''
}
+ @Test
void testCategoryWithMixedOverriding() {
- assertScript """
- class A{def m(){0}}
- class B extends A{def m(){1}}
- class Category{ static m(A a) {2}}
- def b = new B()
- use (Category) {
- assert b.m() == 1
- }
- """
+ assertScript '''
+ class A {
+ def m() { 0 }
+ }
+ class B extends A {
+ def m() { 1 }
+ }
+ class C {
+ static m(A a) { 2 }
+ }
+
+ def b = new B()
+ use(C) {
+ assert b.m() == 1
+ }
+ '''
}
+ @Test
void testCategoryInheritance() {
- assertScript """
- public class Foo {
- static Object foo(Object obj) {
- "Foo.foo()"
- }
- }
-
- public class Bar extends Foo{
- static Object bar(Object obj) {
- "Bar.bar()"
- }
- }
+ assertScript '''
+ class Foo {
+ static Object foo(Object obj) {
+ 'Foo.foo()'
+ }
+ }
+ class Bar extends Foo {
+ static Object bar(Object obj) {
+ 'Bar.bar()'
+ }
+ }
- def obj = new Object()
+ def obj = new Object()
+ use(Foo) {
+ assert obj.foo() == 'Foo.foo()'
+ }
+ use(Bar) {
+ assert obj.bar() == 'Bar.bar()'
+ assert obj.foo() == 'Foo.foo()'
+ }
+ '''
+ }
- use(Foo){
- assert obj.foo() == "Foo.foo()"
- }
+ // GROOVY-8433
+ @Test
+ void testCategoryAnnotationAndAIC() {
+ assertScript '''
+ @Category(Number)
+ class NumberCategory {
+ def m() {
+ String variable = 'works'
+ new Object() { // "Cannot cast object '1' with class
'java.lang.Integer' to class 'NumberCategory'" due to implicit "this"
+ String toString() { variable }
+ }
+ }
+ }
- use(Bar){
- assert obj.bar() == "Bar.bar()"
- assert obj.foo() == "Foo.foo()"
- }
- """
+ use(NumberCategory) {
+ String result = 1.m()
+ assert result == 'works'
+ }
+ '''
}
+ // GROOVY-5248
+ @Test
void testNullReceiverChangeForPOJO() {
- // GROOVY-5248
// this test will call a method using a POJO while a category is active
// in call site caching this triggers the usage of POJOMetaClassSite,
// which was missing a null check for the receiver. The last foo call
// uses null to exactly check that path. I use multiple calls with
foo(1)
// before to ensure for example indy will do the right things as well,
// since indy may need more than one call here.
- assertScript """
- class Cat {
- public static findAll(Integer x, Closure cl) {1}
- }
-
- def foo(x) {
- x.findAll {}
- }
-
- use (Cat) {
- assert foo(1) == 1
- assert foo(1) == 1
- assert foo(1) == 1
- assert foo(null) == []
- assert foo(1) == 1
- assert foo(1) == 1
- assert foo(1) == 1
- }
- """
- }
-
- def foo(x){x.bar()}
-
- void testMethodHiding1() {
- def x = new X()
- assert foo(x) == 1
- use (XCat) {
- assert foo(x) == 2
- def t = Thread.start {assert foo(x)==1}
- t.join()
- }
- assert foo(x) == 1
- def t = Thread.start {use (XCat2){assert foo(x)==3}}
- t.join()
- assert foo(x) == 1
- }
+ assertScript '''
+ class C {
+ static findAll(Integer x, Closure c) { 1 }
+ }
+ def foo(x) {
+ x.findAll { -> }
+ }
- void testMethodHiding2() {
- def x = new X()
- assert foo(x) == 1
- use (XCat) {
- assert foo(x) == 2
- def t = Thread.start {use (XCat2){assert foo(x)==3}}
- t.join()
- assert foo(x) == 2
- t = Thread.start {assert foo(x)==1}
- t.join()
- }
- assert foo(x) == 1
- def t = Thread.start {use (XCat2){assert foo(x)==3}}
- t.join()
- assert foo(x) == 1
+ use(C) {
+ assert foo(1) == 1
+ assert foo(1) == 1
+ assert foo(1) == 1
+ assert foo() == []
+ assert foo(1) == 1
+ assert foo(1) == 1
+ assert foo(1) == 1
+ }
+ '''
}
+ @Test
void testCallToPrivateMethod1() {
assertScript '''
class A {
private foo() { 1 }
def baz() { foo() }
}
-
- class B extends A {}
-
- class C {}
+ class B extends A {
+ }
+ class C {
+ }
use(C) {
assert new B().baz() == 1
@@ -263,16 +352,17 @@ final class CategoryTest extends GroovyTestCase {
}
// GROOVY-6263
+ @Test
void testCallToPrivateMethod2() {
assertScript '''
class A {
private foo(a) { 1 }
def baz() { foo() }
}
-
- class B extends A {}
-
- class C {}
+ class B extends A {
+ }
+ class C {
+ }
use(C) {
assert new B().baz() == 1
@@ -281,13 +371,15 @@ final class CategoryTest extends GroovyTestCase {
}
// GROOVY-5453
+ @Test
void testOverloadedGetterMethod1() {
assertScript '''
- class Cat {
- static getFoo(String s) {'String'}
- static getFoo(CharSequence s) {'CharSequence'}
+ class C {
+ static getFoo(String s) { 'String' }
+ static getFoo(CharSequence s) { 'CharSequence' }
}
- use (Cat) {
+
+ use(C) {
assert 'abc'.getFoo() == 'String'
assert 'abc'.foo == 'String'
}
@@ -295,9 +387,10 @@ final class CategoryTest extends GroovyTestCase {
}
// GROOVY-10214
+ @Test
void testOverloadedGetterMethod2() {
assertScript '''
- class Cat {
+ class C {
static String getFoo(Boolean self) {
'Boolean'
}
@@ -320,7 +413,8 @@ final class CategoryTest extends GroovyTestCase {
'Double'
}
}
- use (Cat) {
+
+ use(C) {
assert 123.foo == 'Integer'
assert 4.5d.foo == 'Double'
}
@@ -328,6 +422,7 @@ final class CategoryTest extends GroovyTestCase {
}
// GROOVY-10743
+ @Test
void testStaticMethodOnInterface() {
assertScript '''
use(java.util.stream.Stream) {
@@ -337,96 +432,108 @@ final class CategoryTest extends GroovyTestCase {
'''
}
- // GROOVY-3867
- void testPropertyMissing() {
- def x = new X()
-
- shouldFail(MissingPropertyException) {
- assert x.baz != "works" // accessing x.baz should throw MPE
- }
+ // GROOVY-11813
+ @Test
+ void testCategoryOperatorMethodAndCustomMetaClass() {
+ assertScript '''
+ import groovy.MetaClassCreator
+ import groovy.time.TimeCategory
- use(XCat4) {
- assert x.baz == "works"
- }
+ GroovySystem.metaClassRegistry.metaClassCreationHandle = new
MetaClassCreator()
- shouldFail(MissingPropertyException) {
- assert x.baz != "works" // accessing x.baz should throw MPE
- }
+ use(TimeCategory) {
+ def date = new Date()
+ def duration = 7.months
+ return (date - duration)
+ }
+ '''
}
- // GROOVY-10783
- void testPropertyMissing2() {
- assertScript '''\
- class X{ def bar(){1}}
- class XCat4{ static propertyMissing(X x, String name) {"works"}}
+
//--------------------------------------------------------------------------
- def x = new X()
+ static class X { def bar() {1} }
+ static class XCat { static bar(X x) {2} }
+ static class XCat2 { static bar(X x) {3} }
+ static class XCat3 { static methodMissing(X x, String name, args) {4} }
+ static class XCat4 { static propertyMissing(X x, String name) {'works'} }
- use(XCat4) {
- assert x.baz == "works"
- }
- '''
+ def foo(x) { x.bar() }
+
+ @Test
+ void testMethodHiding1() {
+ def x = new X()
+ assert foo(x) == 1
+ use(XCat) {
+ assert foo(x) == 2
+ def t = Thread.start {assert foo(x)==1}
+ t.join()
+ }
+ assert foo(x) == 1
+ def t = Thread.start {use(XCat2){assert foo(x)==3}}
+ t.join()
+ assert foo(x) == 1
+ }
+
+ @Test
+ void testMethodHiding2() {
+ def x = new X()
+ assert foo(x) == 1
+ use(XCat) {
+ assert foo(x) == 2
+ def t = Thread.start {use(XCat2){assert foo(x)==3}}
+ t.join()
+ assert foo(x) == 2
+ t = Thread.start {assert foo(x)==1}
+ t.join()
+ }
+ assert foo(x) == 1
+ def t = Thread.start {use(XCat2){assert foo(x)==3}}
+ t.join()
+ assert foo(x) == 1
}
// GROOVY-3867
+ @Test
void testMethodMissing() {
def x = new X()
assert foo(x) == 1
- use (XCat3) {
+ use(XCat3) {
assert foo(x) == 1 // regular foo() is not affected by
methodMissing in category
assert x.baz() == 4 // XCat3.methodMissing is called
}
assert foo(x) == 1
- def t = Thread.start {use (XCat3){assert x.baz()==4}}
+ def t = Thread.start {use(XCat3){assert x.baz()==4}}
t.join()
assert foo(x) == 1
- shouldFail(MissingMethodException) {
+ assertThrows(MissingMethodException) {
x.baz()
}
}
// GROOVY-3867
+ @Test
void testMethodMissingNoStatic() {
def x = new X()
- use (XCat3) {
+ use(XCat3) {
assert x.baz() == 4 // XCat3.methodMissing is called for instance
- shouldFail(MissingMethodException) {
+ assertThrows(MissingMethodException) {
assert X.baz() != 4 // XCat3.methodMissing should not be
called for static method of X
}
}
}
-}
-
-class X{ def bar(){1}}
-class XCat{ static bar(X x){2}}
-class XCat2{ static bar(X x){3}}
-class XCat3{ static methodMissing(X x, String name, args) {4}}
-class XCat4{ static propertyMissing(X x, String name) {"works"}}
-
-class StringCategory {
- static String lower(String string) {
- return string.toLowerCase();
- }
-}
-class IntegerCategory {
- static Integer inc(Integer i) {
- return i + 1;
+ // GROOVY-3867, GROOVY-10783
+ @Test
+ void testPropertyMissing() {
+ def x = new X()
+ assertThrows(MissingPropertyException) {
+ assert x.baz != 'works' // accessing x.baz should throw MPE
+ }
+ use(XCat4) {
+ assert x.baz == 'works'
+ }
+ assertThrows(MissingPropertyException) {
+ assert x.baz != 'works' // accessing x.baz should throw MPE
+ }
}
}
-
-class CategoryTestPropertyCategory {
- private static aVal = "hello"
- static getSomething(Object self) { return aVal }
- static void setSomething(Object self, newValue) { aVal = newValue }
-}
-
-class CategoryTestHelper {
- def aProperty = "aValue"
-}
-
-class CategoryTestHelperPropertyReplacer {
- private static aVal = "anotherValue"
- static getaProperty(CategoryTestHelper self) { return aVal }
- static void setaProperty(CategoryTestHelper self, newValue) { aVal =
newValue }
-}
diff --git a/src/test/groovy/groovy/MetaClassCreator.java
b/src/test/groovy/groovy/MetaClassCreator.java
new file mode 100644
index 0000000000..8d1ff93ab1
--- /dev/null
+++ b/src/test/groovy/groovy/MetaClassCreator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 groovy;
+
+import groovy.lang.MetaClass;
+import groovy.lang.MetaClassImpl;
+import groovy.lang.MetaClassRegistry;
+import org.codehaus.groovy.runtime.GeneratedClosure;
+
+public class MetaClassCreator extends
MetaClassRegistry.MetaClassCreationHandle {
+ @Override
+ protected MetaClass createNormalMetaClass(Class theClass,
MetaClassRegistry registry) {
+ if (!theClass.isAnonymousClass() &&
!GeneratedClosure.class.isAssignableFrom(theClass)) {
+ return new MetaClassImpl(registry, theClass) { }; // GROOVY-11813
+ }
+ return super.createNormalMetaClass(theClass, registry);
+ }
+}