public class ClosureWriter
extends Object
Generates bytecode for closure expressions.
| Modifiers | Name | Description |
|---|---|---|
protected static class |
ClosureWriter.CorrectAccessedVariableVisitor |
Visitor that rewrites VariableExpression nodes whose accessed variable is a FieldNode in an outer class to instead reference the corresponding field in the generated closure inner class. |
protected interface |
ClosureWriter.UseExistingReference |
Marker interface for using existing reference. |
| Modifiers | Name | Description |
|---|---|---|
static String |
GENERATED_CLOSURE_CLASS |
Node metadata key set on a ClosureExpression once it has been compiled to a generated closure class, holding that class's name. |
static String |
OUTER_INSTANCE |
Field name for the outer instance reference. |
static String |
PACKED_METHOD_PREFIX |
Name prefix of hoisted packed-closure bodies (also consulted by the static call-site writer). |
static String |
THIS_OBJECT |
Field name for the this object reference. |
protected WriterController |
controller |
The controller coordinating all bytecode writers for the current class. |
| Constructor and description |
|---|
ClosureWriter(WriterController controller)Creates a closure writer with the given controller. |
| Type Params | Return Type | Name and description |
|---|---|---|
|
protected ConstructorNode |
addConstructor(ClosureExpression expression, Parameter[] localVariableParams, InnerClassNode answer, BlockStatement block)Adds a synthetic public constructor to the closure inner class that accepts
the outer instance, this object, and all captured local variable references. |
|
protected void |
addFieldsForLocalVariables(InnerClassNode closureClass, Parameter[] localVariableParams)Adds synthetic private fields to the closure inner class for each captured local variable parameter, promoting them to Reference holders. |
|
public boolean |
addGeneratedClosureConstructorCall(ConstructorCallExpression call)Emits a super(outerInstance, thisObject) constructor call for a generated
closure class. |
|
protected void |
addSerialVersionUIDField(ClassNode classNode)Adds a synthetic serialVersionUID field to the closure class,
derived from a hash of the class name. |
|
protected BlockStatement |
createBlockStatementForConstructor(ClosureExpression expression, ClassNode outerClass, ClassNode thisClassNode)Creates the block statement for the closure's synthetic constructor, setting up the super(outerInstance, thisObject) call and captured variable references. |
|
protected ClassNode |
createClosureClass(ClosureExpression expression, int modifiers)Creates a new inner class node representing the compiled form of a closure expression. |
|
protected Parameter[] |
getClosureSharedVariables(ClosureExpression expression)Collects the closure-shared local variables referenced by a closure expression as an array of Parameters, using the type chooser to infer each variable's type. |
|
public ClassNode |
getOrAddClosureClass(ClosureExpression expression, int modifiers)Returns the existing generated class for a closure expression, or creates and registers a new one if none exists yet. |
|
protected boolean |
isPackTriggered(ClosureExpression expression, boolean annotated)Whether a packable, non-escaping closure literal should actually be packed. |
|
public static void |
loadReference(String name, WriterController controller)Loads a closure-shared variable reference onto the operand stack, looking it up as a field, local variable, or outer closure field as appropriate. |
|
protected void |
loadThis()Loads the effective this reference onto the operand stack — either the
actual receiver for a regular method, or the result of getThisObject() for
a generated closure/lambda. |
|
protected void |
markHoistedBody(MethodNode hoisted)Marks the hoisted body's compilation mode; the static writer overrides this to compile it statically. |
|
protected boolean |
packedClosureUsesDelegateGuard()Whether the packed adapter installs the runtime delegate guard. |
|
protected ClassNode |
readOnlyCaptureType(String name, ClassNode declaredType, Variable variable)The declared type of a read-only (by-value) capture parameter on the hoisted method. |
|
protected static void |
removeInitialValues(Parameter[] params)Strips initial-value expressions from parameters that are closure-shared, ensuring the closure constructor is not duplicated. |
|
protected String |
triggerDeclineReason(ClosureExpression expression)A trigger-specific decline reason for declineReason, or null if the trigger did
not decline this closure. |
|
public void |
writeClosure(ClosureExpression expression)Generates bytecode for a closure expression. |
|
public void |
writePackedDispatcher()Emits the class's packed-closure dispatch machinery, once per class at the end of class generation (so every hoisted body — including one a hoisted body itself hoisted — has claimed its id): the $packedDispatch$(int, Object[]) table, whose case k casts the
packed argument array ([owner, captured..., args...]) to target k's declared
parameter types and invokes it directly; the array-free per-arity tables
($packedDispatch1$/$packedDispatch2$) doing the same from plain parameter
slots for the hot one/two-value shapes; and the $getPackedDispatchers$() accessor,
whose single invokedynamic site links all three through
IndyInterface.packedDispatchers (delegating to GeneratedDispatcher.bootstrap)
into one constant bundle, lazily on first adapter creation. |
Node metadata key set on a ClosureExpression once it has been compiled to a generated closure class, holding that class's name. Tooling such as the AST browser can read it to show which synthetic class a closure literal became; the compiler itself never consults it. The mark only becomes available after the class-generation phase, since that is when the class is made.
Field name for the outer instance reference.
Name prefix of hoisted packed-closure bodies (also consulted by the static call-site writer).
Field name for the this object reference.
The controller coordinating all bytecode writers for the current class.
Creates a closure writer with the given controller.
controller - the writer controller Adds a synthetic public constructor to the closure inner class that accepts
the outer instance, this object, and all captured local variable references.
expression - the closure expressionlocalVariableParams - parameters for closure-shared local variablesanswer - the closure inner class nodeblock - the constructor body Adds synthetic private fields to the closure inner class for each captured
local variable parameter, promoting them to Reference holders.
closureClass - the closure inner class nodelocalVariableParams - the closure-shared local variable parameters Emits a super(outerInstance, thisObject) constructor call for a generated
closure class. Returns false if the current class is not a generated closure.
call - the constructor call expression representing super(...)true if the closure constructor call was emitted Adds a synthetic serialVersionUID field to the closure class,
derived from a hash of the class name.
classNode - the closure class node to add the field to Creates the block statement for the closure's synthetic constructor, setting up
the super(outerInstance, thisObject) call and captured variable references.
expression - the closure expressionouterClass - the class declaring the closurethisClassNode - the this type in scope at the closure declaration siteCreates a new inner class node representing the compiled form of a closure expression.
expression - the closure expression to compilemodifiers - the access modifiers for the generated classCollects the closure-shared local variables referenced by a closure expression as an array of Parameters, using the type chooser to infer each variable's type.
expression - the closure expressionReturns the existing generated class for a closure expression, or creates and registers a new one if none exists yet.
expression - the closure expression to compile as an inner classmodifiers - the access modifiers for the generated class Whether a packable, non-escaping closure literal should actually be packed. Dynamic compilation
has no proof of delegate-independence, so the only trigger is the @PackedClosures trust
assertion; StaticTypesClosureWriter overrides this to add the
groovy.target.closure.pack flag and the delegate-independence proof.
annotated - whether a non-DISABLED @PackedClosures is in scopeLoads a closure-shared variable reference onto the operand stack, looking it up as a field, local variable, or outer closure field as appropriate.
name - the variable name to loadcontroller - the writer controller for the enclosing class Loads the effective this reference onto the operand stack — either the
actual receiver for a regular method, or the result of getThisObject() for
a generated closure/lambda.
Marks the hoisted body's compilation mode; the static writer overrides this to compile it statically.
Whether the packed adapter installs the runtime delegate guard. The dynamic trust path needs it
(an unverifiable assertion must fail fast on misuse); the static writer proved independence, so
it overrides this to false and a caller-set delegate is then stored and ignored.
The declared type of a read-only (by-value) capture parameter on the hoisted method. The
dynamic writer types it as Object; the static writer overrides this to the capture's
declared/flow-inferred type so the body compiles with static dispatch. This is the seam that
keeps @CompileStatic-specific typing out of the general (dynamic) emitter.
Strips initial-value expressions from parameters that are closure-shared, ensuring the closure constructor is not duplicated.
params - the parameters to mutate in-place A trigger-specific decline reason for declineReason, or null if the trigger did
not decline this closure. Only the static writer has one (a delegate-resolved body); the dynamic
path trusts the annotation and never declines on trigger grounds.
Generates bytecode for a closure expression.
expression - the closure expression Emits the class's packed-closure dispatch machinery, once per class at the end of class
generation (so every hoisted body — including one a hoisted body itself hoisted — has claimed
its id): the $packedDispatch$(int, Object[]) table, whose case k casts the
packed argument array ([owner, captured..., args...]) to target k's declared
parameter types and invokes it directly; the array-free per-arity tables
($packedDispatch1$/$packedDispatch2$) doing the same from plain parameter
slots for the hot one/two-value shapes; and the $getPackedDispatchers$() accessor,
whose single invokedynamic site links all three through
IndyInterface.packedDispatchers (delegating to GeneratedDispatcher.bootstrap)
into one constant bundle, lazily on first adapter creation. A no-op for classes that packed
nothing.
Copyright © 2003-2026 The Apache Software Foundation. All rights reserved.