Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-6510

Problems with implicit closures inside of @Category-annotated classes

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • None
    • 2.5.16, 3.0.10, 4.0.1
    • None
    • None

    Description

      The behavior of implicit closures is in my opinion broken when used inside of a category.

      As you can see in the attached TestCase in a simplified example, I had the following scenario:

      • first there is an utility method (in the example the method ClosureInvoker#setDelegateAndInvokeClosure(Closure)) that gets a closure, resets the delegate object of it and calls this closure.
      • next we have an ExtendedClass which isn't really used in the example itself other than being used as "base"-class for the category/extension-methods. This is just because its own functionallity doesn't matter to get the buggy behavior. Just be ensured that it made more sense in the original context (e.g. it was passed to the utility method and used to setup the delegate object)
      • now there is a category class with a method that invokes the utility method and passing an implicit closure to it (@see CategoryClass#doWorkWithImplicitClosure()). that implicit closure is invoking a method of the delegate object
      • at last the testcase itself will call this category method and assert, that the delegate method was really called by the closure (@see ClosureInsideExtensionOrCategoryTest#testCategoryWithImplicitClosure())

      As you can see in the additional test methods, the basic behavior does work as expected. But only in the special combination of an implicit closure inside of the category it fails.

      As far as I can backtrack it, it seems that the AST-manipulations for category classes may do the damage. It appears to me, that it will manipulate ALL unqualified member-calls inside of any category-methods to be instead calls to the class given by the @Category-annotation. But this manipulation should in my opinion be escaped when entering the code-block of an implicit closure.

      So refering to my example the method CategoryClass#doWorkWithImplicitClosure() seems to lead to the following code after AST-manipulation:

      def static doWorkWithImplicitClosure(ExtendedClass obj) {
        ClosureInvoker.setDelegateAndInvokeClosure {->
          obj.invokeDelegate()
        }
      }
      

      This will result in avoiding the whole resolve strategy of the closure, as there won't be any unqualified member calls inside of the closure at all.

      Instead I would expect, that it would generate the following code:

      def static doWorkWithImplicitClosure(ExtendedClass obj) {	
        ClosureInvoker.setDelegateAndInvokeClosure {->
          //AST manipulation would "prefix" all unqualified 
          //member calls with "obj." outside of the closure 
          //but not here as we are inside of a closure
          invokeDelegate()
        }
      }
      

      which would be equivalent to ExtensionMethodClass#doWorkWithImplicitClosure(ExtendedClass)

      Attachments

        Activity

          People

            emilles Eric Milles
            david.hangl David Hangl
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: