I’m seeing something I can’t explain with Groovy (1.8) mixins when I drop an invokeMethod method onto one of the mixin classes.
The following test demonstrates this effect:
1: import java.net.Socket
2:
3: import org.junit.Test
4: import static org.junit.Assert.*
5:
6: class MixinPropertyTest {
7: static class Foo {
8: def message
9:
10: Object invokeMethod(String name, args) {
11: if (name != "println") {
12: println "invokeMethod sees ${message}"
13: println "invoking ${name}"
14: }
15: def metaMethod = metaClass.getMetaMethod(name, args)
16: metaMethod?.invoke(this, args)
17: }
18:
19: String message() {
20: message
21: }
22: }
23:
24: @Mixin(Foo)
25: static class Bar {
26: }
27:
28: @Test
29: void test() {
30: assertEquals 'hello', new Bar(message: 'hello').message()
31: }
32: }
This test fails with the following output:
invokeMethod sees hello
invoking message
However, if I clip out invokeMethod, it passes. What about the invokeMethod being there causes this to stop woring?
EDIT: If I set breakpoints at lines 15 and 20, I see this as MixinPropertyTest$Foo (id=43) and MixinPropertyTest$Foo (id=75), respectively. It looks as though the Foo instance I’m interacting with changes during the course of the MetaMethod.invoke call.
I managed to puzzle this out with the debugger.
Because
invokeMethod()was usingthis(an instance ofFoo) rather than the object owning that mixin,MixinInMetaClass.getMixinInstance()under the covers decides to create a new instance ofFoo. (It caches mixin instances based on the outer object and it thinks that I’m looking for a mixin instance ofFoowithin an instance ofFoo.)I appear able to work around this by noticing when invokeMethod is called on an instance of an OwnedMetaClass:
This seems like something that should be handled automatically by Groovy. Requiring classes to have extra code in
invokeMethodto enable being used as a mixin seems un-Groovy.