Class PackedClosure
- All Implemented Interfaces:
GroovyCallable<Object>,GroovyObject,Serializable,Cloneable,Runnable,Callable<Object>
- Direct Known Subclasses:
PackedClosure.Fixed0,PackedClosure.Fixed1,PackedClosure.Fixed2,PackedClosure.Fixed3,PackedClosure.Fixed4,PackedClosure.FixedIt,PackedClosure.FixedN
Closure adapter family for @PackedClosures compact closure
compilation.
Normally the Groovy compiler generates one inner class per closure literal
(e.g. Owner$_method_closure1). Under @PackedClosures an eligible
closure body is instead hoisted into a synthetic method on the enclosing ("owner")
class, and the closure literal is replaced by an instance of the fixed-arity family
member matching its declared parameter count (PackedClosure.Fixed0..PackedClosure.Fixed4,
PackedClosure.FixedIt for implicit-parameter literals, PackedClosure.FixedN for higher and vararg
arities), which dispatches back to that method. This removes the per-closure generated
class (and the deeply-nested $_closure1$_closure2$_closure3 name explosion)
while still yielding a real groovy.lang.Closure instance, so features that
operate through call() (iteration, curry, memoize,
trampoline) continue to work — and the family member's declared doCall
signature(s) give class-level introspection (SAM-overload selection, MOP method
selection) exactly the view a generated closure class would present.
Dispatch goes through the hosting class's GeneratedDispatcher tables: the adapter
holds the class's shared dispatchers, the hoisted method's compile-time id, and its own
dispatch receiver (captured at construction, so dehydrate()/rehydrate()
leave the closure callable). A target taking one or two values beyond the receiver — the
overwhelmingly common closure shapes — dispatches through the array-free per-arity tables,
passing the receiver, captured values and arguments as plain parameters; anything else
packs [receiver, captured..., args...] into one array for the general table.
Either way the chain is ordinary, JIT-friendly bytecode — no reflection, and no
per-instance MethodHandle (which the JIT cannot constant-fold, making its invoker
path many times slower than a direct call). The id binds the exact hoisted method, so an
inherited packed closure can never misdispatch to a same-named method a subclass happens
to declare. Argument adaptation on every route replicates metaclass dispatch on a
generated closure class: arity mismatches raise MissingMethodException, a single
List destructures across a non-one-parameter signature, trailing-array parameters
vararg-collect, and per-parameter coercion follows the metaclass compatibility rules
(including Closure-to-SAM).
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classFixed-arity members of the adapter family: the compiler instantiates the member matching the literal's declared parameter count (PackedClosure.FixedNserves higher arities), so a packed closure's arity is visible to class-level introspection exactly as on a generated closure class — notablyMetaClassHelper's SAM-overload disambiguation, which reflects the argument class's declareddoCalland cannot consult the instance.static final classstatic final classstatic final classstatic final classstatic final classThe implicit-parameter literal ({ it * 2 }): a generated closure class declares bothdoCall()anddoCall(Object)for it, which class-level introspection reads as the fuzzy "0 or 1" arity that lets the closure match both zero- and one-parameter SAM overloads (GROOVY-10905) — so this member declares the same pair.static final classArities above four (rare) and vararg-shaped literals (trailing array parameter, e.g. -
Field Summary
Fields inherited from class groovy.lang.Closure
DELEGATE_FIRST, DELEGATE_ONLY, DONE, IDENTITY, maximumNumberOfParameters, OWNER_FIRST, OWNER_ONLY, parameterTypes, SKIP, TO_SELF -
Constructor Summary
ConstructorsConstructorDescriptionPackedClosure(Object owner, GeneratedDispatcher.Bundle dispatchers, int id, String method, Object[] captured, Class[] visibleTypes, boolean strict) -
Method Summary
Modifier and TypeMethodDescriptionfinal ObjectInvokes the closure with given argument(s), returning any value if applicable.final ObjectInvokes the closure with given argument(s), returning any value if applicable.static PackedClosurecreate(Object owner, GeneratedDispatcher.Bundle dispatchers, int id, String method, Object[] captured, Class[] visibleTypes, boolean strict, boolean implicit, boolean vararg) The single entry point for creating a packed closure, reached from generated bytecode viaScriptBytecodeAdapter.packedClosure(java.lang.Object, java.lang.Object, int, java.lang.String, java.lang.Object[], java.lang.Class[], boolean, boolean, boolean)(so neither this class nor the fixed-arity family members appear on the emitted-bytecode surface).final ObjectdispatchAll(Object[] args) The single dispatch entry every route funnels into: thecalllanes, theFixed*subclasses' arity-truedoCalls, and thePackedClosureMetaClassshort-circuit.voidsetDelegate(Object delegate) Allows the delegate to be changed such as when performing markup buildingvoidsetResolveStrategy(int resolveStrategy) Sets the strategy which the closure uses to resolve property references and methods.toString()Names the hoisted body, so diagnostics identify the literal the way a generated class's name would (Script$_run_closure1becomesScript.$packed$closure$0).Methods inherited from class groovy.lang.Closure
andThen, andThenSelf, andThenSelf, asWritable, call, checkForReferenceCycle, clone, compose, composeSelf, composeSelf, curry, curry, dehydrate, getDelegate, getDirective, getMaximumNumberOfParameters, getOwner, getParameterTypes, getProperty, getResolveStrategy, getThisObject, isCase, leftShift, leftShift, memoize, memoizeAtLeast, memoizeAtMost, memoizeBetween, mopUnperturbed, ncurry, ncurry, rcurry, rcurry, rehydrate, rightShift, run, setDirective, setMetaClass, setProperty, throwRuntimeException, trampoline, trampolineMethods inherited from class groovy.lang.GroovyObjectSupport
getMetaClassMethods inherited from class java.lang.Object
equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, waitMethods inherited from interface groovy.lang.GroovyObject
invokeMethod
-
Constructor Details
-
PackedClosure
public PackedClosure(Object owner, GeneratedDispatcher.Bundle dispatchers, int id, String method, Object[] captured, Class[] visibleTypes, boolean strict) - Parameters:
owner- the enclosing instance the hoisted method lives on (also used as thisObject; the enclosing class itself for a hoisted method in a static context)dispatchers- the hosting class's shared dispatch tables (seeGeneratedDispatcher)id- the hoisted method's compile-time-assigned index in those tablesmethod- the name of the hoisted synthetic method (kept for diagnostics only)captured- values captured from the enclosing scope, passed before the call arguments on every dispatch (a written capture passes its sharedgroovy.lang.Referenceunchanged, so writes still propagate)visibleTypes- the closure's declared parameter types, so callers that key behaviour ongetParameterTypes()(DGM arity/type decisions) and argument adaptation (vararg collection into a trailing array parameter) behave exactly as with a generated closure classstrict- whether the delegate guard throws.truefor the dynamic trust path (an unverifiable@PackedClosuresassertion must fail fast on misuse);falsewhen the type checker PROVED every free name owner-resolved.
-
-
Method Details
-
create
public static PackedClosure create(Object owner, GeneratedDispatcher.Bundle dispatchers, int id, String method, Object[] captured, Class[] visibleTypes, boolean strict, boolean implicit, boolean vararg) The single entry point for creating a packed closure, reached from generated bytecode viaScriptBytecodeAdapter.packedClosure(java.lang.Object, java.lang.Object, int, java.lang.String, java.lang.Object[], java.lang.Class[], boolean, boolean, boolean)(so neither this class nor the fixed-arity family members appear on the emitted-bytecode surface). The runtime shape switch is negligible against the object-creation cost.implicitmarks an implicit-itliteral (fuzzy 0/1 arity),vararga trailing-array parameter; both need the varargsdoCallthat onlyFixedIt/FixedNdeclare. -
setDelegate
Description copied from class:ClosureAllows the delegate to be changed such as when performing markup building- Overrides:
setDelegatein classClosure<Object>- Parameters:
delegate- the new delegate
-
setResolveStrategy
public void setResolveStrategy(int resolveStrategy) Description copied from class:ClosureSets the strategy which the closure uses to resolve property references and methods. The default is Closure.OWNER_FIRST- Overrides:
setResolveStrategyin classClosure<Object>- Parameters:
resolveStrategy- The resolve strategy to set- See Also:
-
toString
Names the hoisted body, so diagnostics identify the literal the way a generated class's name would (Script$_run_closure1becomesScript.$packed$closure$0). -
call
Description copied from class:ClosureInvokes the closure with given argument(s), returning any value if applicable. -
call
Description copied from class:ClosureInvokes the closure with given argument(s), returning any value if applicable. -
dispatchAll
The single dispatch entry every route funnels into: thecalllanes, theFixed*subclasses' arity-truedoCalls, and thePackedClosureMetaClassshort-circuit. Deliberately NOT nameddoCall: a public varargsdoCall(Object[])inherited by every family member would, under MOP-routed dispatch, exact-match a singleObject[]argument, beatdoCall(Object), and spread the array — a generated closure class declares no such method, so a classed closure never spreads.
-