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

public abstract class PackedClosure extends Closure<Object>
The shared 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:
  • 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 (see GeneratedDispatcher)
      id - the hoisted method's compile-time-assigned index in those tables
      method - 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 shared groovy.lang.Reference unchanged, so writes still propagate)
      visibleTypes - the closure's declared parameter types, so callers that key behaviour on getParameterTypes() (DGM arity/type decisions) and argument adaptation (vararg collection into a trailing array parameter) behave exactly as with a generated closure class
      strict - whether the delegate guard throws. true for the dynamic trust path (an unverifiable @PackedClosures assertion must fail fast on misuse); false when 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 via ScriptBytecodeAdapter.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. implicit marks an implicit-it literal (fuzzy 0/1 arity), vararg a trailing-array parameter; both need the varargs doCall that only FixedIt/FixedN declare.
    • setDelegate

      public void setDelegate(Object delegate)
      Description copied from class: Closure
      Allows the delegate to be changed such as when performing markup building
      Overrides:
      setDelegate in class Closure<Object>
      Parameters:
      delegate - the new delegate
    • setResolveStrategy

      public void setResolveStrategy(int resolveStrategy)
      Description copied from class: Closure
      Sets the strategy which the closure uses to resolve property references and methods. The default is Closure.OWNER_FIRST
      Overrides:
      setResolveStrategy in class Closure<Object>
      Parameters:
      resolveStrategy - The resolve strategy to set
      See Also:
    • toString

      public String toString()
      Names the hoisted body, so diagnostics identify the literal the way a generated class's name would (Script$_run_closure1 becomes Script.$packed$closure$0).
      Overrides:
      toString in class Object
    • call

      public final Object call(Object... args)
      Description copied from class: Closure
      Invokes the closure with given argument(s), returning any value if applicable.
      Overrides:
      call in class Closure<Object>
      Returns:
      The value if applicable or null if there is no return statement in the closure.
    • call

      public final Object call(Object arguments)
      Description copied from class: Closure
      Invokes the closure with given argument(s), returning any value if applicable.
      Overrides:
      call in class Closure<Object>
      Parameters:
      arguments - could be a single value or a List of values
      Returns:
      The value if applicable or null if there is no return statement in the closure.
    • dispatchAll

      public final Object dispatchAll(Object[] args)
      The single dispatch entry every route funnels into: the call lanes, the Fixed* subclasses' arity-true doCalls, and the PackedClosureMetaClass short-circuit. Deliberately NOT named doCall: a public varargs doCall(Object[]) inherited by every family member would, under MOP-routed dispatch, exact-match a single Object[] argument, beat doCall(Object), and spread the array — a generated closure class declares no such method, so a classed closure never spreads.