Paul King created GROOVY-12749:
----------------------------------
Summary: GEP-27: pack closures that use free names, with a
pluggable resolver in place of a generated class per closure
Key: GROOVY-12749
URL: https://issues.apache.org/jira/browse/GROOVY-12749
Project: Groovy
Issue Type: Bug
Reporter: Paul King
Closure packing (GROOVY-12151) compiles a closure's body to a method of the
enclosing class instead of generating a class for each closure, but only when
the closure has no free names: a name in the body that is not a parameter or
local variable is resolved at run time through the owner, the delegate and
{{resolveStrategy}}, which a hoisted body bound to the owner cannot honour.
Such closures still get a class of their own.
This issue extends packing to those closures, as the next step of GEP-27. Such
a closure is packed "open":
* each use of a free name in the body is rewritten into a call on a leading
{{OpenClosure.Resolver}} parameter of the hoisted method;
* the closure is created with a {{ClassicResolver}}, which implements today's
resolution through the owner, the delegate and {{resolveStrategy}}, so
{{setDelegate}} and {{setResolveStrategy}} keep working after creation;
* the closure is an ordinary fixed-arity {{PackedClosure}}, sharing the
dispatch tables of other packed closures, with no class generated for it.
Behaviour is meant to be identical to today's generated closure classes.
h3. Shape
* Open closures are created through {{ScriptBytecodeAdapter.openClosure}}
({{@GroovyABI(since = "7.0.0")}}), which builds the resolver from the owner, so
only the {{Resolver}} interface appears in generated class files.
* {{PackedClosure}} forwards {{setDelegate}} and {{setResolveStrategy}} to the
attached resolver. An open closure never has the strict guard of an ordinary
packed closure: the resolver replaces it.
* The open path has its own trigger, {{isOpenPackTriggered}}, which the static
closure writer turns off, so {{@CompileStatic}} code is unchanged.
* The feature is off by default and enabled by {{<switch to be named>}}.
h3. Parity with generated closure classes
Tests compile the same source both ways and compare the results for:
* each resolve strategy ({{OWNER_FIRST}}, {{DELEGATE_FIRST}},
{{DELEGATE_ONLY}}, {{OWNER_ONLY}}, {{TO_SELF}}), including the exceptions for a
name that cannot be resolved;
* a delegate and a resolve strategy set after the closure is created;
* a name that both the owner and the delegate define;
* {{methodMissing}} and {{propertyMissing}} on the owner and on the delegate;
* a script as the owner;
* assignment to a free name.
h3. Not packed open (the closure keeps its generated class)
* nested closures;
* {{++}} or {{--}} applied to a free name;
* closures compiled by the static compiler.
h3. Still to address
* what happens when an open closure is passed to statically compiled code,
stored in a field of type {{Closure}}, or serialized; it needs either tests or
a statement that it is unsupported or falls back;
* the GEP-27 text, to describe open closures and the resolver.
h3. Related
* GROOVY-12151 (closure packing), GROOVY-12143 (lambda hoisting)
* PR: https://github.com/apache/groovy/pull/2714
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)