Index: src/test/groovy/bugs/Groovy3422Bug.groovy =================================================================== --- src/test/groovy/bugs/Groovy3422Bug.groovy (revision 0) +++ src/test/groovy/bugs/Groovy3422Bug.groovy (revision 0) @@ -0,0 +1,37 @@ +package groovy.bugs + +class Groovy3422Bug extends GroovyTestCase { + public void testCallingUnqualifiedStaticClosureFromAnother() { + assert FFoo1.second() == "first call" + + // re-assign a different closure to the static prop + FFoo1.first = { val -> "second $val" } + assert FFoo1.second() == "second call" + + FFoo1.second.delegate = FFooDelegate + FFoo1.second.resolveStrategy = Closure.DELEGATE_FIRST + assert FFoo1.second() == "delegate call" + + FFoo1.second.resolveStrategy = Closure.OWNER_ONLY + assert FFoo1.second() == "second call" + + FFoo2.second.delegate = FFooDelegate + assert FFoo2.second() == "delegate call" + + FFoo2.second.resolveStrategy = Closure.DELEGATE_ONLY + assert FFoo2.second() == "delegate call" + println "testCallingUnqualifiedStaticClosureFromAnother() Done" + } +} +class FFoo1 { + static first = { val -> "first $val" } + static second = { first( "call" ) } +} + +class FFooDelegate { + static first = { val -> "delegate $val" } +} + +class FFoo2 { + static second = { first( "call" ) } +} Index: src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java =================================================================== --- src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java (revision 15851) +++ src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java (working copy) @@ -196,7 +196,7 @@ return (MetaMethod) answer; } - private MetaMethod getDelegateMethod(Closure closure, Object delegate, String methodName, Class[] argClasses) { + private MetaMethod getDelegateMethod(Closure closure, Object delegate, String methodName, Class[] argClasses, Object[] callObjs) { if (delegate == closure || delegate == null) return null; MetaClass delegateMetaClass; if (delegate instanceof Class) { @@ -201,7 +201,22 @@ MetaClass delegateMetaClass; if (delegate instanceof Class) { delegateMetaClass = registry.getMetaClass((Class)delegate); - return delegateMetaClass.getStaticMetaMethod(methodName, argClasses); + MetaMethod mm = delegateMetaClass.getStaticMetaMethod(methodName, argClasses); + if(mm != null) { + return mm; + } else { + // check if there is a static property of the same name that is a closure + MetaProperty mp = delegateMetaClass.getMetaProperty(methodName); + if(mp != null) { + Object prop = delegateMetaClass.getProperty(delegate, methodName); + if(prop instanceof Closure) { + delegateMetaClass = registry.getMetaClass(prop.getClass()); + mm = delegateMetaClass.getMetaMethod(CLOSURE_DO_CALL_METHOD, argClasses); + callObjs[0] = prop; + } + } + return mm; + } } else { delegateMetaClass = lookupObjectMetaClass(delegate); @@ -258,7 +273,8 @@ boolean invokeOnDelegate = false; boolean invokeOnOwner = false; boolean ownerFirst = true; - + Object[] callObjs = new Object[1]; + switch (resolveStrategy) { case Closure.TO_SELF: break; @@ -263,8 +279,9 @@ case Closure.TO_SELF: break; case Closure.DELEGATE_ONLY: - method = getDelegateMethod(closure, delegate, methodName, argClasses); - callObject = delegate; + callObjs[0] = delegate; + method = getDelegateMethod(closure, delegate, methodName, argClasses, callObjs); + callObject = callObjs[0]; if (method == null) { invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject); } @@ -270,8 +287,9 @@ } break; case Closure.OWNER_ONLY: - method = getDelegateMethod(closure, owner, methodName, argClasses); - callObject = owner; + callObjs[0] = owner; + method = getDelegateMethod(closure, owner, methodName, argClasses, callObjs); + callObject = callObjs[0]; if (method == null) { invokeOnOwner = owner != closure && (owner instanceof GroovyObject); } @@ -278,11 +296,13 @@ break; case Closure.DELEGATE_FIRST: - method = getDelegateMethod(closure, delegate, methodName, argClasses); - callObject = delegate; + callObjs[0] = delegate; + method = getDelegateMethod(closure, delegate, methodName, argClasses, callObjs); + callObject = callObjs[0]; if (method == null) { - method = getDelegateMethod(closure, owner, methodName, argClasses); - callObject = owner; + callObjs[0] = owner; + method = getDelegateMethod(closure, owner, methodName, argClasses, callObjs); + callObject = callObjs[0]; } if (method == null) { invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject); @@ -292,9 +312,10 @@ break; default: // owner first // owner first means we start with the outer most owner that is not a generated closure - // this owner is equal to the this object, so we check that one first. - method = getDelegateMethod(closure, thisObject, methodName, argClasses); - callObject = thisObject; + // this owner is equal to the this object, so we check that one first. + callObjs[0] = thisObject; + method = getDelegateMethod(closure, thisObject, methodName, argClasses, callObjs); + callObject = callObjs[0]; if (method == null) { //try finding a delegate that has that method... we start from // outside building a stack and try each delegate @@ -306,10 +327,12 @@ } while (!list.isEmpty() && method==null) { + callObjs[0] = null; Closure closureWithDelegate = (Closure) list.removeLast(); Object currentDelegate = closureWithDelegate.getDelegate(); - method = getDelegateMethod(closureWithDelegate,currentDelegate,methodName,argClasses); - callObject = currentDelegate; + callObjs[0] = currentDelegate; + method = getDelegateMethod(closureWithDelegate,currentDelegate,methodName,argClasses, callObjs); + callObject = callObjs[0]; } } if (method == null) {