[
https://issues.apache.org/jira/browse/GROOVY-12652?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137330#comment-18137330
]
ASF GitHub Bot commented on GROOVY-12652:
-----------------------------------------
codeconsole opened a new pull request, #3173:
URL: https://github.com/apache/groovy/pull/3173
With closure packing enabled (`-Dgroovy.target.closure.pack=true` or
`@PackedClosures`), a closure written in a constructor or in an instance
initializer block was packed even when it escapes:
```groovy
class P { P(List<Closure> s) { s << { x -> x } } } // packed:
PackedClosure$Fixed1
class Q { Closure f; { f = { x -> x } } } // packed:
PackedClosure$Fixed1
```
`ClosureWriter` asked `WriterController.getMethodNode()` for the enclosing
method, and that is null while a constructor is being written. The escape gate,
the serialization gate and the `@PackedClosures` mode lookup therefore saw
nothing. This applies to dynamic, `@TypeChecked` and `@CompileStatic` code.
Instance initializer blocks are inlined into each constructor by the Verifier,
so they were affected as well. Under `@PackedClosures` the escaped closure then
failed the packed adapter's runtime guard as soon as a delegate was set on it.
`ClosureWriter.enclosingMethod()` now returns the constructor node when
there is no method node, and the escape walk, the serialization walk and the
mode lookup use it. `@PackedClosures` can now be placed on a constructor; its
scope includes the initializer blocks inlined into it. A field initializer
keeps its own decline reason.
Seen in 6.0.0 too.
Merging with #3171 (GROOVY-12650): `annotatedMode()` conflicts textually
(resolve as `return annotatedMode(enclosingMethod(),
controller.getClassNode());`), and #3171's marking would then also need to
cover constructors and object initializer statements under `@CompileStatic`.
Until both are in, the `@CompileStatic` rows for `<<` in a constructor and in
an initializer block are left out of `constructorAndInitializerEscapesDecline`.
## Tests
`PackedClosureBoundariesTest.constructorAndInitializerEscapesDecline`
(field, property and append stores and a Serializable cast, in a constructor
and in an initializer block, dynamic, `@TypeChecked` and `@CompileStatic`),
`PackedClosuresTransformTest.escapingClosuresInConstructorsAndInitializersAreDeclined`
and
`PackedClosuresTransformTest.modeOnAConstructorGovernsItsBodyAndInitializers`
fail without the change.
`PackedClosureBoundariesTest.constructorAndInitializerLocalClosuresPack` checks
that non-escaping closures still pack. The packing test classes,
`org.codehaus.groovy.classgen.asm.sc.*` and `groovy.transform.stc.*` pass. The
Checkstyle and CodeNarc gates pass.
> Closure packing ignores constructors and instance initializer blocks:
> escaping closures are packed
> --------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12652
> URL: https://issues.apache.org/jira/browse/GROOVY-12652
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 7.0.0-alpha-1, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> With closure packing enabled ({{-Dgroovy.target.closure.pack=true}} or
> {{@PackedClosures}}), a closure literal in a constructor or an instance
> initializer block bypasses the escape gate, the serialization gate and the
> per-method packing mode:
> {code:groovy}
> class P { P(List<Closure> s) { s << { x -> x } } }
> class Q { Closure f; { f = { x -> x } } }
> def s = []; new P(s)
> [s[0].getClass().name, new Q().f.getClass().name]
> {code}
> *Actual* (dynamic, @TypeChecked and @CompileStatic):
> {{[org.codehaus.groovy.runtime.PackedClosure$Fixed1,
> org.codehaus.groovy.runtime.PackedClosure$Fixed1]}}. Under
> {{@PackedClosures}}, setting a delegate on the escaped closure then throws
> UnsupportedOperationException. {{@PackedClosures}} cannot be placed on a
> constructor ("not allowed on element CONSTRUCTOR").
> *Expected:* {{[P$_closure1, Q$_closure1]}}, the same result as for a method:
> escaping closures keep their class.
> Cause: {{WriterController.getMethodNode()}} is null while a constructor is
> written, so the escape walk, the serialization walk and the annotation lookup
> in ClosureWriter see no enclosing method. Instance initializer blocks are
> inlined into constructors by the Verifier, so they are affected too.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)