[ 
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)

Reply via email to