[ 
https://issues.apache.org/jira/browse/GROOVY-12664?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137409#comment-18137409
 ] 

ASF GitHub Bot commented on GROOVY-12664:
-----------------------------------------

codeconsole opened a new pull request, #3185:
URL: https://github.com/apache/groovy/pull/3185

   GROOVY-11858 changed `ClosureMetaClass.invokeMethod` to defer a method found 
on the closure's metaclass (an Object extension method such as `with`, `tap` or 
`sprintf`, called with implicit this from the closure body), so that delegate 
and owner are tried first. When neither can take the call, the fallback ran the 
deferred method but did not return its result, and then threw 
`MissingMethodException`:
   
   ```groovy
   int count = 0
   def c = { -> with { ++count } }
   c.resolveStrategy = Closure.DELEGATE_ONLY
   c.delegate = null
   assert c() == 1     // 2 on 6.0.0
   assert count == 1   // 2 on 6.0.0
   ```
   
   From compiled code, the indy `MissingMethodException` handler retries 
through `GroovyObject.invokeMethod`, so the method runs a second time. A direct 
`MetaClass.invokeMethod(sender, ...)` call throws instead. The fallback now 
returns the result, which restores the 4.0.x and 5.0.x behaviour. 6.0.0 is 
affected.
   
   ## Tests
   
   
`ClosureDelegationTest.testClosureExtensionCalledOnceWhenDelegateAndOwnerUnavailable`
 (null delegate, self delegate, `dehydrate`, `rehydrate` with `OWNER_ONLY`), 
`testClosureExtensionCalledOnceWhenDelegateAndOwnerUnavailableTypeChecked` and 
`testClosureExtensionResultReturnedByMetaClass` fail without the change. 
`groovy.lang.*Closure*`, `groovy.*Closure*`, `org.codehaus.groovy.runtime.*`, 
`gls.invocation.*`, `groovy.ThisAndSuperTest`, `groovy.transform.stc.*` and 
`org.codehaus.groovy.classgen.asm.sc.*` pass. The Checkstyle and CodeNarc gates 
pass.
   




> Implicit-this call to an Object extension method inside a closure runs twice 
> when delegate and owner cannot take it
> -------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12664
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12664
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 7.0.0-alpha-1, 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> {code:groovy}
> int count = 0
> def c = { -> with { ++count } }
> c.resolveStrategy = Closure.DELEGATE_ONLY
> c.delegate = null
> def result = c()
> println "result=$result count=$count"
> {code}
> *Actual* (6.0.0 and master): {{result=2 count=2}}. The {{with}} block runs 
> twice. Calling the metaclass directly, as 
> {{c.metaClass.invokeMethod(this.getClass(), c, 'sprintf', ['%d', 42] as 
> Object[], false, true)}}, throws MissingMethodException.
> *Expected* (as in 4.0.33 and 5.0.8): {{result=1 count=1}}, and the direct 
> metaclass call returns "42".
> The same happens with a delegate that is the closure itself, after 
> dehydrate() with DELEGATE_ONLY, after rehydrate(null, null, null) with 
> OWNER_ONLY, and with @TypeChecked.
> Cause: GROOVY-11858 made {{ClosureMetaClass.invokeMethod}} defer a 
> non-closure method so that delegate and owner are tried first. When neither 
> can take the call, the deferred method is invoked but its result is not 
> returned, and MissingMethodException is thrown. The indy fallback then 
> retries through GroovyObject.invokeMethod and calls the method again.
> Verified: 6.0.0 and master affected; 4.0.33 and 5.0.8 not affected.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to