[
https://issues.apache.org/jira/browse/GROOVY-12578?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Scott Murphy Heiberg updated GROOVY-12578:
------------------------------------------
Description:
For an implicit-this call in a closure body to a method the closure inherits,
such as {{toString()}} or a default Groovy method like {{with}}, {{tap}} or
{{identity}}, {{ClosureMetaClass}} tries the delegate and the owner first and
keeps the closure's own method as the fallback. When neither can take the call,
it invokes the fallback, discards the result and throws
{{MissingMethodException}}; invokedynamic catches it and calls the method again
through {{GroovyObject.invokeMethod}}, so the method runs twice:
{code:groovy}
class C { Closure<Integer> withCount() { { -> int n = 0; with { n++ }; n } } }
Closure<Integer> c = new C().withCount().rehydrate(null, null, null)
c.resolveStrategy = Closure.OWNER_ONLY
assert c() == 1 // 2
{code}
Called through the meta class directly ({{c.metaClass.invokeMethod(...)}}), the
call throws {{MissingMethodException}} after running the method. A closure
without an owner and with {{OWNER_ONLY}}, or with {{DELEGATE_ONLY}} and no
delegate, reaches the fallback. Expected: the fallback's result is returned.
PR: https://github.com/apache/groovy/pull/3104
was:
For an implicit-this call in a closure body to a method the closure inherits,
such as {{toString()}} or a default Groovy method like {{with}}, {{tap}} or
{{identity}}, {{ClosureMetaClass}} tries the delegate and the owner first and
keeps the closure's own method as the fallback. When neither can take the call,
it invokes the fallback, discards the result and throws
{{MissingMethodException}}; invokedynamic catches it and calls the method again
through {{GroovyObject.invokeMethod}}, so the method runs twice:
{code:groovy}
class C { Closure<Integer> withCount() { { -> int n = 0; with { n++ }; n } } }
Closure<Integer> c = new C().withCount().rehydrate(null, null, null)
c.resolveStrategy = Closure.OWNER_ONLY
assert c() == 1 // 2
{code}
Called through the meta class directly ({{c.metaClass.invokeMethod(...)}}), the
call throws {{MissingMethodException}} after running the method. A closure
without an owner and with {{OWNER_ONLY}}, or with {{DELEGATE_ONLY}} and no
delegate, reaches the fallback. Expected: the fallback's result is returned.
> A closure's own method runs twice when neither the delegate nor the owner can
> take the call
> -------------------------------------------------------------------------------------------
>
> Key: GROOVY-12578
> URL: https://issues.apache.org/jira/browse/GROOVY-12578
> Project: Groovy
> Issue Type: Bug
> Components: groovy-runtime
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> For an implicit-this call in a closure body to a method the closure inherits,
> such as {{toString()}} or a default Groovy method like {{with}}, {{tap}} or
> {{identity}}, {{ClosureMetaClass}} tries the delegate and the owner first and
> keeps the closure's own method as the fallback. When neither can take the
> call, it invokes the fallback, discards the result and throws
> {{MissingMethodException}}; invokedynamic catches it and calls the method
> again through {{GroovyObject.invokeMethod}}, so the method runs twice:
> {code:groovy}
> class C { Closure<Integer> withCount() { { -> int n = 0; with { n++ }; n } } }
> Closure<Integer> c = new C().withCount().rehydrate(null, null, null)
> c.resolveStrategy = Closure.OWNER_ONLY
> assert c() == 1 // 2
> {code}
> Called through the meta class directly ({{c.metaClass.invokeMethod(...)}}),
> the call throws {{MissingMethodException}} after running the method. A
> closure without an owner and with {{OWNER_ONLY}}, or with {{DELEGATE_ONLY}}
> and no delegate, reaches the fallback. Expected: the fallback's result is
> returned.
> PR: https://github.com/apache/groovy/pull/3104
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)