Package org.codehaus.groovy.classgen.asm

# Class CompileStack

java.lang.Object
org.codehaus.groovy.classgen.asm.CompileStack

public class CompileStack extends Object
Manages different aspects of the code of a code block like handling labels, defining variables, and scopes. After a MethodNode is visited clear should be called, for initialization the method init should be used.

Some Notes:

- every push method will require a later pop call
- method parameters may define a category 2 variable, so don't ignore the type stored in the variable object
- the index of the variable may not be as assumed when the variable is a parameter of a method because the parameter may be used in a closure, so don't ignore the stored variable index
- the names of temporary variables can be ignored. The names are only used for debugging and do not conflict with each other or normal variables. For accessing, the index of the variable must be used.
- never mix temporary and normal variables by changes to this class. While the name is very important for a normal variable, it is only a helper construct for temporary variables. That means for example a name for a temporary variable can be used multiple times without conflict. So mixing them both may lead to the problem that a normal or temporary variable is hidden or even removed. That must not happen!

See Also:
  - `AsmClassGenerator`

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static class`
  `CompileStack.BlockRecorder`
  Records a block for finally or other special handling.
  `protected static class`
  `CompileStack.LabelRange`
  Represents a label range for exception handling or other scoping.
  `static final class`
  `CompileStack.SwitchExpressionContext`
  Join-point information for a switch expression being compiled (GROOVY-12255).

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `CompileStack(WriterController controller)`
  Creates a CompileStack managed by the given controller.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `addExceptionBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label goal, String sig)`
  Adds a typed or untyped exception handler to the exception table for the current method.
  `void`
  `applyBlockRecorder()`
  Applies all currently active finally blocks for a normal (non-jump) fall-through.
  `void`
  `applyFinallyBlocks(org.objectweb.asm.Label label, boolean isBreakLabel)`
  Applies any pending finally blocks on the path to `label`.
  `void`
  `clear()`
  Clears the state of the class.
  `boolean`
  `containsVariable(String name)`
  `org.objectweb.asm.Label`
  `createLocalLabel(String name)`
  Creates or returns the label for the given name.
  `int`
  `defineTemporaryVariable(String name, boolean store)`
  Creates a temporary variable.
  `int`
  `defineTemporaryVariable(String name, ClassNode type, boolean store)`
  Creates a temporary variable.
  `int`
  `defineTemporaryVariable(Variable var, boolean store)`
  Creates a temporary variable.
  `BytecodeVariable`
  `defineVariable(Variable v, boolean initFromStack)`
  Defines a new Variable using an AST variable.
  `BytecodeVariable`
  `defineVariable(Variable v, ClassNode variableType, boolean initFromStack)`
  Defines a variable for the given AST variable using an explicitly specified bytecode type (which may differ from the declared type, e.g.
  `org.objectweb.asm.Label`
  `getBreakLabel()`
  Returns the current break label, or `null` if not inside a breakable construct.
  `org.objectweb.asm.Label`
  `getContinueLabel()`
  Returns the current continue label, or `null` if not inside a loop.
  `org.objectweb.asm.Label`
  `getLabel(String name)`
  Returns the label for the given name.
  `org.objectweb.asm.Label`
  `getNamedBreakLabel(String name)`
  `org.objectweb.asm.Label`
  `getNamedContinueLabel(String name)`
  `VariableScope`
  `getScope()`
  Returns the current variable scope.
  `CompileStack.SwitchExpressionContext`
  `getSwitchExpressionContext()`
  Returns the innermost switch-expression context, or `null` if the current method is not compiling a `SwitchExpression`.
  `BytecodeVariable`
  `getVariable(String variableName)`
  Returns a variable by name, throwing a `GroovyBugError` if it does not exist.
  `BytecodeVariable`
  `getVariable(String variableName, boolean mustExist)`
  Returns a normal variable.
  `boolean`
  `hasBlockRecorder()`
  Returns `true` if there are any active finally/synchronized blocks on the stack.
  `void`
  `hidePatternVariablesExcept(Collection<String> candidates, Collection<String> liveNames)`
  Among `candidates`, hides every name that is not in `liveNames`.
  `void`
  `hideVariable(String name)`
  Hides a named variable from name resolution while leaving its slot index allocated.
  `void`
  `init(VariableScope scope, Parameter[] parameters)`
  initializes this class for a MethodNode.
  `boolean`
  `isImplicitThis()`
  Returns `true` if the current `this` reference is implicit (no explicit qualifier).
  `boolean`
  `isInSpecialConstructorCall()`
  Returns `true` if the current context is inside a special constructor call (`super(...)` or `this(...)`).
  `boolean`
  `isLHS()`
  Returns `true` if the current expression is being compiled as a left-hand side.
  `Set<String>`
  `patternVariablesIntroducedSince(Map<String,BytecodeVariable> before)`
  Names whose registry entry is new or whose `BytecodeVariable` identity differs from `before` (re-bind of the same name).
  `void`
  `pop()`
  Restores the compilation state that was saved by the matching `push*` call, and emits end-labels for all local variables declared within the scope that is being popped.
  `void`
  `popBlockRecorderVisit(CompileStack.BlockRecorder finallyBlock)`
  Removes `finallyBlock` from the visited-block set after its inline emission is complete.
  `void`
  `popImplicitThis()`
  Pops the top implicit-this flag, restoring the previous state.
  `void`
  `popLHS()`
  Pops the top left-hand-side flag, restoring the previous LHS state.
  `void`
  `popSwitchExpression()`
  Pops the innermost switch-expression context and restores the previous compile-stack state (GROOVY-12255).
  `void`
  `pushBlockRecorder(CompileStack.BlockRecorder recorder)`
  Pushes a new `CompileStack.BlockRecorder` (finally or synchronized guard) onto the block-recorder stack and saves state so that `pop()` will remove it.
  `void`
  `pushBlockRecorderVisit(CompileStack.BlockRecorder finallyBlock)`
  Marks `finallyBlock` as currently being visited so that recursive finally-block application does not re-enter it.
  `void`
  `pushBooleanExpression()`
  Deprecated.
  Use `pushState()` directly.
  `org.objectweb.asm.Label`
  `pushBreakable(List<String> labelNames)`
  Creates a new break label and an element for the state stack so pop has to be called later.
  `void`
  `pushImplicitThis(boolean implicitThis)`
  Pushes a new implicit-this flag onto the stack.
  `void`
  `pushInSpecialConstructorCall()`
  Enters a special constructor call context (`super(...)` or `this(...)`).
  `void`
  `pushLHS(boolean lhs)`
  Pushes a new left-hand-side flag onto the LHS stack.
  `void`
  `pushLoop(String labelName)`
  Deprecated.
  `void`
  `pushLoop(List<String> labelNames)`
  Should be called when descending into a loop that does not define a scope Creates a element for the state stack so pop has to be called later.
  `void`
  `pushLoop(VariableScope scope, String labelName)`
  Deprecated.
  `void`
  `pushLoop(VariableScope scope, List<String> labelNames)`
  Should be called when descending into a loop that defines also a scope.
  `void`
  `pushState()`
  Saves the current compilation state onto an internal stack so that it can be restored by a later call to `pop()`.
  `org.objectweb.asm.Label`
  `pushSwitch()`
  Creates a new break label and an element for the state stack so pop has to be called later.
  `org.objectweb.asm.Label`
  `pushSwitch(List<String> labelNames)`
  Creates a new break label, registers it as the named break target for each label name, and pushes an element onto the state stack so that a later call to `pop()` restores the previous state.
  `org.objectweb.asm.Label`
  `pushSwitchExpression(ClassNode resultType)`
  Pushes a switch-expression context so that `yield` can jump to the expression's join point (GROOVY-12255).
  `void`
  `pushVariableScope(VariableScope scope)`
  Causes the state-stack to add an element and sets the given scope as new current variable scope.
  `void`
  `recordPatternVariable(BytecodeVariable variable)`
  Records that `variable` was allocated as an `instanceof` pattern binding.
  `void`
  `removeVar(int variableIndex)`
  Indicates that the specified temporary variable is no longer used.
  `CompileStack.SwitchExpressionContext`
  `requireSwitchExpressionContext()`
  Returns the innermost switch-expression context, or throws if `yield` is being compiled outside any switch expression.
  `Map<String,BytecodeVariable>`
  `snapshotPatternVariables()`
  Snapshot of the pattern-variable registry (name → slot).
  `void`
  `visitExcludedFinally(CompileStack.BlockRecorder recorder, Runnable visit)`
  Inlines an excluded finally block via `visit` while hiding the variables of the current (try) scope, then fully restores that scope — including its variables.
  `void`
  `writeExceptionTable(CompileStack.BlockRecorder block, org.objectweb.asm.Label goal, String sig)`
  Writes the exception-table entries for all label ranges recorded in `block`.

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### CompileStack

    public CompileStack(WriterController controller)
    Creates a CompileStack managed by the given controller.
    Parameters:
      `controller` - the writer controller for the current compilation

- ## Method Details

  - ### getBreakLabel

    public org.objectweb.asm.Label getBreakLabel()
    Returns the current break label, or `null` if not inside a breakable construct.

  - ### getContinueLabel

    public org.objectweb.asm.Label getContinueLabel()
    Returns the current continue label, or `null` if not inside a loop.

  - ### getScope

    public VariableScope getScope()
    Returns the current variable scope.

  - ### pushState

    public void pushState()
    Saves the current compilation state onto an internal stack so that it can be restored by a later call to `pop()`. Every `push*` method calls this internally; it is also exposed for callers that need a bare state snapshot without additional label setup.

  - ### removeVar

    public void removeVar(int variableIndex)
    Indicates that the specified temporary variable is no longer used.

  - ### pop

    public void pop()
    Restores the compilation state that was saved by the matching `push*` call, and emits end-labels for all local variables declared within the scope that is being popped.

  - ### defineTemporaryVariable

    public int defineTemporaryVariable(Variable var, boolean store)
    Creates a temporary variable.
    Parameters:
      `var` - specifies name and type
      `store` - defines if the toplevel argument of the stack should be stored
    Returns:
      the index used for this temporary variable

  - ### getVariable

    public BytecodeVariable getVariable(String variableName)
    Returns a variable by name, throwing a `GroovyBugError` if it does not exist. Convenience overload of `getVariable(String, boolean)` with `mustExist = true`.
    Parameters:
      `variableName` - the name to look up
    Returns:
      the corresponding `BytecodeVariable`

  - ### getVariable

    public BytecodeVariable getVariable(String variableName, boolean mustExist)
    Returns a normal variable.

    If `mustExist` is true and the normal variable doesn't exist, then this method will throw a GroovyBugError. It is not the intention of this method to let this happen! And the exception should not be used for flow control - it is just acting as an assertion. If the exception is thrown then it indicates a bug in the class using CompileStack. This method can also not be used to return a temporary variable. Temporary variables are not normal variables.

    Parameters:
      `variableName` - name of the variable
      `mustExist` - throw exception if variable does not exist
    Returns:
      the normal variable or null if not found (and `mustExist` not true)

  - ### defineTemporaryVariable

    public int defineTemporaryVariable(String name, boolean store)
    Creates a temporary variable.
    Parameters:
      `name` - defines type and name
      `store` - defines if the top-level argument of the stack should be stored
    Returns:
      the index used for this temporary variable

  - ### defineTemporaryVariable

    public int defineTemporaryVariable(String name, ClassNode type, boolean store)
    Creates a temporary variable.
    Parameters:
      `name` - the variable name
      `type` - the variable type
      `store` - indicates if the top-level argument of the stack should be stored
    Returns:
      the index used for this temporary variable

  - ### clear

    public void clear()
    Clears the state of the class. This method should be called after a MethodNode is visited. Note that a call to init will fail if clear is not called before

  - ### addExceptionBlock

    public void addExceptionBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label goal, String sig)
    Adds a typed or untyped exception handler to the exception table for the current method. Typed entries (non-null `sig`) are emitted before untyped ones to ensure correct handler ordering in the bytecode.
    Parameters:
      `start` - the start label of the guarded range
      `end` - the end label of the guarded range
      `goal` - the label of the handler block
      `sig` - the internal name of the caught exception type, or `null` for a catch-all handler

  - ### init

    public void init(VariableScope scope, Parameter[] parameters)
    initializes this class for a MethodNode. This method will automatically define variables for the method parameters and will create references if needed. The created variables can be accessed by calling getVariable().

  - ### pushVariableScope

    public void pushVariableScope(VariableScope scope)
    Causes the state-stack to add an element and sets the given scope as new current variable scope. Creates an element for the state stack so pop has to be called later

  - ### pushLoop

    public void pushLoop(VariableScope scope, List<String> labelNames)
    Should be called when descending into a loop that defines also a scope. Calls pushVariableScope and prepares labels for a loop structure. Creates an element for the state stack so pop has to be called later

  - ### pushLoop

    @Deprecated(since="6.0.0") public void pushLoop(VariableScope scope, String labelName)
    Deprecated.
    Should be called when descending into a loop that defines also a scope. Calls pushVariableScope and prepares labels for a loop structure. Creates a element for the state stack so pop has to be called later.

  - ### pushLoop

    public void pushLoop(List<String> labelNames)
    Should be called when descending into a loop that does not define a scope Creates a element for the state stack so pop has to be called later.

  - ### pushLoop

    @Deprecated(since="6.0.0") public void pushLoop(String labelName)
    Deprecated.
    Should be called when descending into a loop that does not define a scope. Creates a element for the state stack so pop has to be called later.

  - ### pushSwitch

    public org.objectweb.asm.Label pushSwitch()
    Creates a new break label and an element for the state stack so pop has to be called later.
    Returns:
      the break label

  - ### pushSwitch

    public org.objectweb.asm.Label pushSwitch(List<String> labelNames)
    Creates a new break label, registers it as the named break target for each label name, and pushes an element onto the state stack so that a later call to `pop()` restores the previous state.
    Parameters:
      `labelNames` - the statement labels on the switch (may be `null`)
    Returns:
      the break label
    Since:
      6.0.0

  - ### pushSwitchExpression

    public org.objectweb.asm.Label pushSwitchExpression(ClassNode resultType)
    Pushes a switch-expression context so that `yield` can jump to the expression's join point (GROOVY-12255). The join label is installed as this frame's `breakLabel` (same as `pushSwitch()`) so `applyFinallyBlocks(org.objectweb.asm.Label, boolean)` only inlines recorders pushed after this call. A second `pushState()` then snapshots that label; without it, a `try`/`synchronized` wrapping the whole expression would be treated as intervening.
    Parameters:
      `resultType` - the unified result type left on the operand stack at the join point
    Returns:
      the end label of the switch expression
    Since:
      6.0.0

  - ### getSwitchExpressionContext

    public CompileStack.SwitchExpressionContext getSwitchExpressionContext()
    Returns the innermost switch-expression context, or `null` if the current method is not compiling a `SwitchExpression`.
    Since:
      6.0.0

  - ### requireSwitchExpressionContext

    public CompileStack.SwitchExpressionContext requireSwitchExpressionContext()
    Returns the innermost switch-expression context, or throws if `yield` is being compiled outside any switch expression.
    Since:
      6.0.0

  - ### popSwitchExpression

    public void popSwitchExpression()
    Pops the innermost switch-expression context and restores the previous compile-stack state (GROOVY-12255).
    Since:
      6.0.0

  - ### pushBreakable

    public org.objectweb.asm.Label pushBreakable(List<String> labelNames)
    Creates a new break label and an element for the state stack so pop has to be called later.
    Returns:
      the break label
    Since:
      6.0.0

  - ### getNamedBreakLabel

    public org.objectweb.asm.Label getNamedBreakLabel(String name)
    Returns:
      the `break` label for name

  - ### getNamedContinueLabel

    public org.objectweb.asm.Label getNamedContinueLabel(String name)
    Returns:
      the `continue` label for name

  - ### getLabel

    public org.objectweb.asm.Label getLabel(String name)
    Returns the label for the given name.

  - ### createLocalLabel

    public org.objectweb.asm.Label createLocalLabel(String name)
    Creates or returns the label for the given name.

  - ### pushBooleanExpression

    @Deprecated(since="5.0.0") public void pushBooleanExpression()
    Deprecated.
    Use `pushState()` directly.
    Because a boolean Expression may not be evaluated completely it is important to keep the registers clean.

  - ### defineVariable

    public BytecodeVariable defineVariable(Variable v, boolean initFromStack)
    Defines a new Variable using an AST variable.
    Parameters:
      `initFromStack` - if true the last element of the stack will be used to initialize the new variable. If false null will be used.

  - ### defineVariable

    public BytecodeVariable defineVariable(Variable v, ClassNode variableType, boolean initFromStack)
    Defines a variable for the given AST variable using an explicitly specified bytecode type (which may differ from the declared type, e.g. when widening for closure-shared variables).
    Parameters:
      `v` - the AST variable to define
      `variableType` - the bytecode type to assign to the slot
      `initFromStack` - if `true` the top of the operand stack is stored into the new slot; otherwise a default/null value is used
    Returns:
      the created `BytecodeVariable`

  - ### containsVariable

    public boolean containsVariable(String name)
    Parameters:
      `name` - the name of the variable of interest
    Returns:
      true if a variable is already defined

  - ### recordPatternVariable

    public void recordPatternVariable(BytecodeVariable variable)
    Records that `variable` was allocated as an `instanceof` pattern binding. The slot is already present in `stackVariables`; this registry lets later control-flow boundaries hide names while keeping the slot indices.

    GROOVY-12242 / JEP 394: pattern slots are defined during condition evaluation (needed for short-circuit `&&` RHS). After the condition, names that are not live on a path must be hidden so (1) a same-named local can be declared on that path and (2) name lookup does not load a slot that scoping forbids.

    Re-recording the same name replaces the previous registry entry (a later condition may re-bind the name to a new slot).

    Parameters:
      `variable` - the pattern local just defined; ignored if `null`
    See Also:
      - `hideVariable(String)`
      - `hidePatternVariablesExcept(Collection, Collection)`

  - ### hideVariable

    public void hideVariable(String name)
    Hides a named variable from name resolution while leaving its slot index allocated. The name becomes free in the current state frame only.

    Call this inside a `pushState()`/`pop()` region so that `pop` restores the previous name map (and thus re-exposes the variable if it was visible in the outer frame). Using hide without a matching push permanently removes the name from the current frame — the intended form for “after this construct the name is no longer in scope”.

    This is the CompileStack-native mechanism for flow-scoped pattern variables (GROOVY-12242). It deliberately does not free the register: the value may still be needed on another path that re-exposes the same slot via pop or a later path that still lists the name as live.

    The variable's LocalVariableTable range is closed at the hide point so debug info never claims visibility over a region where flow scoping has removed the name; if a later `pop()` re-exposes the variable, a new range is opened (emitted as an additional table entry by `clear`).

    Parameters:
      `name` - the variable name to hide; no-op if absent

  - ### snapshotPatternVariables

    public Map<String,BytecodeVariable> snapshotPatternVariables()
    Snapshot of the pattern-variable registry (name → slot). Used to compute which names a condition evaluation introduced or re-bound by comparing `BytecodeVariable` identity before and after the visit. A name-only set is insufficient when a later condition reuses a pattern name.
    Returns:
      an immutable copy of the registry; empty if none recorded

  - ### patternVariablesIntroducedSince

    public Set<String> patternVariablesIntroducedSince(Map<String,BytecodeVariable> before)
    Names whose registry entry is new or whose `BytecodeVariable` identity differs from `before` (re-bind of the same name).
    Parameters:
      `before` - snapshot from `snapshotPatternVariables()` taken before evaluating the condition; may be empty
    Returns:
      names introduced or re-bound by the condition that just ran

  - ### hidePatternVariablesExcept

    public void hidePatternVariablesExcept(Collection<String> candidates, Collection<String> liveNames)
    Among `candidates`, hides every name that is not in `liveNames`. Only names that are both candidates and recorded pattern variables are affected; outer pattern variables not listed in `candidates` stay untouched.

    Must be used with `pushState()`/`pop()` (or as a permanent hide on the current frame) as described by `hideVariable(String)`.

    Parameters:
      `candidates` - names introduced by the current condition; may be empty
      `liveNames` - names that must remain visible; may be empty or `null` (both mean hide every candidate)

  - ### applyFinallyBlocks

    public void applyFinallyBlocks(org.objectweb.asm.Label label, boolean isBreakLabel)
    Applies any pending finally blocks on the path to `label`. Walks the state stack to determine which finally blocks are between the current position and the target label and inlines their bytecode.
    Parameters:
      `label` - the target label (break or continue destination)
      `isBreakLabel` - `true` for a break label, `false` for continue

  - ### applyBlockRecorder

    public void applyBlockRecorder()
    Applies all currently active finally blocks for a normal (non-jump) fall-through.

  - ### hasBlockRecorder

    public boolean hasBlockRecorder()
    Returns `true` if there are any active finally/synchronized blocks on the stack.

  - ### pushBlockRecorder

    public void pushBlockRecorder(CompileStack.BlockRecorder recorder)
    Pushes a new `CompileStack.BlockRecorder` (finally or synchronized guard) onto the block-recorder stack and saves state so that `pop()` will remove it.
    Parameters:
      `recorder` - the recorder to push

  - ### pushBlockRecorderVisit

    public void pushBlockRecorderVisit(CompileStack.BlockRecorder finallyBlock)
    Marks `finallyBlock` as currently being visited so that recursive finally-block application does not re-enter it.
    Parameters:
      `finallyBlock` - the block being visited

  - ### popBlockRecorderVisit

    public void popBlockRecorderVisit(CompileStack.BlockRecorder finallyBlock)
    Removes `finallyBlock` from the visited-block set after its inline emission is complete.
    Parameters:
      `finallyBlock` - the block that finished being visited

  - ### visitExcludedFinally

    public void visitExcludedFinally(CompileStack.BlockRecorder recorder, Runnable visit)
    Inlines an excluded finally block via `visit` while hiding the variables of the current (try) scope, then fully restores that scope — including its variables.

    Popping the current scope keeps try-block locals out of scope in the finally body (GROOVY-4721); restoring the saved variables afterwards ensures any subsequent re-emission of the same statement (e.g. the `OptimizingStatementWriter` fast/slow fork) still resolves those locals rather than falling back to property access (GROOVY-12062).

    Parameters:
      `recorder` - the block recorder being visited
      `visit` - emits the finally body

  - ### writeExceptionTable

    public void writeExceptionTable(CompileStack.BlockRecorder block, org.objectweb.asm.Label goal, String sig)
    Writes the exception-table entries for all label ranges recorded in `block`.
    Parameters:
      `block` - the block recorder containing the try-range labels
      `goal` - the handler label
      `sig` - the internal name of the caught type, or `null` for catch-all

  - ### isLHS

    public boolean isLHS()
    Returns `true` if the current expression is being compiled as a left-hand side.

  - ### pushLHS

    public void pushLHS(boolean lhs)
    Pushes a new left-hand-side flag onto the LHS stack.
    Parameters:
      `lhs` - `true` if the next expression is compiled as an assignment target

  - ### popLHS

    public void popLHS()
    Pops the top left-hand-side flag, restoring the previous LHS state.

  - ### isImplicitThis

    public boolean isImplicitThis()
    Returns `true` if the current `this` reference is implicit (no explicit qualifier).

  - ### pushImplicitThis

    public void pushImplicitThis(boolean implicitThis)
    Pushes a new implicit-this flag onto the stack.
    Parameters:
      `implicitThis` - `true` if `this` is used implicitly in the current context

  - ### popImplicitThis

    public void popImplicitThis()
    Pops the top implicit-this flag, restoring the previous state.

  - ### isInSpecialConstructorCall

    public boolean isInSpecialConstructorCall()
    Returns `true` if the current context is inside a special constructor call (`super(...)` or `this(...)`).

  - ### pushInSpecialConstructorCall

    public void pushInSpecialConstructorCall()
    Enters a special constructor call context (`super(...)` or `this(...)`). Pushes state so that `pop()` will restore the previous context.