[
https://issues.apache.org/jira/browse/GROOVY-12661?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137395#comment-18137395
]
ASF GitHub Bot commented on GROOVY-12661:
-----------------------------------------
codeconsole opened a new pull request, #3182:
URL: https://github.com/apache/groovy/pull/3182
With `@PackedClosures` under dynamic compilation,
`PackedClosure.setDelegate` throws `UnsupportedOperationException` for a
delegate other than the owner, because the hoisted body binds its free names to
the owner at compile time. `Closure.rehydrate` sets `delegate`, `owner` and
`thisObject` on the copy directly, so it bypassed that guard and the new values
were silently ignored:
```groovy
class D { String foo() { 'delegate' } }
@groovy.transform.PackedClosures
class O {
String foo() { 'owner' }
def m(D d) {
def c = { -> foo() }
println c.rehydrate(d, d, d)() // 'owner';
'delegate' without packing
println c.rehydrate(d, this, this).delegate == d // true, although
c.delegate = d throws
}
}
new O().m(new D())
```
Seen in 6.0.0 too.
`PackedClosure.rehydrate` now applies the same guard when the closure was
packed on the dynamic trust path. A non-null delegate other than the original
owner gets the `setDelegate` message; a non-null owner or this object other
than the original owner gets a matching message (an unpacked dynamic closure
resolves names through its owner and a `@CompileStatic` one through its this
object, so both are checked). `null` and the original owner are still allowed,
so `dehydrate()` and rehydrating back onto the original owner keep working. A
runtime guard is used rather than a compile-time decline because a rehydrate in
a called method (the builder idiom) is invisible to the escape walk.
Closures packed under `@CompileStatic`, or on the flag's no-free-name path,
are unchanged: they store and ignore a delegate, as before. The
`@PackedClosures` javadoc now says rehydrate is guarded too.
## Tests
`PackedClosuresTransformTest.rehydrateGoesThroughTheDelegateGuard` fails
without the change.
`PackedClosuresTransformTest.compileStaticRehydrateStoresAndIgnoresADelegate`
and
`ClosurePackCapabilityTest.syntacticallyPackedClosureCanBeRehydratedElsewhere`
pin the unguarded paths. The closure-packing test classes,
`groovy.lang.*Closure*`, `groovy.ClosureTest`,
`org.codehaus.groovy.classgen.asm.sc.*` and `groovy.transform.stc.*` pass. The
Checkstyle and CodeNarc gates pass.
> A @PackedClosures closure silently ignores the delegate, owner and thisObject
> passed to rehydrate instead of failing fast like setDelegate
> ------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12661
> URL: https://issues.apache.org/jira/browse/GROOVY-12661
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 7.0.0-alpha-1, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> With {{@PackedClosures}} under dynamic compilation,
> {{PackedClosure.setDelegate}} refuses a delegate other than the owner,
> because the hoisted body binds its free names to the owner at compile time.
> {{Closure.rehydrate}} writes the delegate, owner and thisObject fields on the
> copy directly, so it bypasses that guard. The packed body keeps calling the
> original owner, and the new delegate or owner is silently ignored.
> {code:groovy}
> class D { String foo() { 'delegate' } }
> @groovy.transform.PackedClosures
> class O {
> String foo() { 'owner' }
> def m(D d) {
> def c = { -> foo() }
> println c.rehydrate(d, d, d)()
> println c.rehydrate(d, this, this).delegate == d
> c.delegate = d
> }
> }
> new O().m(new D())
> {code}
> *Actual:* prints {{owner}} and {{true}}, then the last line throws
> UnsupportedOperationException ("Cannot set a delegate on a @PackedClosures
> closure ..."). Rehydrating onto another owner of the same class also returns
> the original owner's result.
> *Expected:* rehydrate refuses a delegate, owner or this object the packed
> body cannot honour, with the same UnsupportedOperationException as
> setDelegate. Without the annotation the closure prints {{delegate}}.
> Cause: PackedClosure guards setDelegate and setResolveStrategy but not
> rehydrate, which sets the fields without calling setDelegate.
> Verified on 6.0.0 and master.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)