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)

Reply via email to