From e95a3db9ef2cf26e2ed7dca7ac976f9a85dbe17e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 21 Dec 2022 17:52:55 +0100
Subject: [PATCH 01/26] Add BlockListener support...

This feature allows extension authors to register a IBlockListener for
a feature to observe the execution of a feature in more detail.
This surfaces some of Spock's idiosyncrasies, for example interaction
assertions are actually setup right before entering the preceding
`when`-block as well as being evaluated on leaving the `when`-block
before actually entering the `then`-block.

The only valid block description is a constant String, although some
users mistakenly try to use a dynamic GString. Using anything other
than a String, will be treated as a separate statement and thus ignored.
---
 .../spockframework/compiler/AstNodeCache.java |  3 +
 .../compiler/DeepBlockRewriter.java           | 30 ++++++++++
 .../lang/ISpecificationContext.java           |  8 ++-
 .../runtime/SpecificationContext.java         | 11 ++++
 .../spockframework/runtime/SpockRuntime.java  | 14 +++++
 .../runtime/extension/IBlockListener.java     |  8 +++
 .../runtime/model/BlockInfo.java              |  8 +++
 .../runtime/model/FeatureInfo.java            | 11 ++++
 .../runtime/BlockListenerSpec.groovy          | 59 +++++++++++++++++++
 9 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
 create mode 100644 spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index 5c3990af3f..e905ea2dea 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -46,6 +46,7 @@ public class AstNodeCache {
   public final ClassNode SpecificationContext = ClassHelper.makeWithoutCaching(SpecificationContext.class);
   public final ClassNode DataVariableMultiplication = ClassHelper.makeWithoutCaching(DataVariableMultiplication.class);
   public final ClassNode DataVariableMultiplicationFactor = ClassHelper.makeWithoutCaching(DataVariableMultiplicationFactor.class);
+  public final ClassNode BlockInfo = ClassHelper.makeWithoutCaching(BlockInfo.class);
 
   public final MethodNode SpecInternals_GetSpecificationContext =
       SpecInternals.getDeclaredMethods(Identifiers.GET_SPECIFICATION_CONTEXT).get(0);
@@ -70,6 +71,8 @@ public class AstNodeCache {
 
   public final MethodNode SpockRuntime_DespreadList =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0);
+  public final MethodNode SpockRuntime_CallEnterBlock =
+      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_ENTER_BLOCK).get(0);
 
   public final MethodNode ValueRecorder_Reset =
       ValueRecorder.getDeclaredMethods(org.spockframework.runtime.ValueRecorder.RESET).get(0);
diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 396206b854..789acfd95d 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -26,8 +26,10 @@
 import org.spockframework.util.Nullable;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.codehaus.groovy.ast.expr.MethodCallExpression.NO_ARGUMENTS;
+import static org.spockframework.compiler.AstUtil.createDirectMethodCall;
 
 /**
  * Walks the statement and expression tree to:
@@ -54,6 +56,34 @@ public DeepBlockRewriter(IRewriteResources resources) {
   @Override
   public void visit(Block block) {
     super.visit(block);
+    addBlockEnterCall(block);
+  }
+
+  private void addBlockEnterCall(Block block) {
+    BlockParseInfo blockType = block.getParseInfo();
+    if (blockType == BlockParseInfo.WHERE
+      || blockType == BlockParseInfo.METHOD_END
+      || blockType == BlockParseInfo.ANONYMOUS) return;
+
+    MethodCallExpression enterBlockCall = createDirectMethodCall(
+      new ClassExpression(resources.getAstNodeCache().SpockRuntime),
+      resources.getAstNodeCache().SpockRuntime_CallEnterBlock,
+      new ArgumentListExpression(
+        createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
+          resources.getAstNodeCache().SpecInternals_GetSpecificationContext,
+          ArgumentListExpression.EMPTY_ARGUMENTS),
+        new ConstructorCallExpression(resources.getAstNodeCache().BlockInfo,
+          new ArgumentListExpression(
+            new PropertyExpression(
+              new ClassExpression(resources.getAstNodeCache().BlockKind),
+              blockType.name()
+            ),
+            new ListExpression(
+              block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
+            )
+          ))
+      ));
+    block.getAst().add(0, new ExpressionStatement(enterBlockCall));
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
index 28475c3c21..f4070bc1df 100644
--- a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
@@ -15,6 +15,7 @@
 package org.spockframework.lang;
 
 import org.spockframework.mock.IThreadAwareMockController;
+import org.spockframework.runtime.model.BlockInfo;
 import org.spockframework.runtime.model.FeatureInfo;
 import org.spockframework.runtime.model.SpecInfo;
 import org.spockframework.util.Beta;
@@ -24,14 +25,19 @@
 
 @Beta
 public interface ISpecificationContext {
+  @Nullable
   SpecInfo getCurrentSpec();
+  @Nullable
   FeatureInfo getCurrentFeature();
+  @Nullable
   IterationInfo getCurrentIteration();
+  @Nullable
+  BlockInfo getCurrentBlock();
 
   @Nullable
   Throwable getThrownException();
 
   IMockController getMockController();
-  
+
   IThreadAwareMockController getThreadAwareMockController();
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
index d54fb0f541..d884f55832 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
@@ -12,6 +12,8 @@ public class SpecificationContext implements ISpecificationContext {
   private volatile FeatureInfo currentFeature;
   private volatile IterationInfo currentIteration;
 
+  private volatile BlockInfo currentBlock;
+
   private volatile Specification sharedInstance;
 
   private volatile Throwable thrownException;
@@ -56,6 +58,15 @@ public IterationInfo getCurrentIteration() {
     return currentIteration;
   }
 
+  void setCurrentBlock(BlockInfo blockInfo) {
+    this.currentBlock = blockInfo;
+  }
+
+  @Override
+  public BlockInfo getCurrentBlock() {
+    return currentBlock;
+  }
+
   public void setCurrentIteration(IterationInfo currentIteration) {
     this.currentIteration = currentIteration;
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index 31a58fb195..8195e221a8 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -24,7 +24,10 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.collection.IsIterableContainingInAnyOrder;
 import org.opentest4j.MultipleFailuresError;
+import org.spockframework.runtime.extension.IBlockListener;
+import org.spockframework.runtime.model.BlockInfo;
 import org.spockframework.runtime.model.ExpressionInfo;
+import org.spockframework.runtime.model.IterationInfo;
 import org.spockframework.runtime.model.TextPosition;
 import org.spockframework.util.CollectionUtil;
 import org.spockframework.util.ExceptionUtil;
@@ -225,6 +228,17 @@ public static Object[] despreadList(Object[] args, Object[] spreads, int[] posit
     return GroovyRuntimeUtil.despreadList(args, spreads, positions);
   }
 
+  public static final String CALL_ENTER_BLOCK = "callEnterBlock";
+  public static void callEnterBlock(SpecificationContext context, BlockInfo blockInfo) {
+    IterationInfo currentIteration = context.getCurrentIteration();
+    context.setCurrentBlock(blockInfo);
+    List<IBlockListener> blockListeners = currentIteration.getFeature().getBlockListeners();
+    if (blockListeners.isEmpty()) return;
+    for (IBlockListener blockListener : blockListeners) {
+      blockListener.blockEntered(currentIteration, blockInfo);
+    }
+  }
+
   private static List<Object> getValues(ValueRecorder recorder) {
       return recorder == null ? null : recorder.getValues();
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
new file mode 100644
index 0000000000..ba9c4cfa17
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
@@ -0,0 +1,8 @@
+package org.spockframework.runtime.extension;
+
+import org.spockframework.runtime.model.BlockInfo;
+import org.spockframework.runtime.model.IterationInfo;
+
+public interface IBlockListener {
+  void blockEntered(IterationInfo iterationInfo, BlockInfo blockInfo);
+}
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
index 6ef57529ec..0a19bf6564 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
@@ -27,6 +27,14 @@ public class BlockInfo {
   private BlockKind kind;
   private List<String> texts;
 
+  public BlockInfo() {
+  }
+
+  public BlockInfo(BlockKind kind, List<String> texts) {
+    this.kind = kind;
+    this.texts = texts;
+  }
+
   public BlockKind getKind() {
     return kind;
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
index 81cfab9b74..00c5e456a9 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
@@ -15,6 +15,7 @@
 
 package org.spockframework.runtime.model;
 
+import org.spockframework.runtime.extension.IBlockListener;
 import org.spockframework.runtime.extension.IDataDriver;
 import org.spockframework.runtime.extension.IMethodInterceptor;
 import org.spockframework.runtime.model.parallel.ExclusiveResource;
@@ -42,6 +43,8 @@ public class FeatureInfo extends SpecElementInfo<SpecInfo, AnnotatedElement> imp
   private final List<IMethodInterceptor> initializerInterceptors = new ArrayList<>();
   private final Map<MethodInfo, List<IMethodInterceptor>> scopedMethodInterceptors = new HashMap<>();
 
+  private final List<IBlockListener> blockListeners = new ArrayList<>();
+
   private final Set<ExclusiveResource> exclusiveResources = new HashSet<>();
 
   private final Set<TestTag> testTags = new HashSet<>();
@@ -221,6 +224,14 @@ public void addIterationInterceptor(IMethodInterceptor interceptor) {
     iterationInterceptors.add(interceptor);
   }
 
+  public List<IBlockListener> getBlockListeners() {
+    return blockListeners;
+  }
+
+  public void addBlockListener(IBlockListener blockListener) {
+    blockListeners.add(blockListener);
+  }
+
   public MethodInfo getFeatureMethod() {
     return featureMethod;
   }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
new file mode 100644
index 0000000000..e0260e27f4
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
@@ -0,0 +1,59 @@
+package org.spockframework.runtime
+
+import org.spockframework.runtime.model.BlockInfo
+import org.spockframework.runtime.model.BlockKind
+import org.spockframework.runtime.model.IterationInfo
+import spock.lang.Specification
+
+class BlockListenerSpec extends Specification {
+
+  List<BlockInfo> blocks = []
+
+  def setup() {
+    specificationContext.currentIteration.feature.addBlockListener { IterationInfo i, BlockInfo b ->
+      blocks << b
+    }
+  }
+
+  def "BlockListener is called for each Block with text"() {
+    given: "setup"
+    expect: "precondition"
+    when: "action"
+    then: "assertion"
+
+    cleanup: "cleanup"
+    assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP]
+    assert blocks.texts == [["setup"], ["precondition"], ["action"], ["assertion"], ["cleanup"]]
+  }
+
+  def "SpecificationContext holds a reference to the current block"() {
+    assert specificationContext.currentBlock == null
+    given: "setup"
+    assert specificationContext.currentBlock.kind == BlockKind.SETUP
+    expect: "precondition"
+    specificationContext.currentBlock.kind == BlockKind.EXPECT
+    when: "action"
+    assert specificationContext.currentBlock.kind == BlockKind.WHEN
+    then: "assertion"
+    specificationContext.currentBlock.kind == BlockKind.THEN
+
+    cleanup: "cleanup"
+    assert specificationContext.currentBlock.kind == BlockKind.CLEANUP
+  }
+
+  def "blocks extended with and: are treated as singular block with multiple texts"() {
+    given: "setup"
+    and: "setup2"
+    expect: "precondition"
+    and: "precondition2"
+    when: "action"
+    and: "action2"
+    then: "assertion"
+    and: "assertion2"
+
+    cleanup: "cleanup"
+    assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP]
+    and: "cleanup2"
+    assert blocks.texts == [["setup", "setup2"], ["precondition", "precondition2"], ["action", "action2"], ["assertion", "assertion2"], ["cleanup", "cleanup2"]]
+  }
+}

From de2bff0ee781e57ea1d0d23380f904f860815e39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 22 Dec 2022 18:07:01 +0100
Subject: [PATCH 02/26] Expose IErrorContext in ErrorInfo...

Prior to this commit, IRunListener.error(ErrorInfo) didn't give any
context where the error happened.
---
 .../org/spockframework/compiler/AstUtil.java  |  13 ++
 .../compiler/DeepBlockRewriter.java           |  19 ++-
 .../spockframework/compiler/SpecRewriter.java |  22 ++-
 .../lang/ISpecificationContext.java           |   2 +
 .../runtime/DataIteratorFactory.java          |  32 ++--
 .../spockframework/runtime/ErrorContext.java  |  50 +++++++
 .../runtime/MasterRunSupervisor.java          |   4 +-
 .../runtime/PlatformSpecRunner.java           |   6 +-
 .../runtime/SpecificationContext.java         |   7 +-
 .../runtime/model/ErrorInfo.java              |  15 ++
 .../runtime/model/IErrorContext.java          |  14 ++
 .../runtime/RunListenerSpec.groovy            | 137 +++++++++++++++++-
 12 files changed, 282 insertions(+), 39 deletions(-)
 create mode 100644 spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
 create mode 100644 spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
index 61c1287c40..146f903eea 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
@@ -17,6 +17,7 @@
 package org.spockframework.compiler;
 
 import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
 import org.spockframework.lang.Wildcard;
 import org.spockframework.util.*;
 import spock.lang.Specification;
@@ -390,4 +391,16 @@ public static ConstantExpression primitiveConstExpression(int value) {
   public static ConstantExpression primitiveConstExpression(boolean value) {
     return new ConstantExpression(value, true);
   }
+  public static BinaryExpression createVariableNotNullExpression(VariableExpression var) {
+    return new BinaryExpression(
+      new VariableExpression(var),
+      Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1),
+      new ConstantExpression(null));
+  }
+  public static BinaryExpression createVariableIsNullExpression(VariableExpression var) {
+    return new BinaryExpression(
+      new VariableExpression(var),
+      Token.newSymbol(Types.COMPARE_EQUAL, -1, -1),
+      new ConstantExpression(null));
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 789acfd95d..02e5e0abe3 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -17,9 +17,7 @@
 
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.AssertStatement;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.*;
 import org.codehaus.groovy.syntax.Types;
 import org.spockframework.compiler.model.*;
 import org.spockframework.util.Identifiers;
@@ -65,6 +63,7 @@ private void addBlockEnterCall(Block block) {
       || blockType == BlockParseInfo.METHOD_END
       || blockType == BlockParseInfo.ANONYMOUS) return;
 
+    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
     MethodCallExpression enterBlockCall = createDirectMethodCall(
       new ClassExpression(resources.getAstNodeCache().SpockRuntime),
       resources.getAstNodeCache().SpockRuntime_CallEnterBlock,
@@ -83,7 +82,19 @@ private void addBlockEnterCall(Block block) {
             )
           ))
       ));
-    block.getAst().add(0, new ExpressionStatement(enterBlockCall));
+
+    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
+    // so we only call enterBlock if there is no error yet.
+    if (blockType == BlockParseInfo.CLEANUP) {
+      block.getAst().add(0, new IfStatement(
+        // if ($spock_feature_throwable == null)
+        new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, resources.getAstNodeCache().Throwable))),
+        new ExpressionStatement(enterBlockCall),
+        EmptyStatement.INSTANCE
+      ));
+    } else {
+      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
+    }
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 3b02908707..3df86de3d3 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -44,6 +44,9 @@ public class SpecRewriter extends AbstractSpecVisitor implements IRewriteResourc
   // https://issues.apache.org/jira/browse/GROOVY-10403
   // needed for groovy-4 compatibility and only available since groovy-4
   private static final java.lang.reflect.Method GET_PLAIN_NODE_REFERENCE = ReflectionUtil.getMethodBySignature(ClassNode.class, "getPlainNodeReference", boolean.class);
+  public static final String SPOCK_VALUE = "$spock_value";
+  public static final String SPOCK_FEATURE_THROWABLE = "$spock_feature_throwable";
+  public static final String SPOCK_TMP_THROWABLE = "$spock_tmp_throwable";
 
   private final AstNodeCache nodeCache;
   private final SourceLookup lookup;
@@ -159,7 +162,7 @@ private void createFinalFieldGetter(Field field) {
 
   private void createSharedFieldSetter(Field field) {
     String setterName = "set" + MetaClassHelper.capitalize(field.getName());
-    Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), "$spock_value") };
+    Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), SPOCK_VALUE) };
     MethodNode setter = spec.getAst().getMethod(setterName, params);
     if (setter != null) {
       errorReporter.error(field.getAst(),
@@ -180,7 +183,7 @@ private void createSharedFieldSetter(Field field) {
                     // use internal name
                     new ConstantExpression(field.getAst().getName())),
                 Token.newSymbol(Types.ASSIGN, -1, -1),
-                new VariableExpression("$spock_value"))));
+                new VariableExpression(SPOCK_VALUE))));
 
     setter.setSourcePosition(field.getAst());
     spec.getAst().addMethod(setter);
@@ -489,7 +492,7 @@ public void visitCleanupBlock(CleanupBlock block) {
     }
 
     VariableExpression featureThrowableVar =
-        new VariableExpression("$spock_feature_throwable", nodeCache.Throwable);
+        new VariableExpression(SPOCK_FEATURE_THROWABLE, nodeCache.Throwable);
     method.getStatements().add(createVariableDeclarationStatement(featureThrowableVar));
 
     List<Statement> featureStats = new ArrayList<>();
@@ -517,13 +520,6 @@ public void visitCleanupBlock(CleanupBlock block) {
     movedStatsBackToMethod = true;
   }
 
-  private BinaryExpression createVariableNotNullExpression(VariableExpression var) {
-    return new BinaryExpression(
-        new VariableExpression(var),
-        Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1),
-        new ConstantExpression(null));
-  }
-
   private Statement createVariableDeclarationStatement(VariableExpression var) {
     DeclarationExpression throwableDecl =
         new DeclarationExpression(
@@ -548,7 +544,7 @@ private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpr
   }
 
   private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(VariableExpression assignmentVar) {
-    Parameter catchParameter = new Parameter(nodeCache.Throwable, "$spock_tmp_throwable");
+    Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
 
     BinaryExpression assignThrowableExpr =
         new BinaryExpression(
@@ -565,9 +561,9 @@ private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(Variabl
   }
 
   private CatchStatement createHandleSuppressedThrowableStatement(VariableExpression featureThrowableVar) {
-    Parameter catchParameter = new Parameter(nodeCache.Throwable, "$spock_tmp_throwable");
+    Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
 
-    BinaryExpression featureThrowableNotNullExpr = createVariableNotNullExpression(featureThrowableVar);
+    BinaryExpression featureThrowableNotNullExpr = AstUtil.createVariableNotNullExpression(featureThrowableVar);
 
     List<Statement> addSuppressedStats =
       singletonList(new ExpressionStatement(
diff --git a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
index f4070bc1df..7aaf6bf298 100644
--- a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
@@ -40,4 +40,6 @@ public interface ISpecificationContext {
   IMockController getMockController();
 
   IThreadAwareMockController getThreadAwareMockController();
+
+  boolean isSharedContext();
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
index 68b7dcb764..4312d8f297 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
@@ -44,11 +44,15 @@ protected Object invokeRaw(Object target, MethodInfo method, Object... arguments
       try {
         return method.invoke(target, arguments);
       } catch (Throwable throwable) {
-        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable));
+        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable, getErrorContext()));
         return null;
       }
     }
 
+    protected IErrorContext getErrorContext() {
+      return ErrorContext.from(context.getCurrentInstance().getSpecificationContext());
+    }
+
     protected int estimateNumIterations(@Nullable Object dataProvider) {
       if (context.getErrorInfoCollector().hasErrors()) {
         return UNKNOWN_ITERATIONS;
@@ -110,12 +114,12 @@ protected boolean haveNext(Iterator<?>[] iterators, List<DataProviderInfo> dataP
           } else if (result != hasNext) {
             DataProviderInfo provider = dataProviderInfos.get(i);
             supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(provider.getDataProviderMethod(),
-              createDifferentNumberOfDataValuesException(provider, hasNext)));
+              createDifferentNumberOfDataValuesException(provider, hasNext), getErrorContext()));
             return false;
           }
 
         } catch (Throwable t) {
-          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfos.get(i).getDataProviderMethod(), t));
+          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfos.get(i).getDataProviderMethod(), t, getErrorContext()));
           return false;
         }
 
@@ -131,12 +135,12 @@ protected Iterator<?> createIterator(Object dataProvider, DataProviderInfo dataP
         Iterator<?> iter = GroovyRuntimeUtil.asIterator(dataProvider);
         if (iter == null) {
           supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(),
-            new SpockExecutionException("Data provider's iterator() method returned null")));
+            new SpockExecutionException("Data provider's iterator() method returned null"), getErrorContext()));
           return null;
         }
         return iter;
       } catch (Throwable t) {
-        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(), t));
+        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataProviderInfo.getDataProviderMethod(), t, getErrorContext()));
         return null;
       }
     }
@@ -245,7 +249,7 @@ public Object[] next() {
       try {
         return (Object[]) invokeRaw(context.getSharedInstance(), context.getCurrentFeature().getDataProcessorMethod(), next);
       } catch (Throwable t) {
-        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProcessorMethod(), t));
+        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProcessorMethod(), t, getErrorContext()));
         return null;
       }
     }
@@ -322,7 +326,7 @@ public Object[] next() {
             System.arraycopy(nextValues, 0, next, i, nextValues.length);
             i += nextValues.length;
           } catch (Throwable t) {
-            supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProviders().get(i).getDataProviderMethod(), t));
+            supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(context.getCurrentFeature().getDataProviders().get(i).getDataProviderMethod(), t, getErrorContext()));
             return null;
           }
         }
@@ -353,7 +357,7 @@ public Object[] next() {
           }
           // filter block does not like these values, try next ones if available
         } catch (Throwable t) {
-          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(filterMethod, t));
+          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(filterMethod, t, getErrorContext()));
           return null;
         }
       }
@@ -396,7 +400,7 @@ private Object[] createDataProviders() {
           break;
         } else if (provider == null) {
           SpockExecutionException error = new SpockExecutionException("Data provider is null!");
-          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, error));
+          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, error, getErrorContext()));
           break;
         }
 
@@ -419,7 +423,7 @@ private IDataIterator[] createDataProviderIterators() {
           dataVariableMultiplications = Arrays.stream(((DataVariableMultiplication[]) invokeRaw(null, dataVariableMultiplicationsMethod))).iterator();
           nextDataVariableMultiplication = dataVariableMultiplications.next();
         } catch (Throwable t) {
-          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataVariableMultiplicationsMethod, t));
+          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(dataVariableMultiplicationsMethod, t, getErrorContext()));
           return null;
         }
       } else {
@@ -578,7 +582,7 @@ public boolean hasNext() {
       try {
         return iterator.hasNext();
       } catch (Throwable t) {
-        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t));
+        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t, getErrorContext()));
         return false;
       }
     }
@@ -592,7 +596,7 @@ public Object[] next() {
       try {
         return new Object[]{iterator.next()};
       } catch (Throwable t) {
-        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t));
+        supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfo.getDataProviderMethod(), t, getErrorContext()));
         return null;
       }
     }
@@ -870,7 +874,7 @@ public Object[] next() {
           try {
             multiplicandIterators[i] = collectedMultiplicandValues.get(i).iterator();
           } catch (Throwable t) {
-            supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(multiplicandProviderInfos.get(i).getDataProviderMethod(), t));
+            supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(multiplicandProviderInfos.get(i).getDataProviderMethod(), t, getErrorContext()));
             return null;
           }
         }
@@ -948,7 +952,7 @@ protected Object[] extractNextValues(Iterator<?>[] iterators, List<DataProviderI
         try {
           result[i] = iterators[i].next();
         } catch (Throwable t) {
-          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfos.get(i).getDataProviderMethod(), t));
+          supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(providerInfos.get(i).getDataProviderMethod(), t, getErrorContext()));
           return null;
         }
       }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
new file mode 100644
index 0000000000..d1d28ea67a
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
@@ -0,0 +1,50 @@
+package org.spockframework.runtime;
+
+import org.spockframework.lang.ISpecificationContext;
+import org.spockframework.runtime.model.*;
+
+class ErrorContext implements IErrorContext {
+  private final SpecInfo spec;
+  private final FeatureInfo feature;
+  private final IterationInfo iteration;
+  private final BlockInfo block;
+
+  private ErrorContext(SpecInfo spec, FeatureInfo feature, IterationInfo iteration, BlockInfo block) {
+    this.spec = spec;
+    this.feature = feature;
+    this.iteration = iteration;
+    this.block = block;
+  }
+
+  static ErrorContext from(ISpecificationContext context) {
+    if (context.isSharedContext()) {
+      return new ErrorContext(context.getCurrentSpec(), null, null, null);
+    }
+    return new ErrorContext(
+      context.getCurrentSpec(),
+      context.getCurrentFeature(),
+      context.getCurrentIteration(),
+      context.getCurrentBlock()
+    );
+  }
+
+  @Override
+  public SpecInfo getCurrentSpec() {
+    return spec;
+  }
+
+  @Override
+  public FeatureInfo getCurrentFeature() {
+    return feature;
+  }
+
+  @Override
+  public IterationInfo getCurrentIteration() {
+    return iteration;
+  }
+
+  @Override
+  public BlockInfo getCurrentBlock() {
+    return block;
+  }
+}
diff --git a/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java b/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java
index 802a2306e1..904f5d5de6 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/MasterRunSupervisor.java
@@ -45,7 +45,7 @@ public void error(ErrorInfoCollector errorInfoCollector, ErrorInfo error) {
 
     exception = transform(exception);
 
-    ErrorInfo transformedError = new ErrorInfo(error.getMethod(), exception);
+    ErrorInfo transformedError = new ErrorInfo(error.getMethod(), exception, error.getErrorContext());
     if (exception instanceof TestAbortedException || exception instanceof TestSkippedException) {
       // Spock has no concept of "aborted tests", so we don't notify Spock listeners
     } else {
@@ -58,7 +58,7 @@ public void error(ErrorInfoCollector errorInfoCollector, ErrorInfo error) {
   private void handleMultipleFailures(ErrorInfoCollector errorInfoCollector,  ErrorInfo error) {
     MultipleFailuresError multiFailure = (MultipleFailuresError) error.getException();
     for (Throwable failure : multiFailure.getFailures())
-      error(errorInfoCollector, new ErrorInfo(error.getMethod(), failure));
+      error(errorInfoCollector, new ErrorInfo(error.getMethod(), failure, error.getErrorContext()));
   }
 
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
index 9257395514..959a3b5eaf 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
@@ -376,7 +376,7 @@ private void runIterationCleanups(SpockExecutionContext context) {
       try {
         cleanup.run();
       } catch (Throwable t) {
-        ErrorInfo error = new ErrorInfo(CollectionUtil.getFirstElement(context.getSpec().getCleanupMethods()), t);
+        ErrorInfo error = new ErrorInfo(CollectionUtil.getFirstElement(context.getSpec().getCleanupMethods()), t, ErrorContext.from(getSpecificationContext(context)));
         supervisor.error(context.getErrorInfoCollector(), error);
       }
     }
@@ -429,7 +429,7 @@ protected void invoke(SpockExecutionContext context, Object target, MethodInfo m
     try {
       invocation.proceed();
     } catch (Throwable throwable) {
-      ErrorInfo error = new ErrorInfo(method, throwable);
+      ErrorInfo error = new ErrorInfo(method, throwable, ErrorContext.from(getSpecificationContext(context)));
       supervisor.error(context.getErrorInfoCollector(), error);
     }
   }
@@ -438,7 +438,7 @@ protected Object invokeRaw(SpockExecutionContext context, Object target, MethodI
     try {
       return method.invoke(target, arguments);
     } catch (Throwable throwable) {
-      supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable));
+      supervisor.error(context.getErrorInfoCollector(), new ErrorInfo(method, throwable, ErrorContext.from(getSpecificationContext(context))));
       return null;
     }
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
index d884f55832..79d1222745 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
@@ -52,7 +52,7 @@ public void setCurrentFeature(FeatureInfo currentFeature) {
 
   @Override
   public IterationInfo getCurrentIteration() {
-    if (currentIteration == null) {
+    if (isSharedContext()) {
       throw new IllegalStateException("Cannot request current iteration in @Shared context, or feature context");
     }
     return currentIteration;
@@ -91,4 +91,9 @@ public IMockController getMockController() {
   public IThreadAwareMockController getThreadAwareMockController() {
     return mockController;
   }
+
+  @Override
+  public boolean isSharedContext() {
+    return currentFeature == null || currentIteration == null;
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
index a730779216..f5d24572f3 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
@@ -14,13 +14,23 @@
 
 package org.spockframework.runtime.model;
 
+import org.spockframework.util.Nullable;
+
 public class ErrorInfo {
   private final MethodInfo method;
   private final Throwable error;
+  private final IErrorContext errorContext;
 
   public ErrorInfo(MethodInfo method, Throwable error) {
     this.method = method;
     this.error = error;
+    this.errorContext = null;
+  }
+
+  public ErrorInfo(MethodInfo method, Throwable error, IErrorContext errorContext) {
+    this.method = method;
+    this.error = error;
+    this.errorContext = errorContext;
   }
 
   public MethodInfo getMethod() {
@@ -30,4 +40,9 @@ public MethodInfo getMethod() {
   public Throwable getException() {
     return error;
   }
+
+  @Nullable
+  public IErrorContext getErrorContext() {
+    return errorContext;
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
new file mode 100644
index 0000000000..1a5736e9ab
--- /dev/null
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -0,0 +1,14 @@
+package org.spockframework.runtime.model;
+
+import org.spockframework.util.Nullable;
+
+public interface IErrorContext {
+  @Nullable
+  SpecInfo getCurrentSpec();
+  @Nullable
+  FeatureInfo getCurrentFeature();
+  @Nullable
+  IterationInfo getCurrentIteration();
+  @Nullable
+  BlockInfo getCurrentBlock();
+}
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index 2f8255ed25..082713b3ea 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -1,15 +1,22 @@
 package org.spockframework.runtime
 
 import org.spockframework.EmbeddedSpecification
-import org.spockframework.runtime.extension.*
+import org.spockframework.runtime.extension.ExtensionAnnotation
+import org.spockframework.runtime.extension.IAnnotationDrivenExtension
+import org.spockframework.runtime.model.BlockKind
+import org.spockframework.runtime.model.ErrorInfo
 import org.spockframework.runtime.model.SpecInfo
 import org.spockframework.runtime.model.parallel.ExecutionMode
 import spock.lang.Execution
 import spock.lang.Ignore
 import spock.lang.Issue
 import spock.lang.Specification
+import spock.lang.Unroll
 
-import java.lang.annotation.*
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target
 
 @Execution(
   value = ExecutionMode.SAME_THREAD,
@@ -120,6 +127,132 @@ class ASpec extends Specification {
     cleanup:
     RunListenerDelegate.delegate = null
   }
+
+  def "IRunListener gets called for errors"() {
+    given:
+    RunListenerDelegate.delegate = runListener
+    runner.addPackageImport(Specification.package)
+    runner.addClassImport(RegisterRunListener)
+    runner.throwFailure = false
+
+    when:
+    runner.runWithImports '''
+@RegisterRunListener
+class ASpec extends Specification {
+  def "a test"() {
+      expect: "failing expect"
+      false
+
+      cleanup: "failing cleanup"
+      throw new RuntimeException("failing cleanup")
+  }
+}
+'''
+
+    then:
+    1 * runListener.beforeSpec(_)
+    then:
+    1 * runListener.beforeFeature(_)
+    then:
+    1 * runListener.beforeIteration(_)
+    then:
+    1 * runListener.error(_) >> { ErrorInfo errorInfo ->
+      with(errorInfo.errorContext.currentBlock) {
+        it.kind == BlockKind.EXPECT
+        it.texts == ["failing expect"]
+      }
+      assert errorInfo.exception instanceof AssertionError
+      assert errorInfo.exception.suppressed[0].message == "failing cleanup"
+    }
+    then:
+    1 * runListener.afterIteration(_)
+    then:
+    1 * runListener.afterFeature(_)
+    then:
+    1 * runListener.afterSpec(_)
+    then:
+    0 * runListener._
+
+    cleanup:
+    RunListenerDelegate.delegate = null
+  }
+
+  @Unroll("IRunListener.error gets called for #errorLocation")
+  def "IRunListener gets called for different error locations"() {
+    given:
+    RunListenerDelegate.delegate = runListener
+    runner.addPackageImport(Specification.package)
+    runner.addClassImport(RegisterRunListener)
+    runner.throwFailure = false
+
+    when:
+    runner.runWithImports """
+@RegisterRunListener
+class ASpec extends Specification {
+  def setupSpec() {
+    assert "$errorLocation" != "setupSpec"
+  }
+  def setup() {
+    assert "$errorLocation" != "setup"
+  }
+
+  def "a test"() {
+    assert "$errorLocation" != "feature start"
+
+    given: "setup label"
+    assert "$errorLocation" != "feature setup"
+
+    expect: "expect label"
+    "$errorLocation" != "feature expect"
+
+    when: "when label"
+    assert "$errorLocation" != "feature when"
+
+    then: "then label"
+    "$errorLocation" != "feature then"
+
+    cleanup: "cleanup label"
+    assert "$errorLocation" != "feature cleanup"
+  }
+
+  def cleanup() {
+    assert "$errorLocation" != "cleanup"
+  }
+
+  def cleanupSpec() {
+    assert "$errorLocation" != "cleanupSpec"
+  }
+}
+"""
+
+    then:
+    1 * runListener.error(_) >> { ErrorInfo errorInfo ->
+      if (block != null) {
+        with(errorInfo.errorContext.currentBlock) {
+          it.kind == block
+          it.texts == blockTexts
+        }
+      } else {
+        assert errorInfo.errorContext.currentBlock == null
+      }
+    }
+
+    cleanup:
+    RunListenerDelegate.delegate = null
+
+    where:
+    errorLocation     | block             | blockTexts
+    "setupSpec"       | null              | []
+    "setup"           | null              | []
+    "feature start"   | null              | []
+    "feature setup"   | BlockKind.SETUP   | ["setup label"]
+    "feature expect"  | BlockKind.EXPECT  | ["expect label"]
+    "feature when"    | BlockKind.WHEN    | ["when label"]
+    "feature then"    | BlockKind.THEN    | ["then label"]
+    "feature cleanup" | BlockKind.CLEANUP | ["cleanup label"]
+    "cleanup"         | null              | [] // TODO the last block leaks into cleanup, this is a bug
+    "cleanupSpec"     | null              | []
+  }
 }
 
 @Target(ElementType.TYPE)

From d77ab6c4854dab7d2e0f1600755025f96e3121ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 16 Feb 2023 18:35:34 +0100
Subject: [PATCH 03/26] Clear current block on exit from feature method

---
 .../spockframework/compiler/AstNodeCache.java |  2 ++
 .../spockframework/compiler/SpecRewriter.java | 19 +++++++++++++++++++
 .../spockframework/runtime/SpockRuntime.java  |  5 +++++
 .../runtime/RunListenerSpec.groovy            |  2 +-
 4 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index e905ea2dea..09e80585bb 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -73,6 +73,8 @@ public class AstNodeCache {
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0);
   public final MethodNode SpockRuntime_CallEnterBlock =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_ENTER_BLOCK).get(0);
+  public final MethodNode SpockRuntime_ClearCurrentBlock =
+      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CLEAR_CURRENT_BLOCK).get(0);
 
   public final MethodNode ValueRecorder_Reset =
       ValueRecorder.getDeclaredMethods(org.spockframework.runtime.ValueRecorder.RESET).get(0);
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 3df86de3d3..e9441d5deb 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -407,6 +407,9 @@ public void visitMethodAgain(Method method) {
     if (methodHasDeepNonGroupedCondition) {
       defineErrorRethrower(method.getStatements());
     }
+
+    if (method instanceof FeatureMethod)
+      clearCurrentBlockOnExit(method.getStatements());
   }
 
   @Override
@@ -543,6 +546,22 @@ private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpr
     return tryCatchStat;
   }
 
+  // Wraps the feature method in a try-finally to clear the current block on exit.
+  private void clearCurrentBlockOnExit(List<Statement> statements) {
+    MethodCallExpression setCurrentBlockToNull = createDirectMethodCall(new ClassExpression(nodeCache.SpockRuntime),
+      nodeCache.SpockRuntime_ClearCurrentBlock, new ArgumentListExpression(getSpecificationContext()));
+
+    List<Statement> innerStatements = new ArrayList<>(statements);
+
+    TryCatchStatement tryCatchStat =
+        new TryCatchStatement(
+            new BlockStatement(innerStatements, null),
+            new ExpressionStatement(setCurrentBlockToNull));
+
+    statements.clear();
+    statements.add(tryCatchStat);
+  }
+
   private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(VariableExpression assignmentVar) {
     Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index 8195e221a8..24fb95d367 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -239,6 +239,11 @@ public static void callEnterBlock(SpecificationContext context, BlockInfo blockI
     }
   }
 
+  public static final String CLEAR_CURRENT_BLOCK = "clearCurrentBlock";
+  public static void clearCurrentBlock(SpecificationContext context) {
+    context.setCurrentBlock(null);
+  }
+
   private static List<Object> getValues(ValueRecorder recorder) {
       return recorder == null ? null : recorder.getValues();
   }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index 082713b3ea..95037493c3 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -250,7 +250,7 @@ class ASpec extends Specification {
     "feature when"    | BlockKind.WHEN    | ["when label"]
     "feature then"    | BlockKind.THEN    | ["then label"]
     "feature cleanup" | BlockKind.CLEANUP | ["cleanup label"]
-    "cleanup"         | null              | [] // TODO the last block leaks into cleanup, this is a bug
+    "cleanup"         | null              | []
     "cleanupSpec"     | null              | []
   }
 }

From a231f0419bbf229382855bbe28232144df7030c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 16 Feb 2023 19:16:41 +0100
Subject: [PATCH 04/26] Update AST tests with block listeners

---
 .../parameterization/DataProviders.groovy     |  12 +-
 ...used_in_AST_transformation_Groovy____3.txt | 153 +++++++++++---
 ...used_in_AST_transformation_Groovy____4.txt | 200 +++++++++++++++---
 ...ceFeatureBody_can_render_everything.groovy |   9 +-
 ...n_render_everything__Groovy_4_0_2__.groovy |   9 +-
 ...thods_and_its_annotation_by_default.groovy |   9 +-
 ...ers_and_their_annotation_by_default.groovy |   9 +-
 ...rite_keeps_correct_method_reference.groovy |  59 +++---
 ...hod_reference_for_multi_assignments.groovy |  59 +++---
 .../DataAstSpec/multi_parameterization.groovy |  19 +-
 .../nested_multi_parameterization.groovy      |  19 +-
 ...ith__separators_can_be_combined-[0].groovy |  19 +-
 ...ith__separators_can_be_combined-[1].groovy |  19 +-
 ...ith__separators_can_be_combined-[2].groovy |  19 +-
 ...filter_block_becomes_its_own_method.groovy |  21 +-
 ...tionsAsSet_is_transformed_correctly.groovy |  22 +-
 ...InAnyOrder_is_transformed_correctly.groovy |  22 +-
 ...onditions_are_transformed_correctly.groovy |  22 +-
 ...onditions_are_transformed_correctly.groovy |  22 +-
 ...rite_keeps_correct_method_reference.groovy |  24 ++-
 ...hod_reference_for_multi_assignments.groovy |  24 ++-
 21 files changed, 570 insertions(+), 201 deletions(-)

diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
index 9563c41188..73d33b0357 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
@@ -114,7 +114,11 @@ where: x << []
     then:
     result.source == '''\
 public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    this.getSpecificationContext().getMockController().leaveScope()
+    try {
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
@@ -163,7 +167,11 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
     then:
     result.source == '''\
 public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    this.getSpecificationContext().getMockController().leaveScope()
+    try {
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index 36f30e3f84..2edaabc0ad 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -12,10 +12,12 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     TRYCATCHBLOCK L4 L5 L5 java/lang/Throwable
     TRYCATCHBLOCK L4 L5 L6 null
     TRYCATCHBLOCK L5 L7 L6 null
-   L8
+    TRYCATCHBLOCK L8 L9 L10 null
+   L11
     NOP
     INVOKESTATIC apackage/TestSpec.$getCallSiteArray ()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
     ASTORE 1
+   L8
     ALOAD 1
     LDC 0
     AALOAD
@@ -25,7 +27,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/ErrorCollector
     ASTORE 2
-   L9
+   L12
     ALOAD 2
     POP
     ALOAD 1
@@ -37,9 +39,33 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/ValueRecorder
     ASTORE 3
-   L10
+   L13
     ALOAD 3
     POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 2
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 3
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
    L0
     LINENUMBER 4 L0
     ALOAD 2
@@ -59,11 +85,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.verifyCondition (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Object;)V
     ACONST_NULL
     POP
-    GOTO L11
+    GOTO L14
    L1
    FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite; org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder] [java/lang/Throwable]
     ASTORE 4
-   L12
+   L15
     ALOAD 2
     ALOAD 3
     LDC "true"
@@ -76,16 +102,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L3
-    GOTO L11
-   L11
+    GOTO L14
+   L14
    FRAME SAME
-    GOTO L13
+    GOTO L16
    L2
    FRAME SAME1 java/lang/Throwable
     ASTORE 5
     ALOAD 5
     ATHROW
-   L13
+   L16
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -100,14 +126,39 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     ACONST_NULL
     POP
    L4
-    LINENUMBER 6 L4
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 4
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 5
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+   L17
+    LINENUMBER 6 L17
     ICONST_1
     POP
-    GOTO L14
+    GOTO L18
    L5
    FRAME SAME1 java/lang/Throwable
     ASTORE 6
-   L15
+   L19
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -119,20 +170,45 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L7
-    GOTO L14
-   L14
+    GOTO L18
+   L18
    FRAME SAME
-    GOTO L16
+    GOTO L20
    L6
    FRAME SAME1 java/lang/Throwable
     ASTORE 7
     ALOAD 7
     ATHROW
-   L16
-    LINENUMBER 8 L16
+   L20
    FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
     ALOAD 1
-    LDC 2
+    LDC 6
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 7
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+   L21
+    LINENUMBER 8 L21
+    ALOAD 1
+    LDC 8
     AALOAD
     ALOAD 0
     ACONST_NULL
@@ -152,13 +228,40 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L17
+   L22
+    GOTO L9
+   L9
+   FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
+    ACONST_NULL
+    POP
+    GOTO L23
+   L10
+   FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite;] [java/lang/Throwable]
+    ASTORE 8
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
+    ACONST_NULL
+    POP
+    ALOAD 8
+    ATHROW
+   L23
+   FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L8 L17 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L17 2
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L17 3
-    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 4
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 6
+    LOCALVARIABLE this Lapackage/TestSpec; L11 L23 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L22 2
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L22 3
+    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L15 L3 4
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L19 L7 6
     MAXSTACK = 9
-    MAXLOCALS = 8
+    MAXLOCALS = 9
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index 14d0a2cdcd..d3e2ac9a96 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -12,6 +12,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     TRYCATCHBLOCK L4 L5 L5 java/lang/Throwable
     TRYCATCHBLOCK L4 L5 L6 null
     TRYCATCHBLOCK L5 L7 L6 null
+    TRYCATCHBLOCK L8 L9 L10 null
    L8
     LDC Lorg/spockframework/runtime/ErrorRethrower;.class
     INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
@@ -29,7 +30,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ASTORE 1
-   L9
+   L11
     ALOAD 1
     POP
     LDC Lorg/spockframework/runtime/ValueRecorder;.class
@@ -48,9 +49,47 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ASTORE 2
-   L10
+   L12
     ALOAD 2
     POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "EXPECT", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
    L0
     LINENUMBER 4 L0
     ALOAD 1
@@ -70,11 +109,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.verifyCondition (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Object;)V
     ACONST_NULL
     POP
-    GOTO L11
+    GOTO L13
    L1
    FRAME FULL [apackage/TestSpec org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder] [java/lang/Throwable]
     ASTORE 3
-   L12
+   L14
     ALOAD 1
     ALOAD 2
     LDC "true"
@@ -87,16 +126,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L3
-    GOTO L11
-   L11
-   FRAME SAME
     GOTO L13
+   L13
+   FRAME SAME
+    GOTO L15
    L2
    FRAME SAME1 java/lang/Throwable
     ASTORE 4
     ALOAD 4
     ATHROW
-   L13
+   L15
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -112,14 +151,53 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     ACONST_NULL
     POP
    L4
-    LINENUMBER 6 L4
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "WHEN", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+   L16
+    LINENUMBER 6 L16
     ICONST_1
     POP
-    GOTO L14
+    GOTO L17
    L5
    FRAME SAME1 java/lang/Throwable
     ASTORE 5
-   L15
+   L18
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -135,18 +213,57 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L7
-    GOTO L14
-   L14
+    GOTO L17
+   L17
    FRAME SAME
-    GOTO L16
+    GOTO L19
    L6
    FRAME SAME1 java/lang/Throwable
     ASTORE 6
     ALOAD 6
     ATHROW
-   L16
-    LINENUMBER 8 L16
+   L19
    FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "THEN", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+   L20
+    LINENUMBER 8 L20
     ALOAD 0
     ACONST_NULL
     ACONST_NULL
@@ -179,14 +296,49 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L17
-    LINENUMBER 9 L17
+   L21
+    GOTO L9
+   L9
+   FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
+    ACONST_NULL
+    POP
+    GOTO L22
+   L10
+   FRAME FULL [apackage/TestSpec] [java/lang/Throwable]
+    ASTORE 7
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
+    ACONST_NULL
+    POP
+    ALOAD 7
+    ATHROW
+   L22
+    LINENUMBER 9 L22
+   FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L8 L17 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L17 1
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L17 2
-    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 3
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 5
+    LOCALVARIABLE this Lapackage/TestSpec; L8 L22 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L11 L21 1
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L12 L21 2
+    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L14 L3 3
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L18 L7 5
     MAXSTACK = 9
-    MAXLOCALS = 7
+    MAXLOCALS = 8
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index 863a604b80..e73aaa657b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -7,8 +7,13 @@ public class apackage.ASpec extends spock.lang.Specification {
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        java.lang.Object nothing = null
-        this.getSpecificationContext().getMockController().leaveScope()
+        try {
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+            java.lang.Object nothing = null
+            this.getSpecificationContext().getMockController().leaveScope()
+        }
+        finally {
+            org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
     }
 
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index 57dc76b278..09b276800e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -7,8 +7,13 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        java.lang.Object nothing = null
-        this.getSpecificationContext().getMockController().leaveScope()
+        try {
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+            java.lang.Object nothing = null
+            this.getSpecificationContext().getMockController().leaveScope()
+        }
+        finally {
+            org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
     }
 
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
index 39cb8e739b..4279e87111 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
@@ -6,8 +6,13 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    java.lang.Object nothing = null
-    this.getSpecificationContext().getMockController().leaveScope()
+    try {
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object nothing = null
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
index 4a955d2daa..69eb4ecb40 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
@@ -12,8 +12,13 @@ private java.lang.Object $spock_initializeFields() {
 
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 3, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    java.lang.Object nothing = null
-    this.getSpecificationContext().getMockController().leaveScope()
+    try {
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object nothing = null
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index c65f20fc26..b3f2e48dab 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -8,39 +8,48 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    java.lang.Object foobar
-    java.lang.Throwable $spock_feature_throwable
     try {
-        foobar = this.foobar()
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        java.lang.Object foobar
+        java.lang.Throwable $spock_feature_throwable
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
-        finally {
-        }
-    }
-    catch (java.lang.Throwable $spock_tmp_throwable) {
-        $spock_feature_throwable = $spock_tmp_throwable
-        throw $spock_tmp_throwable
-    }
-    finally {
-        try {
-            foobar.size()
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+            foobar = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+            try {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            }
+            catch (java.lang.Throwable $spock_condition_throwable) {
+                org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
+            finally {
+            }
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
-            if ( $spock_feature_throwable != null) {
-                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
-            } else {
-                throw $spock_tmp_throwable
-            }
+            $spock_feature_throwable = $spock_tmp_throwable
+            throw $spock_tmp_throwable
         }
         finally {
+            try {
+                if ( $spock_feature_throwable == null) {
+                    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+                }
+                foobar.size()
+            }
+            catch (java.lang.Throwable $spock_tmp_throwable) {
+                if ( $spock_feature_throwable != null) {
+                    $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+                } else {
+                    throw $spock_tmp_throwable
+                }
+            }
+            finally {
+            }
         }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    this.getSpecificationContext().getMockController().leaveScope()
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index c1d899d23d..55c8b121d6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -8,39 +8,48 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-    java.lang.Throwable $spock_feature_throwable
     try {
-        (foobar, b) = this.foobar()
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        def (java.lang.Object foobar, java.lang.Object b) = [null, null]
+        java.lang.Throwable $spock_feature_throwable
         try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
-        finally {
-        }
-    }
-    catch (java.lang.Throwable $spock_tmp_throwable) {
-        $spock_feature_throwable = $spock_tmp_throwable
-        throw $spock_tmp_throwable
-    }
-    finally {
-        try {
-            foobar.size()
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+            (foobar, b) = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+            try {
+                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            }
+            catch (java.lang.Throwable $spock_condition_throwable) {
+                org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
+            finally {
+            }
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
-            if ( $spock_feature_throwable != null) {
-                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
-            } else {
-                throw $spock_tmp_throwable
-            }
+            $spock_feature_throwable = $spock_tmp_throwable
+            throw $spock_tmp_throwable
         }
         finally {
+            try {
+                if ( $spock_feature_throwable == null) {
+                    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+                }
+                foobar.size()
+            }
+            catch (java.lang.Throwable $spock_tmp_throwable) {
+                if ( $spock_feature_throwable != null) {
+                    $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+                } else {
+                    throw $spock_tmp_throwable
+                }
+            }
+            finally {
+            }
         }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    this.getSpecificationContext().getMockController().leaveScope()
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
index 20b6d371e4..7ff054bfce 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
@@ -6,16 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = [])], parameterNames = ['a', 'b'])
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 @org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
index 9fe8ae6883..38f6c3dc0c 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
@@ -6,16 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = [])], parameterNames = ['a', 'b'])
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 @org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
index 140341467e..3a79ffdfd5 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
@@ -5,16 +5,21 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
index 140341467e..3a79ffdfd5 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
@@ -5,16 +5,21 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
index 140341467e..3a79ffdfd5 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
@@ -5,16 +5,21 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index a6dda8e53b..22c958f31d 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -5,16 +5,22 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
+        finally {
+        }
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
@@ -38,6 +44,7 @@ public static org.spockframework.runtime.model.DataVariableMultiplication[] $spo
 public static void $spock_feature_0_0filter(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == 1', 15, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 1)))
     }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index 1e090f19a3..60eff18a43 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -6,17 +6,23 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    java.lang.Object x = [1]
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object x = [1]
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index 7973626ae8..b95d366dd6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -6,17 +6,23 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    java.lang.Object x = [1]
     try {
-        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object x = [1]
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
index 867b2cac29..dc09d0b15a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
@@ -6,17 +6,23 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    java.lang.Object x = '[1]'
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object x = '[1]'
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
index 63bc98d5d2..d13911fde1 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
@@ -6,17 +6,23 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    java.lang.Object x = 'a1b'
     try {
-        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
+        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object x = 'a1b'
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
+        finally {
+        }
+        this.getSpecificationContext().getMockController().leaveScope()
     }
-    catch (java.lang.Throwable $spock_condition_throwable) {
-        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
     finally {
-    }
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index c76b5be927..8efeecc816 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -8,18 +8,24 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
-    java.lang.Object foobar
-    this.getSpecificationContext().setThrownException(null)
     try {
-        foobar = this.foobar()
-    }
-    catch (java.lang.Throwable $spock_ex) {
-        this.getSpecificationContext().setThrownException($spock_ex)
+        java.lang.Object foobar
+        this.getSpecificationContext().setThrownException(null)
+        try {
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+            foobar = this.foobar()
+        }
+        catch (java.lang.Throwable $spock_ex) {
+            this.getSpecificationContext().setThrownException($spock_ex)
+        }
+        finally {
+        }
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        this.thrownImpl(null, null, java.lang.IllegalStateException)
+        this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
-    }
-    this.thrownImpl(null, null, java.lang.IllegalStateException)
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 9f3f3590e9..652b30e287 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -8,18 +8,24 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
-    def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-    this.getSpecificationContext().setThrownException(null)
     try {
-        (foobar, b) = this.foobar()
-    }
-    catch (java.lang.Throwable $spock_ex) {
-        this.getSpecificationContext().setThrownException($spock_ex)
+        def (java.lang.Object foobar, java.lang.Object b) = [null, null]
+        this.getSpecificationContext().setThrownException(null)
+        try {
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+            (foobar, b) = this.foobar()
+        }
+        catch (java.lang.Throwable $spock_ex) {
+            this.getSpecificationContext().setThrownException($spock_ex)
+        }
+        finally {
+        }
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        this.thrownImpl(null, null, java.lang.IllegalStateException)
+        this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
-    }
-    this.thrownImpl(null, null, java.lang.IllegalStateException)
-    this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
 }
 /*--------- end::snapshot[] ---------*/
 }

From 3407a1a99cc021676d9e1369dd16732c60cc5b3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 10 Mar 2023 11:34:14 +0100
Subject: [PATCH 05/26] Add blockExited to IBlockListener

---
 .../spockframework/compiler/AstNodeCache.java |   2 +
 .../compiler/DeepBlockRewriter.java           |  50 +++++---
 .../spockframework/runtime/SpockRuntime.java  |  11 +-
 .../runtime/extension/IBlockListener.java     |   3 +-
 .../runtime/BlockListenerSpec.groovy          |  14 ++-
 ...used_in_AST_transformation_Groovy____3.txt |  82 ++++++++++++-
 ...used_in_AST_transformation_Groovy____4.txt | 114 ++++++++++++++++++
 ...ceFeatureBody_can_render_everything.groovy |   1 +
 ...n_render_everything__Groovy_4_0_2__.groovy |   1 +
 ...thods_and_its_annotation_by_default.groovy |   1 +
 ...ers_and_their_annotation_by_default.groovy |   1 +
 ...rite_keeps_correct_method_reference.groovy |   5 +
 ...hod_reference_for_multi_assignments.groovy |   5 +
 .../DataAstSpec/multi_parameterization.groovy |   1 +
 .../nested_multi_parameterization.groovy      |   1 +
 ...ith__separators_can_be_combined-[0].groovy |   1 +
 ...ith__separators_can_be_combined-[1].groovy |   1 +
 ...ith__separators_can_be_combined-[2].groovy |   1 +
 ...filter_block_becomes_its_own_method.groovy |   3 +
 ...tionsAsSet_is_transformed_correctly.groovy |   2 +
 ...InAnyOrder_is_transformed_correctly.groovy |   2 +
 ...onditions_are_transformed_correctly.groovy |   2 +
 ...onditions_are_transformed_correctly.groovy |   2 +
 ...rite_keeps_correct_method_reference.groovy |   2 +
 ...hod_reference_for_multi_assignments.groovy |   2 +
 25 files changed, 281 insertions(+), 29 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index 09e80585bb..361ca3d43b 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -73,6 +73,8 @@ public class AstNodeCache {
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0);
   public final MethodNode SpockRuntime_CallEnterBlock =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_ENTER_BLOCK).get(0);
+  public final MethodNode SpockRuntime_CallExitBlock =
+      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_EXIT_BLOCK).get(0);
   public final MethodNode SpockRuntime_ClearCurrentBlock =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CLEAR_CURRENT_BLOCK).get(0);
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 02e5e0abe3..1812330178 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -15,16 +15,20 @@
 
 package org.spockframework.compiler;
 
+import org.codehaus.groovy.ast.MethodNode;
+import org.jetbrains.annotations.NotNull;
+import org.spockframework.compiler.model.*;
+
+import java.util.stream.Collectors;
+
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.*;
 import org.codehaus.groovy.ast.stmt.*;
 import org.codehaus.groovy.syntax.Types;
-import org.spockframework.compiler.model.*;
 import org.spockframework.util.Identifiers;
 import org.spockframework.util.Nullable;
 
 import java.util.List;
-import java.util.stream.Collectors;
 
 import static org.codehaus.groovy.ast.expr.MethodCallExpression.NO_ARGUMENTS;
 import static org.spockframework.compiler.AstUtil.createDirectMethodCall;
@@ -64,9 +68,34 @@ private void addBlockEnterCall(Block block) {
       || blockType == BlockParseInfo.ANONYMOUS) return;
 
     // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
-    MethodCallExpression enterBlockCall = createDirectMethodCall(
+    MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallEnterBlock);
+    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
+    MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallExitBlock);
+
+    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
+    // so we only call enterBlock if there is no error yet.
+    if (blockType == BlockParseInfo.CLEANUP) {
+      block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
+      block.getAst().add(ifThrowableIsNull(exitBlockCall));
+    } else {
+      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
+      block.getAst().add( new ExpressionStatement(exitBlockCall));
+    }
+  }
+
+  private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
+    return new IfStatement(
+      // if ($spock_feature_throwable == null)
+      new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, resources.getAstNodeCache().Throwable))),
+      new ExpressionStatement(methodCall),
+      EmptyStatement.INSTANCE
+    );
+  }
+
+  private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
+    return createDirectMethodCall(
       new ClassExpression(resources.getAstNodeCache().SpockRuntime),
-      resources.getAstNodeCache().SpockRuntime_CallEnterBlock,
+      blockListenerMethod,
       new ArgumentListExpression(
         createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
           resources.getAstNodeCache().SpecInternals_GetSpecificationContext,
@@ -82,19 +111,6 @@ private void addBlockEnterCall(Block block) {
             )
           ))
       ));
-
-    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
-    // so we only call enterBlock if there is no error yet.
-    if (blockType == BlockParseInfo.CLEANUP) {
-      block.getAst().add(0, new IfStatement(
-        // if ($spock_feature_throwable == null)
-        new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, resources.getAstNodeCache().Throwable))),
-        new ExpressionStatement(enterBlockCall),
-        EmptyStatement.INSTANCE
-      ));
-    } else {
-      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
-    }
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index 24fb95d367..fe82e72c28 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -234,9 +234,14 @@ public static void callEnterBlock(SpecificationContext context, BlockInfo blockI
     context.setCurrentBlock(blockInfo);
     List<IBlockListener> blockListeners = currentIteration.getFeature().getBlockListeners();
     if (blockListeners.isEmpty()) return;
-    for (IBlockListener blockListener : blockListeners) {
-      blockListener.blockEntered(currentIteration, blockInfo);
-    }
+    blockListeners.forEach(blockListener -> blockListener.blockEntered(currentIteration, blockInfo));
+  }
+  public static final String CALL_EXIT_BLOCK = "callExitBlock";
+  public static void callExitBlock(SpecificationContext context, BlockInfo blockInfo) {
+    IterationInfo currentIteration = context.getCurrentIteration();
+    List<IBlockListener> blockListeners = currentIteration.getFeature().getBlockListeners();
+    if (blockListeners.isEmpty()) return;
+    blockListeners.forEach(blockListener -> blockListener.blockExited(currentIteration, blockInfo));
   }
 
   public static final String CLEAR_CURRENT_BLOCK = "clearCurrentBlock";
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
index ba9c4cfa17..3cedac71e5 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
@@ -4,5 +4,6 @@
 import org.spockframework.runtime.model.IterationInfo;
 
 public interface IBlockListener {
-  void blockEntered(IterationInfo iterationInfo, BlockInfo blockInfo);
+  default void blockEntered(IterationInfo iterationInfo, BlockInfo blockInfo) {}
+  default void blockExited(IterationInfo iterationInfo, BlockInfo blockInfo) {}
 }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
index e0260e27f4..393983ddd7 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
@@ -1,5 +1,6 @@
 package org.spockframework.runtime
 
+import org.spockframework.runtime.extension.IBlockListener
 import org.spockframework.runtime.model.BlockInfo
 import org.spockframework.runtime.model.BlockKind
 import org.spockframework.runtime.model.IterationInfo
@@ -8,11 +9,16 @@ import spock.lang.Specification
 class BlockListenerSpec extends Specification {
 
   List<BlockInfo> blocks = []
+  List<BlockInfo> exitBlocks = []
 
   def setup() {
-    specificationContext.currentIteration.feature.addBlockListener { IterationInfo i, BlockInfo b ->
-      blocks << b
-    }
+    specificationContext.currentIteration.feature.addBlockListener([
+      blockEntered: { IterationInfo i, BlockInfo b ->
+        blocks << b
+      },
+      blockExited: { IterationInfo i, BlockInfo b ->
+        exitBlocks << b
+      }] as IBlockListener)
   }
 
   def "BlockListener is called for each Block with text"() {
@@ -24,6 +30,7 @@ class BlockListenerSpec extends Specification {
     cleanup: "cleanup"
     assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP]
     assert blocks.texts == [["setup"], ["precondition"], ["action"], ["assertion"], ["cleanup"]]
+    assert exitBlocks .kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN]
   }
 
   def "SpecificationContext holds a reference to the current block"() {
@@ -55,5 +62,6 @@ class BlockListenerSpec extends Specification {
     assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP]
     and: "cleanup2"
     assert blocks.texts == [["setup", "setup2"], ["precondition", "precondition2"], ["action", "action2"], ["assertion", "assertion2"], ["cleanup", "cleanup2"]]
+    assert exitBlocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN]
   }
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index 2edaabc0ad..ca9fe85a7a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -113,6 +113,30 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     ATHROW
    L16
    FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 4
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 5
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -132,11 +156,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ALOAD 1
-    LDC 4
+    LDC 6
     AALOAD
     LDC Lorg/spockframework/runtime/model/BlockInfo;.class
     ALOAD 1
-    LDC 5
+    LDC 7
     AALOAD
     LDC Lorg/spockframework/runtime/model/BlockKind;.class
     INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
@@ -154,6 +178,30 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LINENUMBER 6 L17
     ICONST_1
     POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 8
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 9
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
     GOTO L18
    L5
    FRAME SAME1 java/lang/Throwable
@@ -187,11 +235,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ALOAD 1
-    LDC 6
+    LDC 10
     AALOAD
     LDC Lorg/spockframework/runtime/model/BlockInfo;.class
     ALOAD 1
-    LDC 7
+    LDC 11
     AALOAD
     LDC Lorg/spockframework/runtime/model/BlockKind;.class
     INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
@@ -208,7 +256,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
    L21
     LINENUMBER 8 L21
     ALOAD 1
-    LDC 8
+    LDC 12
     AALOAD
     ALOAD 0
     ACONST_NULL
@@ -221,6 +269,30 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 13
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 14
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
     INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.getMockController ()Lorg/spockframework/mock/IMockController;
     LDC Lorg/spockframework/mock/runtime/MockController;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index d3e2ac9a96..d4b47fea14 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -137,6 +137,44 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     ATHROW
    L15
    FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "EXPECT", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -193,6 +231,44 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LINENUMBER 6 L16
     ICONST_1
     POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "WHEN", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
     GOTO L17
    L5
    FRAME SAME1 java/lang/Throwable
@@ -285,6 +361,44 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "THEN", 
+      0
+    ]
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "<init>", 
+      0
+    ]
+    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
     INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.getMockController ()Lorg/spockframework/mock/IMockController;
     INVOKEDYNAMIC cast(Lorg/spockframework/mock/IMockController;)Lorg/spockframework/mock/runtime/MockController; [
       // handle kind 0x6 : INVOKESTATIC
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index e73aaa657b..6c2432e75e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -10,6 +10,7 @@ public class apackage.ASpec extends spock.lang.Specification {
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
             java.lang.Object nothing = null
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
             this.getSpecificationContext().getMockController().leaveScope()
         }
         finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index 09b276800e..866d3c94aa 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -10,6 +10,7 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
             java.lang.Object nothing = null
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
             this.getSpecificationContext().getMockController().leaveScope()
         }
         finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
index 4279e87111..8d2b68f906 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
@@ -9,6 +9,7 @@ public void $spock_feature_0_0() {
     try {
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object nothing = null
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
index 69eb4ecb40..8dd68e26fc 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
@@ -15,6 +15,7 @@ public void $spock_feature_0_0() {
     try {
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object nothing = null
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index b3f2e48dab..04d292a8c2 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -16,6 +16,7 @@ public void $spock_feature_0_0() {
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             foobar = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
             try {
                 org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
@@ -24,6 +25,7 @@ public void $spock_feature_0_0() {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
             finally {
             }
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             $spock_feature_throwable = $spock_tmp_throwable
@@ -35,6 +37,9 @@ public void $spock_feature_0_0() {
                     org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
                 }
                 foobar.size()
+                if ( $spock_feature_throwable == null) {
+                    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+                }
             }
             catch (java.lang.Throwable $spock_tmp_throwable) {
                 if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 55c8b121d6..c4465121ad 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -16,6 +16,7 @@ public void $spock_feature_0_0() {
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             (foobar, b) = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
             try {
                 org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
@@ -24,6 +25,7 @@ public void $spock_feature_0_0() {
                 org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
             finally {
             }
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             $spock_feature_throwable = $spock_tmp_throwable
@@ -35,6 +37,9 @@ public void $spock_feature_0_0() {
                     org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
                 }
                 foobar.size()
+                if ( $spock_feature_throwable == null) {
+                    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+                }
             }
             catch (java.lang.Throwable $spock_tmp_throwable) {
                 if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
index 7ff054bfce..126cdbd67a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
@@ -17,6 +17,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
index 38f6c3dc0c..b9106e9551 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
@@ -17,6 +17,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
index 3a79ffdfd5..75b9ff5504 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
@@ -16,6 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
index 3a79ffdfd5..75b9ff5504 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
@@ -16,6 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
index 3a79ffdfd5..75b9ff5504 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
@@ -16,6 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index 22c958f31d..596b2fc456 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -16,7 +16,9 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
@@ -59,6 +61,7 @@ public static void $spock_feature_0_0filter(java.lang.Object a, java.lang.Object
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'b == 2', 16, 7, null, $spock_condition_throwable)}
     finally {
     }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index 60eff18a43..c1272fd8a2 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -11,6 +11,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object x = [1]
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
@@ -19,6 +20,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index b95d366dd6..3e88c599c0 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -11,6 +11,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object x = [1]
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
@@ -19,6 +20,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
index dc09d0b15a..b777a18bc7 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
@@ -11,6 +11,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object x = '[1]'
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         try {
             org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
@@ -19,6 +20,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
index d13911fde1..1e8c0b6a79 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
@@ -11,6 +11,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.lang.Object x = 'a1b'
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         try {
             org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
@@ -19,6 +20,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index 8efeecc816..0b01b5fffc 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -14,6 +14,7 @@ public void $spock_feature_0_0() {
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             foobar = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         }
         catch (java.lang.Throwable $spock_ex) {
             this.getSpecificationContext().setThrownException($spock_ex)
@@ -22,6 +23,7 @@ public void $spock_feature_0_0() {
         }
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.thrownImpl(null, null, java.lang.IllegalStateException)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 652b30e287..64c8c2410b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -14,6 +14,7 @@ public void $spock_feature_0_0() {
         try {
             org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             (foobar, b) = this.foobar()
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         }
         catch (java.lang.Throwable $spock_ex) {
             this.getSpecificationContext().setThrownException($spock_ex)
@@ -22,6 +23,7 @@ public void $spock_feature_0_0() {
         }
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.thrownImpl(null, null, java.lang.IllegalStateException)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }
     finally {

From df354aff21f6484283bc8c7c4774e9a37ee8e7ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 10 Mar 2023 11:57:50 +0100
Subject: [PATCH 06/26] Add interaction example AstSpec

---
 .../smoke/ast/mock/MocksAstSpec.groovy        | 30 +++++++++++++++++++
 .../MocksAstSpec/simple_interaction.groovy    | 28 +++++++++++++++++
 2 files changed, 58 insertions(+)
 create mode 100644 spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy

diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
new file mode 100644
index 0000000000..0b01dc7216
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
@@ -0,0 +1,30 @@
+package org.spockframework.smoke.ast.mock
+
+import org.spockframework.EmbeddedSpecification
+import org.spockframework.specs.extension.SpockSnapshotter
+import spock.lang.Snapshot
+
+class MocksAstSpec extends EmbeddedSpecification {
+
+  @Snapshot(extension = 'groovy')
+  SpockSnapshotter snapshotter
+
+  def "simple interaction"() {
+    given:
+    snapshotter.featureBody()
+
+    when:
+    def result = compiler.transpileFeatureBody("""
+    given:
+    List list = Mock()
+
+    when:
+    list.add(1)
+
+    then:
+    1 * list.add(1)
+""")
+    then:
+    snapshotter.assertThat(result.source).matchesSnapshot()
+  }
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
new file mode 100644
index 0000000000..24304a1a95
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -0,0 +1,28 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+  def "aFeature"() {
+/*--------- tag::snapshot[] ---------*/
+@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
+public void $spock_feature_0_0() {
+    try {
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.util.List list = this.MockImpl('list', java.util.List)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        this.getSpecificationContext().getMockController().enterScope()
+        this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        list.add(1)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        this.getSpecificationContext().getMockController().leaveScope()
+    }
+    finally {
+        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+}
+/*--------- end::snapshot[] ---------*/
+  }
+}

From ffe6edc465a32560d510b3ccad818d8d303f8d1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 29 Mar 2023 17:47:04 +0200
Subject: [PATCH 07/26] Fix block listeners for interactions

---
 .../compiler/DeepBlockRewriter.java           |  53 --------
 .../spockframework/compiler/SpecRewriter.java |  62 +++++++++-
 .../runtime/BlockListenerSpec.groovy          |   2 +-
 ...used_in_AST_transformation_Groovy____3.txt |  95 ++++++++-------
 ...used_in_AST_transformation_Groovy____4.txt | 113 +++++++++---------
 ...filter_block_becomes_its_own_method.groovy |   2 -
 ...rite_keeps_correct_method_reference.groovy |   4 +-
 ...hod_reference_for_multi_assignments.groovy |   4 +-
 .../MocksAstSpec/simple_interaction.groovy    |   4 +-
 9 files changed, 171 insertions(+), 168 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 1812330178..262825ccdd 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -58,59 +58,6 @@ public DeepBlockRewriter(IRewriteResources resources) {
   @Override
   public void visit(Block block) {
     super.visit(block);
-    addBlockEnterCall(block);
-  }
-
-  private void addBlockEnterCall(Block block) {
-    BlockParseInfo blockType = block.getParseInfo();
-    if (blockType == BlockParseInfo.WHERE
-      || blockType == BlockParseInfo.METHOD_END
-      || blockType == BlockParseInfo.ANONYMOUS) return;
-
-    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
-    MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallEnterBlock);
-    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
-    MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, resources.getAstNodeCache().SpockRuntime_CallExitBlock);
-
-    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
-    // so we only call enterBlock if there is no error yet.
-    if (blockType == BlockParseInfo.CLEANUP) {
-      block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
-      block.getAst().add(ifThrowableIsNull(exitBlockCall));
-    } else {
-      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
-      block.getAst().add( new ExpressionStatement(exitBlockCall));
-    }
-  }
-
-  private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
-    return new IfStatement(
-      // if ($spock_feature_throwable == null)
-      new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, resources.getAstNodeCache().Throwable))),
-      new ExpressionStatement(methodCall),
-      EmptyStatement.INSTANCE
-    );
-  }
-
-  private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
-    return createDirectMethodCall(
-      new ClassExpression(resources.getAstNodeCache().SpockRuntime),
-      blockListenerMethod,
-      new ArgumentListExpression(
-        createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
-          resources.getAstNodeCache().SpecInternals_GetSpecificationContext,
-          ArgumentListExpression.EMPTY_ARGUMENTS),
-        new ConstructorCallExpression(resources.getAstNodeCache().BlockInfo,
-          new ArgumentListExpression(
-            new PropertyExpression(
-              new ClassExpression(resources.getAstNodeCache().BlockKind),
-              blockType.name()
-            ),
-            new ListExpression(
-              block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
-            )
-          ))
-      ));
   }
 
   @Override
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index e9441d5deb..2ace4b21d2 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -22,6 +22,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.util.*;
+import java.util.stream.Collectors;
 
 import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.ast.expr.*;
@@ -394,8 +395,11 @@ public void visitMethodAgain(Method method) {
     this.block = null;
 
     if (!movedStatsBackToMethod)
-      for (Block b : method.getBlocks())
+      for (Block b : method.getBlocks()) {
+        // this will only have the blocks if there was no 'cleanup' block in the method
+        addBlockListeners(b);
         method.getStatements().addAll(b.getAst());
+      }
 
     // for global required interactions
     if (method instanceof FeatureMethod)
@@ -412,6 +416,59 @@ public void visitMethodAgain(Method method) {
       clearCurrentBlockOnExit(method.getStatements());
   }
 
+
+  private void addBlockListeners(Block block) {
+    BlockParseInfo blockType = block.getParseInfo();
+    if (blockType == BlockParseInfo.WHERE
+      || blockType == BlockParseInfo.METHOD_END
+      || blockType == BlockParseInfo.ANONYMOUS) return;
+
+    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
+    MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallEnterBlock);
+    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
+    MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallExitBlock);
+
+    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
+    // so we only call enterBlock if there is no error yet.
+    if (blockType == BlockParseInfo.CLEANUP) {
+      block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
+      block.getAst().add(ifThrowableIsNull(exitBlockCall));
+    } else {
+      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
+      block.getAst().add( new ExpressionStatement(exitBlockCall));
+    }
+  }
+
+  private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
+    return new IfStatement(
+      // if ($spock_feature_throwable == null)
+      new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
+      new ExpressionStatement(methodCall),
+      EmptyStatement.INSTANCE
+    );
+  }
+
+  private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
+    return createDirectMethodCall(
+      new ClassExpression(nodeCache.SpockRuntime),
+      blockListenerMethod,
+      new ArgumentListExpression(
+        createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
+          nodeCache.SpecInternals_GetSpecificationContext,
+          ArgumentListExpression.EMPTY_ARGUMENTS),
+        new ConstructorCallExpression(nodeCache.BlockInfo,
+          new ArgumentListExpression(
+            new PropertyExpression(
+              new ClassExpression(nodeCache.BlockKind),
+              blockType.name()
+            ),
+            new ListExpression(
+              block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
+            )
+          ))
+      ));
+  }
+
   @Override
   public void visitAnyBlock(Block block) {
     this.block = block;
@@ -490,6 +547,9 @@ private Statement createMockControllerCall(MethodNode method) {
   @Override
   public void visitCleanupBlock(CleanupBlock block) {
     for (Block b : method.getBlocks()) {
+      // call addBlockListeners() here, as this method will consume the blocks,
+      // so we need to transform here as they will be gone in visitMethodAgain where we normally add them
+      addBlockListeners(b);
       if (b == block) break;
       moveVariableDeclarations(b.getAst(), method.getStatements());
     }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
index 393983ddd7..d7a516d8c1 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
@@ -30,7 +30,7 @@ class BlockListenerSpec extends Specification {
     cleanup: "cleanup"
     assert blocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN, BlockKind.CLEANUP]
     assert blocks.texts == [["setup"], ["precondition"], ["action"], ["assertion"], ["cleanup"]]
-    assert exitBlocks .kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN]
+    assert exitBlocks.kind == [BlockKind.SETUP, BlockKind.EXPECT, BlockKind.WHEN, BlockKind.THEN]
   }
 
   def "SpecificationContext holds a reference to the current block"() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index ca9fe85a7a..1b2dc62e11 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -137,19 +137,6 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    LDC Lorg/spockframework/runtime/SpecificationContext;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/SpecificationContext
-    ACONST_NULL
-    LDC Ljava/lang/Throwable;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST java/lang/Throwable
-    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
-    ACONST_NULL
-    POP
-   L4
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -174,39 +161,27 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L17
-    LINENUMBER 6 L17
-    ICONST_1
-    POP
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 8
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 9
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ACONST_NULL
+    LDC Ljava/lang/Throwable;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    CHECKCAST java/lang/Throwable
+    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
     ACONST_NULL
     POP
-    GOTO L18
+   L4
+    LINENUMBER 6 L4
+    ICONST_1
+    POP
+    GOTO L17
    L5
    FRAME SAME1 java/lang/Throwable
     ASTORE 6
-   L19
+   L18
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -218,17 +193,41 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L7
-    GOTO L18
-   L18
+    GOTO L17
+   L17
    FRAME SAME
-    GOTO L20
+    GOTO L19
    L6
    FRAME SAME1 java/lang/Throwable
     ASTORE 7
     ALOAD 7
     ATHROW
-   L20
+   L19
    FRAME SAME
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    LDC Lorg/spockframework/runtime/SpecificationContext;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/SpecificationContext
+    ALOAD 1
+    LDC 8
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    ALOAD 1
+    LDC 9
+    AALOAD
+    LDC Lorg/spockframework/runtime/model/BlockKind;.class
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    ICONST_0
+    ANEWARRAY java/lang/Object
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
+    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
+    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
+    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
+    CHECKCAST org/spockframework/runtime/model/BlockInfo
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ACONST_NULL
+    POP
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -253,8 +252,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L21
-    LINENUMBER 8 L21
+   L20
+    LINENUMBER 8 L20
     ALOAD 1
     LDC 12
     AALOAD
@@ -300,7 +299,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L22
+   L21
     GOTO L9
    L9
    FRAME SAME
@@ -312,7 +311,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
     ACONST_NULL
     POP
-    GOTO L23
+    GOTO L22
    L10
    FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite;] [java/lang/Throwable]
     ASTORE 8
@@ -326,14 +325,14 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     ALOAD 8
     ATHROW
-   L23
+   L22
    FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L11 L23 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L22 2
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L22 3
+    LOCALVARIABLE this Lapackage/TestSpec; L11 L22 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L21 2
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L21 3
     LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L15 L3 4
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L19 L7 6
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L18 L7 6
     MAXSTACK = 9
     MAXLOCALS = 9
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index d4b47fea14..e162dfe0f9 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -175,20 +175,6 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    ACONST_NULL
-    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
-    ACONST_NULL
-    POP
-   L4
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -227,10 +213,54 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L16
-    LINENUMBER 6 L16
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    ACONST_NULL
+    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
+    ACONST_NULL
+    POP
+   L4
+    LINENUMBER 6 L4
     ICONST_1
     POP
+    GOTO L16
+   L5
+   FRAME SAME1 java/lang/Throwable
+    ASTORE 5
+   L17
+    ALOAD 0
+    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
+    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
+      // handle kind 0x6 : INVOKESTATIC
+      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
+      // arguments:
+      "()", 
+      0
+    ]
+    ALOAD 5
+    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
+    ACONST_NULL
+    POP
+    NOP
+   L7
+    GOTO L16
+   L16
+   FRAME SAME
+    GOTO L18
+   L6
+   FRAME SAME1 java/lang/Throwable
+    ASTORE 6
+    ALOAD 6
+    ATHROW
+   L18
+   FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -269,37 +299,6 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-    GOTO L17
-   L5
-   FRAME SAME1 java/lang/Throwable
-    ASTORE 5
-   L18
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    ALOAD 5
-    INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V
-    ACONST_NULL
-    POP
-    NOP
-   L7
-    GOTO L17
-   L17
-   FRAME SAME
-    GOTO L19
-   L6
-   FRAME SAME1 java/lang/Throwable
-    ASTORE 6
-    ALOAD 6
-    ATHROW
-   L19
-   FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -338,8 +337,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L20
-    LINENUMBER 8 L20
+   L19
+    LINENUMBER 8 L19
     ALOAD 0
     ACONST_NULL
     ACONST_NULL
@@ -410,7 +409,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L21
+   L20
     GOTO L9
    L9
    FRAME SAME
@@ -426,7 +425,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
     ACONST_NULL
     POP
-    GOTO L22
+    GOTO L21
    L10
    FRAME FULL [apackage/TestSpec] [java/lang/Throwable]
     ASTORE 7
@@ -444,15 +443,15 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     ALOAD 7
     ATHROW
-   L22
-    LINENUMBER 9 L22
+   L21
+    LINENUMBER 9 L21
    FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L8 L22 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L11 L21 1
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L12 L21 2
+    LOCALVARIABLE this Lapackage/TestSpec; L8 L21 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L11 L20 1
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L12 L20 2
     LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L14 L3 3
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L18 L7 5
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L17 L7 5
     MAXSTACK = 9
     MAXLOCALS = 8
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index 596b2fc456..916bbc28c8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -46,7 +46,6 @@ public static org.spockframework.runtime.model.DataVariableMultiplication[] $spo
 public static void $spock_feature_0_0filter(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == 1', 15, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 1)))
     }
@@ -61,7 +60,6 @@ public static void $spock_feature_0_0filter(java.lang.Object a, java.lang.Object
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'b == 2', 16, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index 0b01b5fffc..3ae7c3ad78 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -10,17 +10,17 @@ public java.lang.Object foobar() {
 public void $spock_feature_0_0() {
     try {
         java.lang.Object foobar
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         this.getSpecificationContext().setThrownException(null)
         try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             foobar = this.foobar()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         }
         catch (java.lang.Throwable $spock_ex) {
             this.getSpecificationContext().setThrownException($spock_ex)
         }
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.thrownImpl(null, null, java.lang.IllegalStateException)
         org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 64c8c2410b..dceaf5079d 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -10,17 +10,17 @@ public java.lang.Object foobar() {
 public void $spock_feature_0_0() {
     try {
         def (java.lang.Object foobar, java.lang.Object b) = [null, null]
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         this.getSpecificationContext().setThrownException(null)
         try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
             (foobar, b) = this.foobar()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         }
         catch (java.lang.Throwable $spock_ex) {
             this.getSpecificationContext().setThrownException($spock_ex)
         }
         finally {
         }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.thrownImpl(null, null, java.lang.IllegalStateException)
         org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
index 24304a1a95..9ae45aef5a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -9,14 +9,14 @@ public void $spock_feature_0_0() {
     try {
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         java.util.List list = this.MockImpl('list', java.util.List)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         this.getSpecificationContext().getMockController().enterScope()
         this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
         list.add(1)
         org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        this.getSpecificationContext().getMockController().leaveScope()
         org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        this.getSpecificationContext().getMockController().leaveScope()
         org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         this.getSpecificationContext().getMockController().leaveScope()
     }

From 7f68336528fe4610668263c0c580694dfd2321d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 21 Mar 2024 18:23:55 +0100
Subject: [PATCH 08/26] Implement Review Feedback

---
 .../java/org/spockframework/runtime/model/ErrorInfo.java    | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
index f5d24572f3..3da5aff7b6 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
@@ -22,12 +22,10 @@ public class ErrorInfo {
   private final IErrorContext errorContext;
 
   public ErrorInfo(MethodInfo method, Throwable error) {
-    this.method = method;
-    this.error = error;
-    this.errorContext = null;
+    this(method, error, null);
   }
 
-  public ErrorInfo(MethodInfo method, Throwable error, IErrorContext errorContext) {
+  public ErrorInfo(MethodInfo method, Throwable error, @Nullable IErrorContext errorContext) {
     this.method = method;
     this.error = error;
     this.errorContext = errorContext;

From f0e69a7b0cd6e30030cc98afc650deaecb33a085 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Sat, 23 Mar 2024 18:08:29 +0100
Subject: [PATCH 09/26] Fix test in RunListenerSpec

---
 .../runtime/RunListenerSpec.groovy              | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index 95037493c3..4e0736a03e 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -184,6 +184,7 @@ class ASpec extends Specification {
     runner.addPackageImport(Specification.package)
     runner.addClassImport(RegisterRunListener)
     runner.throwFailure = false
+    ErrorInfo errorInfo
 
     when:
     runner.runWithImports """
@@ -226,15 +227,15 @@ class ASpec extends Specification {
 """
 
     then:
-    1 * runListener.error(_) >> { ErrorInfo errorInfo ->
-      if (block != null) {
-        with(errorInfo.errorContext.currentBlock) {
-          it.kind == block
-          it.texts == blockTexts
-        }
-      } else {
-        assert errorInfo.errorContext.currentBlock == null
+    1 * runListener.error(_) >> { ErrorInfo it -> errorInfo = it }
+
+    if (block != null) {
+      with(errorInfo.errorContext.currentBlock) {
+        it.kind == block
+        it.texts == blockTexts
       }
+    } else {
+      assert errorInfo.errorContext.currentBlock == null
     }
 
     cleanup:

From 802b84efb29269b67d52751f93b48007d0fd5daa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Sat, 23 Mar 2024 18:08:55 +0100
Subject: [PATCH 10/26] Implement Review comments

---
 .../spockframework/compiler/SpecRewriter.java    |  8 +++-----
 .../org/spockframework/runtime/SpockRuntime.java | 16 ++++++++++++----
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 2ace4b21d2..baeb529a9a 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -453,9 +453,7 @@ private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo
       new ClassExpression(nodeCache.SpockRuntime),
       blockListenerMethod,
       new ArgumentListExpression(
-        createDirectMethodCall(VariableExpression.THIS_EXPRESSION,
-          nodeCache.SpecInternals_GetSpecificationContext,
-          ArgumentListExpression.EMPTY_ARGUMENTS),
+        getSpecificationContext(),
         new ConstructorCallExpression(nodeCache.BlockInfo,
           new ArgumentListExpression(
             new PropertyExpression(
@@ -547,8 +545,8 @@ private Statement createMockControllerCall(MethodNode method) {
   @Override
   public void visitCleanupBlock(CleanupBlock block) {
     for (Block b : method.getBlocks()) {
-      // call addBlockListeners() here, as this method will consume the blocks,
-      // so we need to transform here as they will be gone in visitMethodAgain where we normally add them
+      // call addBlockListeners() here, as this method will already copy the contents of the blocks,
+      // so we need to transform here as they won't be copied again in visitMethodAgain where we normally add them
       addBlockListeners(b);
       if (b == block) break;
       moveVariableDeclarations(b.getAst(), method.getStatements());
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index fe82e72c28..7392dab94f 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -34,6 +34,7 @@
 import org.spockframework.util.Nullable;
 
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -229,22 +230,29 @@ public static Object[] despreadList(Object[] args, Object[] spreads, int[] posit
   }
 
   public static final String CALL_ENTER_BLOCK = "callEnterBlock";
+
   public static void callEnterBlock(SpecificationContext context, BlockInfo blockInfo) {
     IterationInfo currentIteration = context.getCurrentIteration();
     context.setCurrentBlock(blockInfo);
+    notifyBlockListener(currentIteration, blockListener -> blockListener.blockEntered(currentIteration, blockInfo));
+  }
+
+  private static void notifyBlockListener(IterationInfo currentIteration, Consumer<IBlockListener> consumer) {
     List<IBlockListener> blockListeners = currentIteration.getFeature().getBlockListeners();
     if (blockListeners.isEmpty()) return;
-    blockListeners.forEach(blockListener -> blockListener.blockEntered(currentIteration, blockInfo));
+    blockListeners.forEach(consumer);
   }
+
   public static final String CALL_EXIT_BLOCK = "callExitBlock";
+
   public static void callExitBlock(SpecificationContext context, BlockInfo blockInfo) {
     IterationInfo currentIteration = context.getCurrentIteration();
-    List<IBlockListener> blockListeners = currentIteration.getFeature().getBlockListeners();
-    if (blockListeners.isEmpty()) return;
-    blockListeners.forEach(blockListener -> blockListener.blockExited(currentIteration, blockInfo));
+    notifyBlockListener(currentIteration, blockListener -> blockListener.blockExited(currentIteration, blockInfo));
+    context.setCurrentBlock(null);
   }
 
   public static final String CLEAR_CURRENT_BLOCK = "clearCurrentBlock";
+
   public static void clearCurrentBlock(SpecificationContext context) {
     context.setCurrentBlock(null);
   }

From 5a5cdfde460bfa5d67182020721d429a4642c949 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Sun, 28 Apr 2024 13:22:52 +0200
Subject: [PATCH 11/26] Move current block clearing to PlatformSpecRunner

---
 .../spockframework/compiler/SpecRewriter.java | 19 -----
 .../runtime/PlatformSpecRunner.java           |  1 +
 .../parameterization/DataProviders.groovy     | 12 +--
 ...used_in_AST_transformation_Groovy____3.txt | 77 ++++++-----------
 ...used_in_AST_transformation_Groovy____4.txt | 84 ++++++-------------
 ...ceFeatureBody_can_render_everything.groovy | 12 +--
 ...n_render_everything__Groovy_4_0_2__.groovy | 12 +--
 ...thods_and_its_annotation_by_default.groovy | 12 +--
 ...ers_and_their_annotation_by_default.groovy | 12 +--
 ...rite_keeps_correct_method_reference.groovy | 70 ++++++++--------
 ...hod_reference_for_multi_assignments.groovy | 70 ++++++++--------
 .../DataAstSpec/multi_parameterization.groovy | 22 ++---
 .../nested_multi_parameterization.groovy      | 22 ++---
 ...ith__separators_can_be_combined-[0].groovy | 22 ++---
 ...ith__separators_can_be_combined-[1].groovy | 22 ++---
 ...ith__separators_can_be_combined-[2].groovy | 22 ++---
 ...filter_block_becomes_its_own_method.groovy | 26 +++---
 ...tionsAsSet_is_transformed_correctly.groovy | 28 +++----
 ...InAnyOrder_is_transformed_correctly.groovy | 28 +++----
 ...onditions_are_transformed_correctly.groovy | 28 +++----
 ...onditions_are_transformed_correctly.groovy | 28 +++----
 ...rite_keeps_correct_method_reference.groovy | 30 +++----
 ...hod_reference_for_multi_assignments.groovy | 30 +++----
 .../MocksAstSpec/simple_interaction.groovy    | 28 +++----
 24 files changed, 275 insertions(+), 442 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index baeb529a9a..8646d130f9 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -411,9 +411,6 @@ public void visitMethodAgain(Method method) {
     if (methodHasDeepNonGroupedCondition) {
       defineErrorRethrower(method.getStatements());
     }
-
-    if (method instanceof FeatureMethod)
-      clearCurrentBlockOnExit(method.getStatements());
   }
 
 
@@ -604,22 +601,6 @@ private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpr
     return tryCatchStat;
   }
 
-  // Wraps the feature method in a try-finally to clear the current block on exit.
-  private void clearCurrentBlockOnExit(List<Statement> statements) {
-    MethodCallExpression setCurrentBlockToNull = createDirectMethodCall(new ClassExpression(nodeCache.SpockRuntime),
-      nodeCache.SpockRuntime_ClearCurrentBlock, new ArgumentListExpression(getSpecificationContext()));
-
-    List<Statement> innerStatements = new ArrayList<>(statements);
-
-    TryCatchStatement tryCatchStat =
-        new TryCatchStatement(
-            new BlockStatement(innerStatements, null),
-            new ExpressionStatement(setCurrentBlockToNull));
-
-    statements.clear();
-    statements.add(tryCatchStat);
-  }
-
   private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(VariableExpression assignmentVar) {
     Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
index 959a3b5eaf..93d26f2801 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/PlatformSpecRunner.java
@@ -332,6 +332,7 @@ void runFeatureMethod(SpockExecutionContext context) {
 
     Object[] dataValues = context.getCurrentIteration().getDataValues();
     invoke(context, context.getCurrentInstance(), featureIteration, dataValues);
+    getSpecificationContext(context).setCurrentBlock(null);
   }
 
   void runCleanup(SpockExecutionContext context) {
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
index 73d33b0357..9563c41188 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
@@ -114,11 +114,7 @@ where: x << []
     then:
     result.source == '''\
 public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    try {
-        this.getSpecificationContext().getMockController().leaveScope()
-    }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
@@ -167,11 +163,7 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
     then:
     result.source == '''\
 public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    try {
-        this.getSpecificationContext().getMockController().leaveScope()
-    }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index 1b2dc62e11..4fb8ee7edd 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -12,12 +12,10 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     TRYCATCHBLOCK L4 L5 L5 java/lang/Throwable
     TRYCATCHBLOCK L4 L5 L6 null
     TRYCATCHBLOCK L5 L7 L6 null
-    TRYCATCHBLOCK L8 L9 L10 null
-   L11
+   L8
     NOP
     INVOKESTATIC apackage/TestSpec.$getCallSiteArray ()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
     ASTORE 1
-   L8
     ALOAD 1
     LDC 0
     AALOAD
@@ -27,7 +25,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/ErrorCollector
     ASTORE 2
-   L12
+   L9
     ALOAD 2
     POP
     ALOAD 1
@@ -39,7 +37,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/ValueRecorder
     ASTORE 3
-   L13
+   L10
     ALOAD 3
     POP
     ALOAD 0
@@ -85,11 +83,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.verifyCondition (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Object;)V
     ACONST_NULL
     POP
-    GOTO L14
+    GOTO L11
    L1
    FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite; org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder] [java/lang/Throwable]
     ASTORE 4
-   L15
+   L12
     ALOAD 2
     ALOAD 3
     LDC "true"
@@ -102,16 +100,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L3
-    GOTO L14
-   L14
+    GOTO L11
+   L11
    FRAME SAME
-    GOTO L16
+    GOTO L13
    L2
    FRAME SAME1 java/lang/Throwable
     ASTORE 5
     ALOAD 5
     ATHROW
-   L16
+   L13
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -177,11 +175,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LINENUMBER 6 L4
     ICONST_1
     POP
-    GOTO L17
+    GOTO L14
    L5
    FRAME SAME1 java/lang/Throwable
     ASTORE 6
-   L18
+   L15
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
@@ -193,16 +191,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L7
-    GOTO L17
-   L17
+    GOTO L14
+   L14
    FRAME SAME
-    GOTO L19
+    GOTO L16
    L6
    FRAME SAME1 java/lang/Throwable
     ASTORE 7
     ALOAD 7
     ATHROW
-   L19
+   L16
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -252,8 +250,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L20
-    LINENUMBER 8 L20
+   L17
+    LINENUMBER 8 L17
     ALOAD 1
     LDC 12
     AALOAD
@@ -299,40 +297,13 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L21
-    GOTO L9
-   L9
-   FRAME SAME
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    LDC Lorg/spockframework/runtime/SpecificationContext;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/SpecificationContext
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
-    ACONST_NULL
-    POP
-    GOTO L22
-   L10
-   FRAME FULL [apackage/TestSpec [Lorg/codehaus/groovy/runtime/callsite/CallSite;] [java/lang/Throwable]
-    ASTORE 8
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    LDC Lorg/spockframework/runtime/SpecificationContext;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/SpecificationContext
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
-    ACONST_NULL
-    POP
-    ALOAD 8
-    ATHROW
-   L22
-   FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
+   L18
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L11 L22 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L12 L21 2
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L13 L21 3
-    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L15 L3 4
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L18 L7 6
+    LOCALVARIABLE this Lapackage/TestSpec; L8 L18 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L18 2
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L18 3
+    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 4
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 6
     MAXSTACK = 9
-    MAXLOCALS = 9
+    MAXLOCALS = 8
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index e162dfe0f9..a6d0ee105b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -12,7 +12,6 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     TRYCATCHBLOCK L4 L5 L5 java/lang/Throwable
     TRYCATCHBLOCK L4 L5 L6 null
     TRYCATCHBLOCK L5 L7 L6 null
-    TRYCATCHBLOCK L8 L9 L10 null
    L8
     LDC Lorg/spockframework/runtime/ErrorRethrower;.class
     INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
@@ -30,7 +29,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ASTORE 1
-   L11
+   L9
     ALOAD 1
     POP
     LDC Lorg/spockframework/runtime/ValueRecorder;.class
@@ -49,7 +48,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ASTORE 2
-   L12
+   L10
     ALOAD 2
     POP
     ALOAD 0
@@ -109,11 +108,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.verifyCondition (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Object;)V
     ACONST_NULL
     POP
-    GOTO L13
+    GOTO L11
    L1
    FRAME FULL [apackage/TestSpec org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder] [java/lang/Throwable]
     ASTORE 3
-   L14
+   L12
     ALOAD 1
     ALOAD 2
     LDC "true"
@@ -126,16 +125,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L3
-    GOTO L13
-   L13
+    GOTO L11
+   L11
    FRAME SAME
-    GOTO L15
+    GOTO L13
    L2
    FRAME SAME1 java/lang/Throwable
     ASTORE 4
     ALOAD 4
     ATHROW
-   L15
+   L13
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -230,11 +229,11 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LINENUMBER 6 L4
     ICONST_1
     POP
-    GOTO L16
+    GOTO L14
    L5
    FRAME SAME1 java/lang/Throwable
     ASTORE 5
-   L17
+   L15
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
     INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
@@ -250,16 +249,16 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     POP
     NOP
    L7
-    GOTO L16
-   L16
+    GOTO L14
+   L14
    FRAME SAME
-    GOTO L18
+    GOTO L16
    L6
    FRAME SAME1 java/lang/Throwable
     ASTORE 6
     ALOAD 6
     ATHROW
-   L18
+   L16
    FRAME SAME
     ALOAD 0
     INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
@@ -337,8 +336,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
     ACONST_NULL
     POP
-   L19
-    LINENUMBER 8 L19
+   L17
+    LINENUMBER 8 L17
     ALOAD 0
     ACONST_NULL
     ACONST_NULL
@@ -409,49 +408,14 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V
     ACONST_NULL
     POP
-   L20
-    GOTO L9
-   L9
-   FRAME SAME
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
-    ACONST_NULL
-    POP
-    GOTO L21
-   L10
-   FRAME FULL [apackage/TestSpec] [java/lang/Throwable]
-    ASTORE 7
-    ALOAD 0
-    INVOKEVIRTUAL org/spockframework/lang/SpecInternals.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext;
-    INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.clearCurrentBlock (Lorg/spockframework/runtime/SpecificationContext;)V
-    ACONST_NULL
-    POP
-    ALOAD 7
-    ATHROW
-   L21
-    LINENUMBER 9 L21
-   FRAME APPEND [org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder]
+   L18
+    LINENUMBER 9 L18
     RETURN
-    LOCALVARIABLE this Lapackage/TestSpec; L8 L21 0
-    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L11 L20 1
-    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L12 L20 2
-    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L14 L3 3
-    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L17 L7 5
+    LOCALVARIABLE this Lapackage/TestSpec; L8 L18 0
+    LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L18 1
+    LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L18 2
+    LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 3
+    LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 5
     MAXSTACK = 9
-    MAXLOCALS = 8
+    MAXLOCALS = 7
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index 6c2432e75e..f4da13325a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -7,14 +7,10 @@ public class apackage.ASpec extends spock.lang.Specification {
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-            java.lang.Object nothing = null
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-            this.getSpecificationContext().getMockController().leaveScope()
-        }
-        finally {
-            org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object nothing = null
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        this.getSpecificationContext().getMockController().leaveScope()
     }
 
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index 866d3c94aa..64e22a94fe 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -7,14 +7,10 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-            java.lang.Object nothing = null
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-            this.getSpecificationContext().getMockController().leaveScope()
-        }
-        finally {
-            org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        java.lang.Object nothing = null
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        this.getSpecificationContext().getMockController().leaveScope()
     }
 
 }
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
index 8d2b68f906..a56812945e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
@@ -6,14 +6,10 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        this.getSpecificationContext().getMockController().leaveScope()
-    }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object nothing = null
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
index 8dd68e26fc..9762b7dc12 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
@@ -12,14 +12,10 @@ private java.lang.Object $spock_initializeFields() {
 
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 3, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        this.getSpecificationContext().getMockController().leaveScope()
-    }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object nothing = null
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index 04d292a8c2..9ae7b1b066 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -8,53 +8,49 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    java.lang.Object foobar
+    java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        java.lang.Object foobar
-        java.lang.Throwable $spock_feature_throwable
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        foobar = this.foobar()
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-            foobar = this.foobar()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-            try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
+        finally {
+        }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    }
+    catch (java.lang.Throwable $spock_tmp_throwable) {
+        $spock_feature_throwable = $spock_tmp_throwable
+        throw $spock_tmp_throwable
+    }
+    finally {
+        try {
+            if ( $spock_feature_throwable == null) {
+                org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             }
-            catch (java.lang.Throwable $spock_condition_throwable) {
-                org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
-            finally {
+            foobar.size()
+            if ( $spock_feature_throwable == null) {
+                org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             }
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
-            $spock_feature_throwable = $spock_tmp_throwable
-            throw $spock_tmp_throwable
+            if ( $spock_feature_throwable != null) {
+                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+            } else {
+                throw $spock_tmp_throwable
+            }
         }
         finally {
-            try {
-                if ( $spock_feature_throwable == null) {
-                    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-                }
-                foobar.size()
-                if ( $spock_feature_throwable == null) {
-                    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-                }
-            }
-            catch (java.lang.Throwable $spock_tmp_throwable) {
-                if ( $spock_feature_throwable != null) {
-                    $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
-                } else {
-                    throw $spock_tmp_throwable
-                }
-            }
-            finally {
-            }
         }
-        this.getSpecificationContext().getMockController().leaveScope()
     }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index c4465121ad..d58a88f5b8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -8,53 +8,49 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    def (java.lang.Object foobar, java.lang.Object b) = [null, null]
+    java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-        java.lang.Throwable $spock_feature_throwable
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        (foobar, b) = this.foobar()
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         try {
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-            (foobar, b) = this.foobar()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-            try {
-                org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
+        finally {
+        }
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    }
+    catch (java.lang.Throwable $spock_tmp_throwable) {
+        $spock_feature_throwable = $spock_tmp_throwable
+        throw $spock_tmp_throwable
+    }
+    finally {
+        try {
+            if ( $spock_feature_throwable == null) {
+                org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             }
-            catch (java.lang.Throwable $spock_condition_throwable) {
-                org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
-            finally {
+            foobar.size()
+            if ( $spock_feature_throwable == null) {
+                org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             }
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
-            $spock_feature_throwable = $spock_tmp_throwable
-            throw $spock_tmp_throwable
+            if ( $spock_feature_throwable != null) {
+                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+            } else {
+                throw $spock_tmp_throwable
+            }
         }
         finally {
-            try {
-                if ( $spock_feature_throwable == null) {
-                    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-                }
-                foobar.size()
-                if ( $spock_feature_throwable == null) {
-                    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-                }
-            }
-            catch (java.lang.Throwable $spock_tmp_throwable) {
-                if ( $spock_feature_throwable != null) {
-                    $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
-                } else {
-                    throw $spock_tmp_throwable
-                }
-            }
-            finally {
-            }
         }
-        this.getSpecificationContext().getMockController().leaveScope()
     }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
index 126cdbd67a..294105cf5e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
@@ -6,22 +6,18 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = [])], parameterNames = ['a', 'b'])
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 @org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
index b9106e9551..f4529d9ffd 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
@@ -6,22 +6,18 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = [])], parameterNames = ['a', 'b'])
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 @org.spockframework.runtime.model.DataProviderMetadata(line = 3, dataVariables = ['a', 'b'])
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
index 75b9ff5504..0b05b88f80 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
@@ -5,22 +5,18 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
index 75b9ff5504..0b05b88f80 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
@@ -5,22 +5,18 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
index 75b9ff5504..0b05b88f80 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
@@ -5,22 +5,18 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index 916bbc28c8..2e54448fc6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -5,24 +5,20 @@ class ASpec extends Specification {
   def "aFeature"() {
 /*--------- tag::snapshot[] ---------*/
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 
 public java.lang.Object $spock_feature_0_0prov0() {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index c1272fd8a2..0c3335011d 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -6,25 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object x = [1]
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object x = [1]
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index 3e88c599c0..fe8a68a227 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -6,25 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object x = [1]
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object x = [1]
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
index b777a18bc7..dc34962863 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
@@ -6,25 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object x = '[1]'
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object x = '[1]'
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
index 1e8c0b6a79..9e5830c739 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
@@ -6,25 +6,21 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.lang.Object x = 'a1b'
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
     try {
-        org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.lang.Object x = 'a1b'
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
     }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index 3ae7c3ad78..0f5d72bec5 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -8,26 +8,22 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
+    java.lang.Object foobar
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    this.getSpecificationContext().setThrownException(null)
     try {
-        java.lang.Object foobar
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        this.getSpecificationContext().setThrownException(null)
-        try {
-            foobar = this.foobar()
-        }
-        catch (java.lang.Throwable $spock_ex) {
-            this.getSpecificationContext().setThrownException($spock_ex)
-        }
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.thrownImpl(null, null, java.lang.IllegalStateException)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        foobar = this.foobar()
+    }
+    catch (java.lang.Throwable $spock_ex) {
+        this.getSpecificationContext().setThrownException($spock_ex)
     }
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.thrownImpl(null, null, java.lang.IllegalStateException)
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index dceaf5079d..ca410aeb71 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -8,26 +8,22 @@ public java.lang.Object foobar() {
 }
 
 public void $spock_feature_0_0() {
+    def (java.lang.Object foobar, java.lang.Object b) = [null, null]
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    this.getSpecificationContext().setThrownException(null)
     try {
-        def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        this.getSpecificationContext().setThrownException(null)
-        try {
-            (foobar, b) = this.foobar()
-        }
-        catch (java.lang.Throwable $spock_ex) {
-            this.getSpecificationContext().setThrownException($spock_ex)
-        }
-        finally {
-        }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.thrownImpl(null, null, java.lang.IllegalStateException)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.getSpecificationContext().getMockController().leaveScope()
+        (foobar, b) = this.foobar()
+    }
+    catch (java.lang.Throwable $spock_ex) {
+        this.getSpecificationContext().setThrownException($spock_ex)
     }
     finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.thrownImpl(null, null, java.lang.IllegalStateException)
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
 }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
index 9ae45aef5a..5cbd1b850d 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -6,22 +6,18 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        java.util.List list = this.MockImpl('list', java.util.List)
-        this.getSpecificationContext().getMockController().enterScope()
-        this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        list.add(1)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.getSpecificationContext().getMockController().leaveScope()
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
-        this.getSpecificationContext().getMockController().leaveScope()
-    }
-    finally {
-        org.spockframework.runtime.SpockRuntime.clearCurrentBlock(this.getSpecificationContext())}
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    java.util.List list = this.MockImpl('list', java.util.List)
+    this.getSpecificationContext().getMockController().enterScope()
+    this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    list.add(1)
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.getSpecificationContext().getMockController().leaveScope()
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
   }

From 146689fed65d6bf4cf9cd100bc4b55c816dd3809 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Sun, 28 Apr 2024 13:34:14 +0200
Subject: [PATCH 12/26] Move DataProviders expected ast to snapshots

---
 .../parameterization/DataProviders.groovy     | 61 ++++---------------
 ...rower_variable_in_data_provider_method.txt | 30 +++++++++
 ...ower_variable_in_data_processor_method.txt | 30 +++++++++
 3 files changed, 71 insertions(+), 50 deletions(-)
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt

diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
index 9563c41188..4ab7dba1db 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
@@ -18,6 +18,7 @@ package org.spockframework.smoke.parameterization
 
 import org.spockframework.EmbeddedSpecification
 import org.spockframework.runtime.SpockExecutionException
+import org.spockframework.specs.extension.SpockSnapshotter
 import spock.lang.*
 import spock.util.Show
 
@@ -103,7 +104,10 @@ where: x << []
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
-  def "data provider with asserting closure produces error rethrower variable in data provider method"() {
+  def "data provider with asserting closure produces error rethrower variable in data provider method"(@Snapshot SpockSnapshotter snapshotter) {
+    given:
+    snapshotter.specBody()
+
     when:
     def result = compiler.transpileFeatureBody('''
       where:
@@ -112,30 +116,7 @@ where: x << []
     ''', EnumSet.of(Show.METHODS))
 
     then:
-    result.source == '''\
-public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    this.getSpecificationContext().getMockController().leaveScope()
-}
-
-public java.lang.Object $spock_feature_0_0prov0() {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    return [{ ->
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 2, 29, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 2, 29, null, $spock_condition_throwable)}
-        finally {
-        }
-    }]
-}
-
-public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
-    java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object)
-    java.lang.Object dataVariable = ((null) as java.lang.Object)
-    return new java.lang.Object[]{ dataPipe , dataVariable }
-}'''
+    snapshotter.assertThat(result.source).matchesSnapshot()
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
@@ -152,7 +133,10 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
-  def "data variable with asserting closure produces error rethrower variable in data processor method"() {
+  def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot SpockSnapshotter snapshotter) {
+    given:
+    snapshotter.specBody()
+
     when:
     def result = compiler.transpileFeatureBody('''
       where:
@@ -161,30 +145,7 @@ public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
     ''', EnumSet.of(Show.METHODS))
 
     then:
-    result.source == '''\
-public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
-    this.getSpecificationContext().getMockController().leaveScope()
-}
-
-public java.lang.Object $spock_feature_0_0prov0() {
-    return [null]
-}
-
-public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
-    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
-    java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object)
-    java.lang.Object dataVariable = (({ ->
-        org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
-        try {
-            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 3, 31, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true))
-        }
-        catch (java.lang.Throwable $spock_condition_throwable) {
-            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 3, 31, null, $spock_condition_throwable)}
-        finally {
-        }
-    }) as java.lang.Object)
-    return new java.lang.Object[]{ dataPipe , dataVariable }
-}'''
+    snapshotter.assertThat(result.source).matchesSnapshot()
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt
new file mode 100644
index 0000000000..f5929cc270
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt
@@ -0,0 +1,30 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+/*--------- tag::snapshot[] ---------*/
+public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
+    this.getSpecificationContext().getMockController().leaveScope()
+}
+
+public java.lang.Object $spock_feature_0_0prov0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    return [{ ->
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 2, 29, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 2, 29, null, $spock_condition_throwable)}
+        finally {
+        }
+    }]
+}
+
+public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
+    java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object)
+    java.lang.Object dataVariable = ((null) as java.lang.Object)
+    return new java.lang.Object[]{ dataPipe , dataVariable }
+}
+/*--------- end::snapshot[] ---------*/
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt
new file mode 100644
index 0000000000..97a553191b
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt
@@ -0,0 +1,30 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+/*--------- tag::snapshot[] ---------*/
+public void $spock_feature_0_0(java.lang.Object dataPipe, java.lang.Object dataVariable) {
+    this.getSpecificationContext().getMockController().leaveScope()
+}
+
+public java.lang.Object $spock_feature_0_0prov0() {
+    return [null]
+}
+
+public java.lang.Object $spock_feature_0_0proc(java.lang.Object $spock_p0) {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    java.lang.Object dataPipe = (( $spock_p0 ) as java.lang.Object)
+    java.lang.Object dataVariable = (({ ->
+        org.spockframework.runtime.ValueRecorder $spock_valueRecorder1 = new org.spockframework.runtime.ValueRecorder()
+        try {
+            org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder1.reset(), 'true', 3, 31, null, $spock_valueRecorder1.record($spock_valueRecorder1.startRecordingValue(0), true))
+        }
+        catch (java.lang.Throwable $spock_condition_throwable) {
+            org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder1, 'true', 3, 31, null, $spock_condition_throwable)}
+        finally {
+        }
+    }) as java.lang.Object)
+    return new java.lang.Object[]{ dataPipe , dataVariable }
+}
+/*--------- end::snapshot[] ---------*/
+}

From fa4811b8589028767f44b3c8b681cb7b0ff1a5fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Tue, 14 May 2024 19:29:39 +0200
Subject: [PATCH 13/26] Restore failed block when cleanup runs

---
 .../spockframework/compiler/AstNodeCache.java | 10 ++-
 .../org/spockframework/compiler/AstUtil.java  |  4 +-
 .../spockframework/compiler/SpecRewriter.java | 64 ++++++++++++-------
 .../spockframework/compiler/SpockNames.java   |  1 +
 .../runtime/SpecificationContext.java         |  4 +-
 .../spockframework/runtime/SpockRuntime.java  |  6 --
 ...rite_keeps_correct_method_reference.groovy | 13 ++--
 ...hod_reference_for_multi_assignments.groovy | 13 ++--
 8 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index 361ca3d43b..f071596566 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -71,12 +71,12 @@ public class AstNodeCache {
 
   public final MethodNode SpockRuntime_DespreadList =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0);
+
   public final MethodNode SpockRuntime_CallEnterBlock =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_ENTER_BLOCK).get(0);
+
   public final MethodNode SpockRuntime_CallExitBlock =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_EXIT_BLOCK).get(0);
-  public final MethodNode SpockRuntime_ClearCurrentBlock =
-      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CLEAR_CURRENT_BLOCK).get(0);
 
   public final MethodNode ValueRecorder_Reset =
       ValueRecorder.getDeclaredMethods(org.spockframework.runtime.ValueRecorder.RESET).get(0);
@@ -114,6 +114,12 @@ public class AstNodeCache {
   public final MethodNode SpecificationContext_GetSharedInstance =
       SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_SHARED_INSTANCE).get(0);
 
+  public final MethodNode SpecificationContext_GetBlockCurrentBlock =
+      SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_CURRENT_BLOCK).get(0);
+
+  public final MethodNode SpecificationContext_SetBlockCurrentBlock =
+      SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.SET_CURRENT_BLOCK).get(0);
+
   public final MethodNode List_Get =
       ClassHelper.LIST_TYPE.getDeclaredMethods("get").get(0);
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
index 146f903eea..6880b37de8 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
@@ -391,12 +391,14 @@ public static ConstantExpression primitiveConstExpression(int value) {
   public static ConstantExpression primitiveConstExpression(boolean value) {
     return new ConstantExpression(value, true);
   }
-  public static BinaryExpression createVariableNotNullExpression(VariableExpression var) {
+
+  public static BinaryExpression createVariableIsNotNullExpression(VariableExpression var) {
     return new BinaryExpression(
       new VariableExpression(var),
       Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1),
       new ConstantExpression(null));
   }
+
   public static BinaryExpression createVariableIsNullExpression(VariableExpression var) {
     return new BinaryExpression(
       new VariableExpression(var),
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 8646d130f9..305620f710 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -16,20 +16,25 @@
 
 package org.spockframework.compiler;
 
-import org.spockframework.compiler.model.*;
-import org.spockframework.runtime.SpockException;
-import org.spockframework.util.*;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.*;
-import java.util.stream.Collectors;
-
 import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.ast.expr.*;
 import org.codehaus.groovy.ast.stmt.*;
 import org.codehaus.groovy.runtime.MetaClassHelper;
-import org.codehaus.groovy.syntax.*;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.jetbrains.annotations.NotNull;
 import org.objectweb.asm.Opcodes;
+import org.spockframework.compiler.model.*;
+import org.spockframework.runtime.SpockException;
+import org.spockframework.util.InternalIdentifiers;
+import org.spockframework.util.ObjectUtil;
+import org.spockframework.util.ReflectionUtil;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
@@ -428,19 +433,33 @@ private void addBlockListeners(Block block) {
     // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
     // so we only call enterBlock if there is no error yet.
     if (blockType == BlockParseInfo.CLEANUP) {
-      block.getAst().add(0, ifThrowableIsNull(enterBlockCall));
-      block.getAst().add(ifThrowableIsNull(exitBlockCall));
+      VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
+      block.getAst().addAll(0, asList(
+        ifThrowableIsNotNull(storeFailedBlock(failedBlock)),
+        new ExpressionStatement(enterBlockCall)
+      ));
+      block.getAst().add(new ExpressionStatement(exitBlockCall));
     } else {
       block.getAst().add(0, new ExpressionStatement(enterBlockCall));
       block.getAst().add( new ExpressionStatement(exitBlockCall));
     }
   }
 
-  private IfStatement ifThrowableIsNull(MethodCallExpression methodCall) {
+  private @NotNull Statement storeFailedBlock(VariableExpression failedBlock) {
+    MethodCallExpression getCurrentBlock = createDirectMethodCall(getSpecificationContext(), nodeCache.SpecificationContext_GetBlockCurrentBlock, ArgumentListExpression.EMPTY_ARGUMENTS);
+    return new ExpressionStatement(new BinaryExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), getCurrentBlock));
+  }
+
+  private @NotNull Statement restoreFailedBlock(VariableExpression failedBlock) {
+
+    return new ExpressionStatement(createDirectMethodCall(new CastExpression(nodeCache.SpecificationContext, getSpecificationContext()), nodeCache.SpecificationContext_SetBlockCurrentBlock, new ArgumentListExpression(failedBlock)));
+  }
+
+  private IfStatement ifThrowableIsNotNull(Statement statement) {
     return new IfStatement(
-      // if ($spock_feature_throwable == null)
-      new BooleanExpression(AstUtil.createVariableIsNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
-      new ExpressionStatement(methodCall),
+      // if ($spock_feature_throwable != null)
+      new BooleanExpression(AstUtil.createVariableIsNotNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
+      statement,
       EmptyStatement.INSTANCE
     );
   }
@@ -560,9 +579,10 @@ public void visitCleanupBlock(CleanupBlock block) {
     }
 
     CatchStatement featureCatchStat = createThrowableAssignmentAndRethrowCatchStatement(featureThrowableVar);
-
-    List<Statement> cleanupStats = singletonList(
-        createCleanupTryCatch(block, featureThrowableVar));
+    VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
+    List<Statement> cleanupStats = asList(
+        new ExpressionStatement(new DeclarationExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), ConstantExpression.NULL)),
+        createCleanupTryCatch(block, featureThrowableVar, failedBlock));
 
     TryCatchStatement tryFinally =
         new TryCatchStatement(
@@ -588,13 +608,13 @@ private Statement createVariableDeclarationStatement(VariableExpression var) {
     return new ExpressionStatement(throwableDecl);
   }
 
-  private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpression featureThrowableVar) {
+  private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpression featureThrowableVar, VariableExpression failedBlock) {
     List<Statement> cleanupStats = new ArrayList<>(block.getAst());
-
     TryCatchStatement tryCatchStat =
         new TryCatchStatement(
             new BlockStatement(cleanupStats, null),
-            EmptyStatement.INSTANCE);
+            ifThrowableIsNotNull(restoreFailedBlock(failedBlock))
+        );
 
     tryCatchStat.addCatch(createHandleSuppressedThrowableStatement(featureThrowableVar));
 
@@ -621,7 +641,7 @@ private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(Variabl
   private CatchStatement createHandleSuppressedThrowableStatement(VariableExpression featureThrowableVar) {
     Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
 
-    BinaryExpression featureThrowableNotNullExpr = AstUtil.createVariableNotNullExpression(featureThrowableVar);
+    BinaryExpression featureThrowableNotNullExpr = AstUtil.createVariableIsNotNullExpression(featureThrowableVar);
 
     List<Statement> addSuppressedStats =
       singletonList(new ExpressionStatement(
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
index aa4edf9024..4eadf163ba 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
@@ -3,6 +3,7 @@
 public class SpockNames {
   public static final String VALUE_RECORDER = "$spock_valueRecorder";
   public static final String ERROR_COLLECTOR = "$spock_errorCollector";
+  public static final String FAILED_BLOCK = "$spock_failedBlock";
   public static final String OLD_VALUE = "$spock_oldValue";
   public static final String SPOCK_EX = "$spock_ex";
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
index 79d1222745..27fd9dcf0a 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
@@ -58,10 +58,12 @@ public IterationInfo getCurrentIteration() {
     return currentIteration;
   }
 
-  void setCurrentBlock(BlockInfo blockInfo) {
+  public static final String SET_CURRENT_BLOCK = "setCurrentBlock";
+  public void setCurrentBlock(BlockInfo blockInfo) {
     this.currentBlock = blockInfo;
   }
 
+  public static final String GET_CURRENT_BLOCK = "getCurrentBlock";
   @Override
   public BlockInfo getCurrentBlock() {
     return currentBlock;
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index 7392dab94f..9796979229 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -251,12 +251,6 @@ public static void callExitBlock(SpecificationContext context, BlockInfo blockIn
     context.setCurrentBlock(null);
   }
 
-  public static final String CLEAR_CURRENT_BLOCK = "clearCurrentBlock";
-
-  public static void clearCurrentBlock(SpecificationContext context) {
-    context.setCurrentBlock(null);
-  }
-
   private static List<Object> getValues(ValueRecorder recorder) {
       return recorder == null ? null : recorder.getValues();
   }
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index 9ae7b1b066..986dc656ba 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -31,14 +31,14 @@ public void $spock_feature_0_0() {
         throw $spock_tmp_throwable
     }
     finally {
+        org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null
         try {
-            if ( $spock_feature_throwable == null) {
-                org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            if ( $spock_feature_throwable != null) {
+                $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             foobar.size()
-            if ( $spock_feature_throwable == null) {
-                org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-            }
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
@@ -48,6 +48,9 @@ public void $spock_feature_0_0() {
             }
         }
         finally {
+            if ( $spock_feature_throwable != null) {
+                ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock)
+            }
         }
     }
     this.getSpecificationContext().getMockController().leaveScope()
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index d58a88f5b8..7c20cce30e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -31,14 +31,14 @@ public void $spock_feature_0_0() {
         throw $spock_tmp_throwable
     }
     finally {
+        org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null
         try {
-            if ( $spock_feature_throwable == null) {
-                org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            if ( $spock_feature_throwable != null) {
+                $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
             foobar.size()
-            if ( $spock_feature_throwable == null) {
-                org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
-            }
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
@@ -48,6 +48,9 @@ public void $spock_feature_0_0() {
             }
         }
         finally {
+            if ( $spock_feature_throwable != null) {
+                ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock)
+            }
         }
     }
     this.getSpecificationContext().getMockController().leaveScope()

From 1f6aa2d7634b56e5eb67aac49f2a7e77470457b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 23 May 2024 20:40:23 +0200
Subject: [PATCH 14/26] Implement Review comments re SpecificationContext

---
 .../lang/ISpecificationContext.java           |  7 +++---
 .../runtime/DataIteratorFactory.java          |  2 +-
 .../spockframework/runtime/ErrorContext.java  |  9 +++----
 .../runtime/SpecificationContext.java         | 25 ++++++++++++-------
 4 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
index 7aaf6bf298..8e3b01674a 100644
--- a/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/lang/ISpecificationContext.java
@@ -27,10 +27,11 @@
 public interface ISpecificationContext {
   @Nullable
   SpecInfo getCurrentSpec();
-  @Nullable
+
   FeatureInfo getCurrentFeature();
-  @Nullable
+
   IterationInfo getCurrentIteration();
+
   @Nullable
   BlockInfo getCurrentBlock();
 
@@ -40,6 +41,4 @@ public interface ISpecificationContext {
   IMockController getMockController();
 
   IThreadAwareMockController getThreadAwareMockController();
-
-  boolean isSharedContext();
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
index 4312d8f297..235b439dd8 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/DataIteratorFactory.java
@@ -50,7 +50,7 @@ protected Object invokeRaw(Object target, MethodInfo method, Object... arguments
     }
 
     protected IErrorContext getErrorContext() {
-      return ErrorContext.from(context.getCurrentInstance().getSpecificationContext());
+      return ErrorContext.from((SpecificationContext) context.getCurrentInstance().getSpecificationContext());
     }
 
     protected int estimateNumIterations(@Nullable Object dataProvider) {
diff --git a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
index d1d28ea67a..b0da3710f8 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
@@ -16,14 +16,11 @@ private ErrorContext(SpecInfo spec, FeatureInfo feature, IterationInfo iteration
     this.block = block;
   }
 
-  static ErrorContext from(ISpecificationContext context) {
-    if (context.isSharedContext()) {
-      return new ErrorContext(context.getCurrentSpec(), null, null, null);
-    }
+  static ErrorContext from(SpecificationContext context) {
     return new ErrorContext(
       context.getCurrentSpec(),
-      context.getCurrentFeature(),
-      context.getCurrentIteration(),
+      context.getCurrentFeatureOrNull(),
+      context.getCurrentIterationOrNull(),
       context.getCurrentBlock()
     );
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
index 27fd9dcf0a..b17b607150 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
@@ -5,6 +5,7 @@
 import org.spockframework.mock.IThreadAwareMockController;
 import org.spockframework.mock.runtime.MockController;
 import org.spockframework.runtime.model.*;
+import org.spockframework.util.Nullable;
 import spock.lang.Specification;
 
 public class SpecificationContext implements ISpecificationContext {
@@ -46,18 +47,32 @@ public FeatureInfo getCurrentFeature() {
     return currentFeature;
   }
 
+  @Nullable
+  FeatureInfo getCurrentFeatureOrNull() {
+    return currentFeature;
+  }
+
   public void setCurrentFeature(FeatureInfo currentFeature) {
     this.currentFeature = currentFeature;
   }
 
   @Override
   public IterationInfo getCurrentIteration() {
-    if (isSharedContext()) {
+    if (currentIteration == null) {
       throw new IllegalStateException("Cannot request current iteration in @Shared context, or feature context");
     }
     return currentIteration;
   }
 
+  @Nullable
+  IterationInfo getCurrentIterationOrNull() {
+    return currentIteration;
+  }
+
+  public void setCurrentIteration(IterationInfo currentIteration) {
+    this.currentIteration = currentIteration;
+  }
+
   public static final String SET_CURRENT_BLOCK = "setCurrentBlock";
   public void setCurrentBlock(BlockInfo blockInfo) {
     this.currentBlock = blockInfo;
@@ -69,10 +84,6 @@ public BlockInfo getCurrentBlock() {
     return currentBlock;
   }
 
-  public void setCurrentIteration(IterationInfo currentIteration) {
-    this.currentIteration = currentIteration;
-  }
-
   @Override
   public Throwable getThrownException() {
     return thrownException;
@@ -94,8 +105,4 @@ public IThreadAwareMockController getThreadAwareMockController() {
     return mockController;
   }
 
-  @Override
-  public boolean isSharedContext() {
-    return currentFeature == null || currentIteration == null;
-  }
 }

From bbba7f5a50a84fa7079c41d25643623269150102 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 23 May 2024 20:46:22 +0200
Subject: [PATCH 15/26] Implement Review comments re ErrorContext/Info

---
 .../java/org/spockframework/runtime/ErrorContext.java    | 1 -
 .../java/org/spockframework/runtime/model/ErrorInfo.java | 9 +--------
 .../org/spockframework/runtime/model/IErrorContext.java  | 3 +++
 .../spockframework/runtime/AsyncRunListenerSpec.groovy   | 2 +-
 4 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
index b0da3710f8..34f670df9e 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
@@ -1,6 +1,5 @@
 package org.spockframework.runtime;
 
-import org.spockframework.lang.ISpecificationContext;
 import org.spockframework.runtime.model.*;
 
 class ErrorContext implements IErrorContext {
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
index 3da5aff7b6..a978c325e2 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
@@ -14,18 +14,12 @@
 
 package org.spockframework.runtime.model;
 
-import org.spockframework.util.Nullable;
-
 public class ErrorInfo {
   private final MethodInfo method;
   private final Throwable error;
   private final IErrorContext errorContext;
 
-  public ErrorInfo(MethodInfo method, Throwable error) {
-    this(method, error, null);
-  }
-
-  public ErrorInfo(MethodInfo method, Throwable error, @Nullable IErrorContext errorContext) {
+  public ErrorInfo(MethodInfo method, Throwable error, IErrorContext errorContext) {
     this.method = method;
     this.error = error;
     this.errorContext = errorContext;
@@ -39,7 +33,6 @@ public Throwable getException() {
     return error;
   }
 
-  @Nullable
   public IErrorContext getErrorContext() {
     return errorContext;
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
index 1a5736e9ab..42c7c7317c 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -5,10 +5,13 @@
 public interface IErrorContext {
   @Nullable
   SpecInfo getCurrentSpec();
+
   @Nullable
   FeatureInfo getCurrentFeature();
+
   @Nullable
   IterationInfo getCurrentIteration();
+
   @Nullable
   BlockInfo getCurrentBlock();
 }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy
index 3a33687d87..b33cfb6493 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy
@@ -13,7 +13,7 @@ class AsyncRunListenerSpec extends Specification {
     def specInfo = new SpecInfoBuilder(getClass()).build()
     def featureInfo = specInfo.features[0]
     def iterationInfo = new IterationInfo(featureInfo, 0, [] as Object[], 1)
-    def errorInfo = new ErrorInfo(featureInfo.featureMethod, new Exception())
+    def errorInfo = new ErrorInfo(featureInfo.featureMethod, new Exception(), new ErrorContext(specInfo, featureInfo, iterationInfo, null))
 
     when:
     asyncListener.start()

From 49dfa26a0550acce905186845b380d93d90eb0c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 31 May 2024 10:58:15 +0200
Subject: [PATCH 16/26] Revert all changes to DeepBlockRewriter

---
 .../spockframework/compiler/DeepBlockRewriter.java   | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
index 262825ccdd..396206b854 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/DeepBlockRewriter.java
@@ -15,23 +15,19 @@
 
 package org.spockframework.compiler;
 
-import org.codehaus.groovy.ast.MethodNode;
-import org.jetbrains.annotations.NotNull;
-import org.spockframework.compiler.model.*;
-
-import java.util.stream.Collectors;
-
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.syntax.Types;
+import org.spockframework.compiler.model.*;
 import org.spockframework.util.Identifiers;
 import org.spockframework.util.Nullable;
 
 import java.util.List;
 
 import static org.codehaus.groovy.ast.expr.MethodCallExpression.NO_ARGUMENTS;
-import static org.spockframework.compiler.AstUtil.createDirectMethodCall;
 
 /**
  * Walks the statement and expression tree to:

From 4f4e4c5ee5fb4f74c60711cc5550aff6aceb32c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 31 May 2024 11:58:57 +0200
Subject: [PATCH 17/26] Implement Review Comments

---
 .../spockframework/compiler/SpecRewriter.java | 17 ++++++----
 .../spockframework/runtime/ErrorContext.java  | 12 ++++++-
 .../spockframework/runtime/IRunListener.java  |  2 ++
 .../runtime/SpecInfoBuilder.java              |  5 +--
 .../runtime/extension/IBlockListener.java     | 34 +++++++++++++++++++
 .../runtime/model/BlockInfo.java              |  4 +++
 .../runtime/model/ErrorInfo.java              |  9 +++++
 .../runtime/model/FeatureInfo.java            | 10 +++++-
 .../runtime/model/IErrorContext.java          |  5 +++
 .../runtime/RunListenerSpec.groovy            |  1 +
 10 files changed, 87 insertions(+), 12 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 305620f710..ce3b6109ab 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -399,16 +399,20 @@ private void handleWhereBlock(Method method) {
   public void visitMethodAgain(Method method) {
     this.block = null;
 
-    if (!movedStatsBackToMethod)
+    if (!movedStatsBackToMethod) {
       for (Block b : method.getBlocks()) {
-        // this will only have the blocks if there was no 'cleanup' block in the method
+        // This will only run if there was no 'cleanup' block in the method.
+        // Otherwise, the blocks have already been copied to try block by visitCleanupBlock.
+        // We need to run as late as possible, so we'll have to do the handling here and in visitCleanupBlock.
         addBlockListeners(b);
         method.getStatements().addAll(b.getAst());
       }
+    }
 
     // for global required interactions
-    if (method instanceof FeatureMethod)
+    if (method instanceof FeatureMethod) {
       method.getStatements().add(createMockControllerCall(nodeCache.MockController_LeaveScope));
+    }
 
     if (methodHasCondition) {
       defineValueRecorder(method.getStatements(), "");
@@ -423,6 +427,7 @@ private void addBlockListeners(Block block) {
     BlockParseInfo blockType = block.getParseInfo();
     if (blockType == BlockParseInfo.WHERE
       || blockType == BlockParseInfo.METHOD_END
+      || blockType == BlockParseInfo.COMBINED
       || blockType == BlockParseInfo.ANONYMOUS) return;
 
     // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
@@ -430,9 +435,10 @@ private void addBlockListeners(Block block) {
     // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
     MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallExitBlock);
 
-    // As the cleanup block finalizes the specification, it would override any previous block in ErrorInfo,
-    // so we only call enterBlock if there is no error yet.
     if (blockType == BlockParseInfo.CLEANUP) {
+      // In case of a cleanup block we need store a reference of the previously `currentBlock` in case that an exception occurred
+      // and restore it at the end of the cleanup block, so that the correct `BlockInfo` is available for the `IErrorContext`.
+      // The restoration happens in the `finally` statement created by `createCleanupTryCatch`.
       VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
       block.getAst().addAll(0, asList(
         ifThrowableIsNotNull(storeFailedBlock(failedBlock)),
@@ -451,7 +457,6 @@ private void addBlockListeners(Block block) {
   }
 
   private @NotNull Statement restoreFailedBlock(VariableExpression failedBlock) {
-
     return new ExpressionStatement(createDirectMethodCall(new CastExpression(nodeCache.SpecificationContext, getSpecificationContext()), nodeCache.SpecificationContext_SetBlockCurrentBlock, new ArgumentListExpression(failedBlock)));
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
index 34f670df9e..8beeff184e 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
@@ -1,6 +1,7 @@
 package org.spockframework.runtime;
 
 import org.spockframework.runtime.model.*;
+import org.spockframework.util.Nullable;
 
 class ErrorContext implements IErrorContext {
   private final SpecInfo spec;
@@ -8,7 +9,7 @@ class ErrorContext implements IErrorContext {
   private final IterationInfo iteration;
   private final BlockInfo block;
 
-  private ErrorContext(SpecInfo spec, FeatureInfo feature, IterationInfo iteration, BlockInfo block) {
+  private ErrorContext(@Nullable SpecInfo spec, @Nullable FeatureInfo feature, @Nullable IterationInfo iteration, @Nullable BlockInfo block) {
     this.spec = spec;
     this.feature = feature;
     this.iteration = iteration;
@@ -43,4 +44,13 @@ public IterationInfo getCurrentIteration() {
   public BlockInfo getCurrentBlock() {
     return block;
   }
+
+  @Override
+  public String toString() {
+    return "ErrorContext{Spec: " + (spec == null ? "null" : spec.getDisplayName()) +
+      ", Feature: " + (feature == null ? "null" : feature.getDisplayName()) +
+      ", Iteration: " + (iteration == null ? "null" : iteration.getDisplayName()) +
+      ", Block: " + (block == null ? "null" : (block.getKind() + " " + block.getTexts()))
+      + "}";
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java b/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
index 1764c54565..6c27252faa 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
@@ -21,7 +21,9 @@
  * Listens to a spec run. Currently, only extensions can register listeners.
  * They do so by invoking <tt>SpecInfo.addListener()<tt>. See
  * {@link StepwiseExtension} for an example of how to use a listener.
+ * <p>
  *
+ * @see org.spockframework.runtime.extension.IBlockListener
  * @author Peter Niederwieser
  */
 public interface IRunListener {
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java
index 7c7f4445fb..4eb810a245 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java
@@ -169,10 +169,7 @@ private FeatureInfo createFeature(Method method, FeatureMetadata featureMetadata
     }
 
     for (BlockMetadata blockMetadata : featureMetadata.blocks()) {
-      BlockInfo block = new BlockInfo();
-      block.setKind(blockMetadata.kind());
-      block.setTexts(asList(blockMetadata.texts()));
-      feature.addBlock(block);
+      feature.addBlock(new BlockInfo(blockMetadata.kind(), asList(blockMetadata.texts())));
     }
 
     return feature;
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
index 3cedac71e5..32bd914922 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
@@ -1,9 +1,43 @@
 package org.spockframework.runtime.extension;
 
 import org.spockframework.runtime.model.BlockInfo;
+import org.spockframework.runtime.model.ErrorInfo;
 import org.spockframework.runtime.model.IterationInfo;
+import org.spockframework.util.Beta;
 
+/**
+ * Listens to block events during the execution of a feature.
+ * <p>
+ * Usually used in conjunction with {@link org.spockframework.runtime.IRunListener}.
+ * Currently, only extensions can register listeners.
+ * They do so by invoking {@link org.spockframework.runtime.model.FeatureInfo#addBlockListener(IBlockListener)}.
+ * It is preferred to use a single instance of this.
+ * <p>
+ * It is discouraged to perform long-running operations in the listener methods,
+ * as they are called during the execution of the specification.
+ * It is discouraged to perform any side effects affecting the tests.
+ * <p>
+ * When an exception is thrown in a block, the {@code blockExited} will not be called for that block.
+ * If a cleanup block is present the cleanup block listener methods will still be called.
+ *
+ * @see org.spockframework.runtime.IRunListener
+ * @author Leonard Brünings
+ * @since 2.4
+ */
+@Beta
 public interface IBlockListener {
+
+  /**
+   * Called when a block is entered.
+   */
   default void blockEntered(IterationInfo iterationInfo, BlockInfo blockInfo) {}
+
+  /**
+   * Called when a block is exited.
+   * <p>
+   * This method is not called if an exception is thrown in the block.
+   * The block that was active will be available in the {@link org.spockframework.runtime.model.IErrorContext}
+   * and can be observed via {@link org.spockframework.runtime.IRunListener#error(ErrorInfo)}.
+   */
   default void blockExited(IterationInfo iterationInfo, BlockInfo blockInfo) {}
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
index 0a19bf6564..3cac692cbf 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
@@ -16,6 +16,8 @@
 
 package org.spockframework.runtime.model;
 
+import org.spockframework.util.Nullable;
+
 import java.util.List;
 
 /**
@@ -35,6 +37,7 @@ public BlockInfo(BlockKind kind, List<String> texts) {
     this.texts = texts;
   }
 
+  @Nullable
   public BlockKind getKind() {
     return kind;
   }
@@ -43,6 +46,7 @@ public void setKind(BlockKind kind) {
     this.kind = kind;
   }
 
+  @Nullable
   public List<String> getTexts() {
     return texts;
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
index a978c325e2..7a5c0f29b9 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
@@ -36,4 +36,13 @@ public Throwable getException() {
   public IErrorContext getErrorContext() {
     return errorContext;
   }
+
+  @Override
+  public String toString() {
+    return "ErrorInfo{" +
+      "method=" + method +
+      ", errorContext=" + errorContext +
+      ", error=" + error +
+      '}';
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
index 00c5e456a9..53ee0d5744 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java
@@ -168,7 +168,7 @@ public List<IMethodInterceptor> getInitializerInterceptors() {
   }
 
   /**
-   * Adds a initializer interceptor for this feature.
+   * Adds an initializer interceptor for this feature.
    * <p>
    * The feature-scoped interceptors will execute before the spec interceptors.
    *
@@ -224,10 +224,18 @@ public void addIterationInterceptor(IMethodInterceptor interceptor) {
     iterationInterceptors.add(interceptor);
   }
 
+  /**
+   * @since 2.4
+   */
+  @Beta
   public List<IBlockListener> getBlockListeners() {
     return blockListeners;
   }
 
+  /**
+   * @since 2.4
+   */
+  @Beta
   public void addBlockListener(IBlockListener blockListener) {
     blockListeners.add(blockListener);
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
index 42c7c7317c..3ddbf1a0df 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -2,6 +2,11 @@
 
 import org.spockframework.util.Nullable;
 
+/**
+ * Provides context information for an error that occurred during the execution of a specification.
+ * <p>
+ * Depending on the context in which the error occurred, some of the methods may return {@code null}.
+ */
 public interface IErrorContext {
   @Nullable
   SpecInfo getCurrentSpec();
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index 4e0736a03e..c9af3050d2 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -234,6 +234,7 @@ class ASpec extends Specification {
         it.kind == block
         it.texts == blockTexts
       }
+      assert errorInfo.errorContext.toString() == ''
     } else {
       assert errorInfo.errorContext.currentBlock == null
     }

From 3f8f22a27658a54a2d2ec1b379d0aff988f7be3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Sun, 23 Jun 2024 19:02:50 +0200
Subject: [PATCH 18/26] Use BlockInfo from SpecInfo via an index lookup

---
 .../compiler/SpecAnnotator.java               |   4 +
 .../spockframework/compiler/SpecParser.java   |   6 +
 .../spockframework/compiler/SpecRewriter.java |  12 +-
 .../compiler/model/AnonymousBlock.java        |   5 +
 .../spockframework/compiler/model/Block.java  |  21 +++
 .../spockframework/runtime/SpockRuntime.java  |   6 +-
 .../runtime/RunListenerSpec.groovy            |   1 -
 .../spockframework/smoke/ast/BlocksAst.groovy |  88 +++++++++
 ...used_in_AST_transformation_Groovy____3.txt | 112 ++----------
 ...used_in_AST_transformation_Groovy____4.txt | 170 ++----------------
 ...ceFeatureBody_can_render_everything.groovy |   4 +-
 ...n_render_everything__Groovy_4_0_2__.groovy |   4 +-
 ...thods_and_its_annotation_by_default.groovy |   4 +-
 ...ers_and_their_annotation_by_default.groovy |   4 +-
 ...servable_blocks_with_GString_labels.groovy |  40 +++++
 ...observable_blocks_with_empty_labels.groovy |  50 ++++++
 ...vable_blocks_with_labels_and_blocks.groovy |  52 ++++++
 ...rite_keeps_correct_method_reference.groovy |  12 +-
 ...hod_reference_for_multi_assignments.groovy |  12 +-
 .../DataAstSpec/multi_parameterization.groovy |   4 +-
 .../nested_multi_parameterization.groovy      |   4 +-
 ...ith__separators_can_be_combined-[0].groovy |   4 +-
 ...ith__separators_can_be_combined-[1].groovy |   4 +-
 ...ith__separators_can_be_combined-[2].groovy |   4 +-
 ...filter_block_becomes_its_own_method.groovy |   8 +-
 ...tionsAsSet_is_transformed_correctly.groovy |   8 +-
 ...InAnyOrder_is_transformed_correctly.groovy |   8 +-
 ...onditions_are_transformed_correctly.groovy |   8 +-
 ...onditions_are_transformed_correctly.groovy |   8 +-
 ...rite_keeps_correct_method_reference.groovy |   8 +-
 ...hod_reference_for_multi_assignments.groovy |   8 +-
 .../MocksAstSpec/simple_interaction.groovy    |  12 +-
 32 files changed, 357 insertions(+), 338 deletions(-)
 create mode 100644 spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
 create mode 100644 spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
index 877203e093..25061a9e23 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
@@ -30,6 +30,7 @@
 
 import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.ast.expr.*;
+import org.spockframework.util.Assert;
 
 import static java.util.stream.Collectors.*;
 import static org.spockframework.compiler.AstUtil.*;
@@ -205,6 +206,9 @@ private void addBlockMetadata(Block block, BlockKind kind) {
     for (String text : block.getDescriptions())
       textExprs.addExpression(new ConstantExpression(text));
     blockAnn.setMember(BlockMetadata.TEXTS, textExprs);
+    int index = blockAnnElems.getExpressions().size();
+    Assert.that(index == block.getBlockMetaDataIndex(),
+      () -> kind+" block mismatch of index: " + index + ", block.getBlockMetaDataIndex(): " + block.getBlockMetaDataIndex());
     blockAnnElems.addExpression(new AnnotationConstantExpression(blockAnn));
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
index f7a32ffb8b..6659d24d5b 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
@@ -200,6 +200,12 @@ private void buildBlocks(Method method) throws InvalidSpecCompileException {
     checkIsValidSuccessor(method, BlockParseInfo.METHOD_END,
         method.getAst().getLastLineNumber(), method.getAst().getLastColumnNumber());
 
+    // set the block metaData index for each block this must be equal to the index of the block in the @BlockMetadata annotation
+    int i = -1;
+    for (Block block : method.getBlocks()) {
+      if(!block.hasBlockMetadata()) continue;
+      block.setBlockMetaDataIndex(++i);
+    }
     // now that statements have been copied to blocks, the original statement
     // list is cleared; statements will be copied back after rewriting is done
     stats.clear();
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index ce3b6109ab..7a0c69c138 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -470,21 +470,13 @@ private IfStatement ifThrowableIsNotNull(Statement statement) {
   }
 
   private MethodCallExpression createBlockListenerCall(Block block, BlockParseInfo blockType, MethodNode blockListenerMethod) {
+    if (block.getBlockMetaDataIndex() < 0) throw new SpockException("Block metadata index not set: " + block);
     return createDirectMethodCall(
       new ClassExpression(nodeCache.SpockRuntime),
       blockListenerMethod,
       new ArgumentListExpression(
         getSpecificationContext(),
-        new ConstructorCallExpression(nodeCache.BlockInfo,
-          new ArgumentListExpression(
-            new PropertyExpression(
-              new ClassExpression(nodeCache.BlockKind),
-              blockType.name()
-            ),
-            new ListExpression(
-              block.getDescriptions().stream().map(ConstantExpression::new).collect(Collectors.toList())
-            )
-          ))
+        new ConstantExpression(block.getBlockMetaDataIndex(), true)
       ));
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java b/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java
index fd1a7528c6..94c5224938 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/model/AnonymousBlock.java
@@ -37,4 +37,9 @@ public void accept(ISpecVisitor visitor) throws Exception {
   public BlockParseInfo getParseInfo() {
     return BlockParseInfo.ANONYMOUS;
   }
+
+  @Override
+  public boolean hasBlockMetadata() {
+    return false;
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/Block.java b/spock-core/src/main/java/org/spockframework/compiler/model/Block.java
index 49ba575512..1ee316b772 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/model/Block.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/model/Block.java
@@ -31,6 +31,7 @@ public abstract class Block extends Node<Method, List<Statement>> {
   private final List<String> descriptions = new ArrayList<>(3);
   private Block prev;
   private Block next;
+  private int blockMetaDataIndex = -1;
 
   public Block(Method parent) {
     super(parent, new ArrayList<>());
@@ -80,5 +81,25 @@ public boolean isFirstInChain() {
     return isFirst() || getClass() != prev.getClass();
   }
 
+  public void setBlockMetaDataIndex(int blockMetaDataIndex) {
+    this.blockMetaDataIndex = blockMetaDataIndex;
+  }
+
+  public int getBlockMetaDataIndex() {
+    return blockMetaDataIndex;
+  }
+
+  /**
+   * Returns whether this block will be written to the {@link org.spockframework.runtime.model.BlockMetadata}.
+   */
+  public boolean hasBlockMetadata() {
+    return true;
+  }
+
   public abstract BlockParseInfo getParseInfo();
+
+  @Override
+  public String toString() {
+    return "Block kind: " + getClass().getSimpleName() + ", descriptions: " + descriptions;
+  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index 9796979229..c7dabcff59 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -231,8 +231,9 @@ public static Object[] despreadList(Object[] args, Object[] spreads, int[] posit
 
   public static final String CALL_ENTER_BLOCK = "callEnterBlock";
 
-  public static void callEnterBlock(SpecificationContext context, BlockInfo blockInfo) {
+  public static void callEnterBlock(SpecificationContext context, int blockInfoIndex) {
     IterationInfo currentIteration = context.getCurrentIteration();
+    BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex);
     context.setCurrentBlock(blockInfo);
     notifyBlockListener(currentIteration, blockListener -> blockListener.blockEntered(currentIteration, blockInfo));
   }
@@ -245,8 +246,9 @@ private static void notifyBlockListener(IterationInfo currentIteration, Consumer
 
   public static final String CALL_EXIT_BLOCK = "callExitBlock";
 
-  public static void callExitBlock(SpecificationContext context, BlockInfo blockInfo) {
+  public static void callExitBlock(SpecificationContext context, int blockInfoIndex) {
     IterationInfo currentIteration = context.getCurrentIteration();
+    BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex);
     notifyBlockListener(currentIteration, blockListener -> blockListener.blockExited(currentIteration, blockInfo));
     context.setCurrentBlock(null);
   }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index c9af3050d2..4e0736a03e 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -234,7 +234,6 @@ class ASpec extends Specification {
         it.kind == block
         it.texts == blockTexts
       }
-      assert errorInfo.errorContext.toString() == ''
     } else {
       assert errorInfo.errorContext.currentBlock == null
     }
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy
new file mode 100644
index 0000000000..ccc4ba3571
--- /dev/null
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/BlocksAst.groovy
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.spockframework.smoke.ast
+
+import org.spockframework.EmbeddedSpecification
+import org.spockframework.specs.extension.SpockSnapshotter
+import spock.lang.Snapshot
+
+class BlocksAst extends EmbeddedSpecification {
+  @Snapshot(extension = 'groovy')
+  SpockSnapshotter snapshotter
+
+  def "all observable blocks with empty labels"() {
+    given:
+    snapshotter.featureBody()
+
+    when:
+    def result = compiler.transpileFeatureBody('''
+    given: ''
+    expect: ''
+    when: ''
+    then: ''
+    cleanup: ''
+    where: ''
+    combined: ''
+    filter: ''
+    ''')
+
+    then:
+    snapshotter.assertThat(result.source).matchesSnapshot()
+  }
+  def "all observable blocks with labels and blocks"() {
+    given:
+    snapshotter.featureBody()
+
+    when:
+    def result = compiler.transpileFeatureBody('''
+    given: 'given'
+    and: 'and given'
+    expect: 'expect'
+    and: 'and expect'
+    when: 'when'
+    and: 'and when'
+    then: 'then'
+    and: 'and then'
+    then: 'then2'
+    and: 'and then2'
+    cleanup: 'cleanup'
+    and: 'and cleanup'
+    where: 'where'
+    combined: 'combine'
+    filter: 'only one execution'
+    ''')
+
+    then:
+    snapshotter.assertThat(result.source).matchesSnapshot()
+  }
+
+  def "all observable blocks with GString labels"() {
+    given:
+    snapshotter.featureBody()
+
+    when:
+    def result = compiler.transpileFeatureBody('''
+    int idx = 0
+    given: "given ${idx++}"
+    expect: "expect ${idx++}"
+    when: "when ${idx++}"
+    then: "then ${idx++}"
+    ''')
+
+    then:
+    snapshotter.assertThat(result.source).matchesSnapshot()
+  }
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index 4fb8ee7edd..8f16d4d117 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -45,23 +45,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 2
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 3
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
     ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L0
@@ -116,23 +101,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 4
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 5
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
     ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -140,23 +110,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 6
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 7
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_1
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -207,23 +162,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 8
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 9
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_1
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -231,29 +171,14 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 10
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 11
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_2
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L17
     LINENUMBER 8 L17
     ALOAD 1
-    LDC 12
+    LDC 2
     AALOAD
     ALOAD 0
     ACONST_NULL
@@ -266,23 +191,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     LDC Lorg/spockframework/runtime/SpecificationContext;.class
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
-    ALOAD 1
-    LDC 13
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    ALOAD 1
-    LDC 14
-    AALOAD
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty (Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEINTERFACE org/codehaus/groovy/runtime/callsite/CallSite.callConstructor (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (itf)
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
-    CHECKCAST org/spockframework/runtime/model/BlockInfo
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_2
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index a6d0ee105b..32599b20df 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -60,33 +60,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "EXPECT", 
-      0
-    ]
     ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L0
@@ -145,33 +120,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "EXPECT", 
-      0
-    ]
     ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -183,33 +133,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "WHEN", 
-      0
-    ]
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_1
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -269,33 +194,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "WHEN", 
-      0
-    ]
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_1
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -307,33 +207,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "THEN", 
-      0
-    ]
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_2
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L17
@@ -359,33 +234,8 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       "()", 
       0
     ]
-    LDC Lorg/spockframework/runtime/model/BlockInfo;.class
-    LDC Lorg/spockframework/runtime/model/BlockKind;.class
-    INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "THEN", 
-      0
-    ]
-    ICONST_0
-    ANEWARRAY java/lang/Object
-    INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
-    INVOKEDYNAMIC init(Ljava/lang/Class;Ljava/lang/Object;Ljava/util/List;)Ljava/lang/Object; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "<init>", 
-      0
-    ]
-    INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/model/BlockInfo; [
-      // handle kind 0x6 : INVOKESTATIC
-      org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite;
-      // arguments:
-      "()", 
-      0
-    ]
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;Lorg/spockframework/runtime/model/BlockInfo;)V
+    ICONST_2
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index f4da13325a..81d1ef8aa8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -7,9 +7,9 @@ public class apackage.ASpec extends spock.lang.Specification {
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
         java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index 64e22a94fe..e0945eb56a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -7,9 +7,9 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
         java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
index a56812945e..872fb00ade 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
@@ -6,9 +6,9 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object nothing = null
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
index 9762b7dc12..f15995b174 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
@@ -12,9 +12,9 @@ private java.lang.Object $spock_initializeFields() {
 
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 3, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object nothing = null
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
new file mode 100644
index 0000000000..d93d268e55
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
@@ -0,0 +1,40 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+  def "aFeature"() {
+/*--------- tag::snapshot[] ---------*/
+@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
+public void $spock_feature_0_0() {
+    org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
+    org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
+    java.lang.Integer idx = 0
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    "given ${( idx )++}"
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    try {
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"expect \${idx++}\"', 3, 13, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}expect "))
+    }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"expect \${idx++}\"', 3, 13, null, $spock_condition_throwable)}
+    finally {
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+    "when ${( idx )++}"
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
+    try {
+        org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"then \${idx++}\"', 5, 11, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}then "))
+    }
+    catch (java.lang.Throwable $spock_condition_throwable) {
+        org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"then \${idx++}\"', 5, 11, null, $spock_condition_throwable)}
+    finally {
+    }
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
+    this.getSpecificationContext().getMockController().leaveScope()
+}
+/*--------- end::snapshot[] ---------*/
+  }
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
new file mode 100644
index 0000000000..b4ce663bde
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
@@ -0,0 +1,50 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+  def "aFeature"() {
+/*--------- tag::snapshot[] ---------*/
+@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.CLEANUP, texts = ['']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = ['', '']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = [''])], parameterNames = [])
+public void $spock_feature_0_0() {
+    java.lang.Throwable $spock_feature_throwable
+    try {
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
+    }
+    catch (java.lang.Throwable $spock_tmp_throwable) {
+        $spock_feature_throwable = $spock_tmp_throwable
+        throw $spock_tmp_throwable
+    }
+    finally {
+        org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null
+        try {
+            if ( $spock_feature_throwable != null) {
+                $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
+            }
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 4)
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 4)
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            if ( $spock_feature_throwable != null) {
+                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+            } else {
+                throw $spock_tmp_throwable
+            }
+        }
+        finally {
+            if ( $spock_feature_throwable != null) {
+                ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock)
+            }
+        }
+    }
+    this.getSpecificationContext().getMockController().leaveScope()
+}
+/*--------- end::snapshot[] ---------*/
+  }
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
new file mode 100644
index 0000000000..a4997557a9
--- /dev/null
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
@@ -0,0 +1,52 @@
+package aPackage
+import spock.lang.*
+
+class ASpec extends Specification {
+  def "aFeature"() {
+/*--------- tag::snapshot[] ---------*/
+@org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = ['given', 'and given']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.EXPECT, texts = ['expect', 'and expect']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = ['when', 'and when']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['then', 'and then']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = ['then2', 'and then2']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.CLEANUP, texts = ['cleanup', 'and cleanup']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHERE, texts = ['where', 'combine']), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.FILTER, texts = ['only one execution'])], parameterNames = [])
+public void $spock_feature_0_0() {
+    java.lang.Throwable $spock_feature_throwable
+    try {
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 4)
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 4)
+    }
+    catch (java.lang.Throwable $spock_tmp_throwable) {
+        $spock_feature_throwable = $spock_tmp_throwable
+        throw $spock_tmp_throwable
+    }
+    finally {
+        org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null
+        try {
+            if ( $spock_feature_throwable != null) {
+                $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
+            }
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 5)
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 5)
+        }
+        catch (java.lang.Throwable $spock_tmp_throwable) {
+            if ( $spock_feature_throwable != null) {
+                $spock_feature_throwable.addSuppressed($spock_tmp_throwable)
+            } else {
+                throw $spock_tmp_throwable
+            }
+        }
+        finally {
+            if ( $spock_feature_throwable != null) {
+                ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock)
+            }
+        }
+    }
+    this.getSpecificationContext().getMockController().leaveScope()
+}
+/*--------- end::snapshot[] ---------*/
+  }
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index 986dc656ba..99460b34f4 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -13,10 +13,10 @@ public void $spock_feature_0_0() {
     java.lang.Object foobar
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
         foobar = this.foobar()
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
@@ -24,7 +24,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
         finally {
         }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -36,9 +36,9 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
             foobar.size()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 7c20cce30e..1ca7fb969e 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -13,10 +13,10 @@ public void $spock_feature_0_0() {
     def (java.lang.Object foobar, java.lang.Object b) = [null, null]
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
         (foobar, b) = this.foobar()
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
@@ -24,7 +24,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
         finally {
         }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -36,9 +36,9 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
             foobar.size()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.CLEANUP, []))
+            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
index 294105cf5e..84e95ba26c 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
@@ -8,7 +8,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
@@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
index f4529d9ffd..8fbcbc5853 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
@@ -8,7 +8,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
@@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
index 0b05b88f80..18169b2e81 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
index 0b05b88f80..18169b2e81 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
index 0b05b88f80..18169b2e81 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index 2e54448fc6..b87d83b4cf 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,9 +15,9 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.FILTER, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index 0c3335011d..16faae41cb 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object x = [1]
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index fe8a68a227..d925e60e44 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object x = [1]
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
index dc34962863..8668795cda 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object x = '[1]'
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
index 9e5830c739..70dfef39f8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.lang.Object x = 'a1b'
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.EXPECT, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index 0f5d72bec5..aae2ea5cf1 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -9,7 +9,7 @@ public java.lang.Object foobar() {
 
 public void $spock_feature_0_0() {
     java.lang.Object foobar
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().setThrownException(null)
     try {
         foobar = this.foobar()
@@ -19,10 +19,10 @@ public void $spock_feature_0_0() {
     }
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     this.thrownImpl(null, null, java.lang.IllegalStateException)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index ca410aeb71..659981a807 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -9,7 +9,7 @@ public java.lang.Object foobar() {
 
 public void $spock_feature_0_0() {
     def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     this.getSpecificationContext().setThrownException(null)
     try {
         (foobar, b) = this.foobar()
@@ -19,10 +19,10 @@ public void $spock_feature_0_0() {
     }
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     this.thrownImpl(null, null, java.lang.IllegalStateException)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
index 5cbd1b850d..aed9a88605 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -6,17 +6,17 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
     java.util.List list = this.MockImpl('list', java.util.List)
     this.getSpecificationContext().getMockController().enterScope()
     this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.SETUP, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
     list.add(1)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.WHEN, []))
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), new org.spockframework.runtime.model.BlockInfo(org.spockframework.runtime.model.BlockKind.THEN, []))
+    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/

From c6d4b01b0b9fff31b4115072e76a591a38da4a3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 18 Oct 2024 13:23:48 +0200
Subject: [PATCH 19/26] Polishing

---
 .../java/org/spockframework/compiler/SpecAnnotator.java  | 8 +++++---
 .../java/org/spockframework/compiler/SpecRewriter.java   | 9 ++++-----
 .../java/org/spockframework/runtime/model/ErrorInfo.java | 5 +++++
 .../org/spockframework/runtime/model/IErrorContext.java  | 2 ++
 4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
index 25061a9e23..3a7a180112 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecAnnotator.java
@@ -191,8 +191,9 @@ private void addFeatureMetadata(FeatureMethod feature) {
     ann.setMember(FeatureMetadata.BLOCKS, blockAnnElems = new ListExpression());
 
     ListExpression paramNames = new ListExpression();
-    for (Parameter param : feature.getAst().getParameters())
+    for (Parameter param : feature.getAst().getParameters()) {
       paramNames.addExpression(new ConstantExpression(param.getName()));
+    }
     ann.setMember(FeatureMetadata.PARAMETER_NAMES, paramNames);
 
     feature.getAst().addAnnotation(ann);
@@ -203,12 +204,13 @@ private void addBlockMetadata(Block block, BlockKind kind) {
     blockAnn.setMember(BlockMetadata.KIND, new PropertyExpression(
         new ClassExpression(nodeCache.BlockKind), kind.name()));
     ListExpression textExprs = new ListExpression();
-    for (String text : block.getDescriptions())
+    for (String text : block.getDescriptions()) {
       textExprs.addExpression(new ConstantExpression(text));
+    }
     blockAnn.setMember(BlockMetadata.TEXTS, textExprs);
     int index = blockAnnElems.getExpressions().size();
     Assert.that(index == block.getBlockMetaDataIndex(),
-      () -> kind+" block mismatch of index: " + index + ", block.getBlockMetaDataIndex(): " + block.getBlockMetaDataIndex());
+        () -> kind + " block mismatch of index: " + index + ", block.getBlockMetaDataIndex(): " + block.getBlockMetaDataIndex());
     blockAnnElems.addExpression(new AnnotationConstantExpression(blockAnn));
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 7a0c69c138..893c83e41a 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -34,7 +34,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.stream.Collectors;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
@@ -430,9 +429,9 @@ private void addBlockListeners(Block block) {
       || blockType == BlockParseInfo.COMBINED
       || blockType == BlockParseInfo.ANONYMOUS) return;
 
-    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
+    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, blockMetaDataIndex))
     MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallEnterBlock);
-    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, [blockTexts]))
+    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, blockMetaDataIndex))
     MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallExitBlock);
 
     if (blockType == BlockParseInfo.CLEANUP) {
@@ -447,7 +446,7 @@ private void addBlockListeners(Block block) {
       block.getAst().add(new ExpressionStatement(exitBlockCall));
     } else {
       block.getAst().add(0, new ExpressionStatement(enterBlockCall));
-      block.getAst().add( new ExpressionStatement(exitBlockCall));
+      block.getAst().add(new ExpressionStatement(exitBlockCall));
     }
   }
 
@@ -559,7 +558,7 @@ private Statement createMockControllerCall(MethodNode method) {
   public void visitCleanupBlock(CleanupBlock block) {
     for (Block b : method.getBlocks()) {
       // call addBlockListeners() here, as this method will already copy the contents of the blocks,
-      // so we need to transform here as they won't be copied again in visitMethodAgain where we normally add them
+      // so we need to transform the block listeners here as they won't be copied in visitMethodAgain where we normally add them
       addBlockListeners(b);
       if (b == block) break;
       moveVariableDeclarations(b.getAst(), method.getStatements());
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
index 7a5c0f29b9..f9c4bf6f99 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/ErrorInfo.java
@@ -33,6 +33,11 @@ public Throwable getException() {
     return error;
   }
 
+  /**
+   * {@return the error context}
+   *
+   * @since 2.4
+   */
   public IErrorContext getErrorContext() {
     return errorContext;
   }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
index 3ddbf1a0df..7e11bbd882 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -6,6 +6,8 @@
  * Provides context information for an error that occurred during the execution of a specification.
  * <p>
  * Depending on the context in which the error occurred, some of the methods may return {@code null}.
+ *
+ * @since 2.4
  */
 public interface IErrorContext {
   @Nullable

From acc83191ae438fabb225bac1102a552b58ac3f41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 18 Oct 2024 18:08:48 +0200
Subject: [PATCH 20/26] Apply review comments

---
 .../spockframework/compiler/SpecRewriter.java  | 18 +++++++-----------
 .../spockframework/compiler/SpockNames.java    |  5 ++++-
 .../runtime/model/IErrorContext.java           |  2 ++
 3 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index 893c83e41a..bb877578e2 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -49,9 +49,6 @@ public class SpecRewriter extends AbstractSpecVisitor implements IRewriteResourc
   // https://issues.apache.org/jira/browse/GROOVY-10403
   // needed for groovy-4 compatibility and only available since groovy-4
   private static final java.lang.reflect.Method GET_PLAIN_NODE_REFERENCE = ReflectionUtil.getMethodBySignature(ClassNode.class, "getPlainNodeReference", boolean.class);
-  public static final String SPOCK_VALUE = "$spock_value";
-  public static final String SPOCK_FEATURE_THROWABLE = "$spock_feature_throwable";
-  public static final String SPOCK_TMP_THROWABLE = "$spock_tmp_throwable";
 
   private final AstNodeCache nodeCache;
   private final SourceLookup lookup;
@@ -167,7 +164,7 @@ private void createFinalFieldGetter(Field field) {
 
   private void createSharedFieldSetter(Field field) {
     String setterName = "set" + MetaClassHelper.capitalize(field.getName());
-    Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), SPOCK_VALUE) };
+    Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), SpockNames.SPOCK_VALUE) };
     MethodNode setter = spec.getAst().getMethod(setterName, params);
     if (setter != null) {
       errorReporter.error(field.getAst(),
@@ -188,7 +185,7 @@ private void createSharedFieldSetter(Field field) {
                     // use internal name
                     new ConstantExpression(field.getAst().getName())),
                 Token.newSymbol(Types.ASSIGN, -1, -1),
-                new VariableExpression(SPOCK_VALUE))));
+                new VariableExpression(SpockNames.SPOCK_VALUE))));
 
     setter.setSourcePosition(field.getAst());
     spec.getAst().addMethod(setter);
@@ -443,11 +440,10 @@ private void addBlockListeners(Block block) {
         ifThrowableIsNotNull(storeFailedBlock(failedBlock)),
         new ExpressionStatement(enterBlockCall)
       ));
-      block.getAst().add(new ExpressionStatement(exitBlockCall));
     } else {
       block.getAst().add(0, new ExpressionStatement(enterBlockCall));
-      block.getAst().add(new ExpressionStatement(exitBlockCall));
     }
+    block.getAst().add(new ExpressionStatement(exitBlockCall));
   }
 
   private @NotNull Statement storeFailedBlock(VariableExpression failedBlock) {
@@ -462,7 +458,7 @@ private void addBlockListeners(Block block) {
   private IfStatement ifThrowableIsNotNull(Statement statement) {
     return new IfStatement(
       // if ($spock_feature_throwable != null)
-      new BooleanExpression(AstUtil.createVariableIsNotNullExpression(new VariableExpression(SpecRewriter.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
+      new BooleanExpression(AstUtil.createVariableIsNotNullExpression(new VariableExpression(SpockNames.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable))),
       statement,
       EmptyStatement.INSTANCE
     );
@@ -565,7 +561,7 @@ public void visitCleanupBlock(CleanupBlock block) {
     }
 
     VariableExpression featureThrowableVar =
-        new VariableExpression(SPOCK_FEATURE_THROWABLE, nodeCache.Throwable);
+        new VariableExpression(SpockNames.SPOCK_FEATURE_THROWABLE, nodeCache.Throwable);
     method.getStatements().add(createVariableDeclarationStatement(featureThrowableVar));
 
     List<Statement> featureStats = new ArrayList<>();
@@ -618,7 +614,7 @@ private TryCatchStatement createCleanupTryCatch(CleanupBlock block, VariableExpr
   }
 
   private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(VariableExpression assignmentVar) {
-    Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
+    Parameter catchParameter = new Parameter(nodeCache.Throwable, SpockNames.SPOCK_TMP_THROWABLE);
 
     BinaryExpression assignThrowableExpr =
         new BinaryExpression(
@@ -635,7 +631,7 @@ private CatchStatement createThrowableAssignmentAndRethrowCatchStatement(Variabl
   }
 
   private CatchStatement createHandleSuppressedThrowableStatement(VariableExpression featureThrowableVar) {
-    Parameter catchParameter = new Parameter(nodeCache.Throwable, SPOCK_TMP_THROWABLE);
+    Parameter catchParameter = new Parameter(nodeCache.Throwable, SpockNames.SPOCK_TMP_THROWABLE);
 
     BinaryExpression featureThrowableNotNullExpr = AstUtil.createVariableIsNotNullExpression(featureThrowableVar);
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
index 4eadf163ba..5353720184 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpockNames.java
@@ -1,9 +1,12 @@
 package org.spockframework.compiler;
 
 public class SpockNames {
-  public static final String VALUE_RECORDER = "$spock_valueRecorder";
   public static final String ERROR_COLLECTOR = "$spock_errorCollector";
   public static final String FAILED_BLOCK = "$spock_failedBlock";
   public static final String OLD_VALUE = "$spock_oldValue";
   public static final String SPOCK_EX = "$spock_ex";
+  public static final String SPOCK_FEATURE_THROWABLE = "$spock_feature_throwable";
+  public static final String SPOCK_TMP_THROWABLE = "$spock_tmp_throwable";
+  public static final String SPOCK_VALUE = "$spock_value";
+  public static final String VALUE_RECORDER = "$spock_valueRecorder";
 }
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
index 7e11bbd882..8e38d1fd5c 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -1,5 +1,6 @@
 package org.spockframework.runtime.model;
 
+import org.spockframework.util.Beta;
 import org.spockframework.util.Nullable;
 
 /**
@@ -9,6 +10,7 @@
  *
  * @since 2.4
  */
+@Beta
 public interface IErrorContext {
   @Nullable
   SpecInfo getCurrentSpec();

From cf8b43aa895e427ff623289676f2666b3573bca6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Fri, 1 Nov 2024 15:01:27 +0100
Subject: [PATCH 21/26] Add minimal documentation

---
 docs/extensions.adoc | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/docs/extensions.adoc b/docs/extensions.adoc
index cf4eb85155..8e33bc7872 100644
--- a/docs/extensions.adoc
+++ b/docs/extensions.adoc
@@ -1380,3 +1380,25 @@ It is primarily for framework developers who want to provide a default value for
 Or users of a framework that doesn't provide default values for their special types.
 
 If you want to change the default response behavior for `Stub` have a look at <<interaction_based_testing.adoc#ALaCarteMocks,A la Carte Mocks>> and how to use your own `org.spockframework.mock.IDefaultResponse`.
+
+=== Listeners
+
+Extensions can register listeners to receive notifications about the progress of the test run.
+These listeners are intended to be used for reporting, logging, or other monitoring purposes.
+They are not intended to modify the test run in any way.
+You can register the same listener instance on multiple specifications or features.
+Please consult the JavaDoc of the respective listener interfaces for more information.
+
+==== `IRunListener`
+
+The `org.spockframework.runtime.IRunListener` can be registered via `SpecInfo.addListener(IRunListener)` and will receive notifications about the progress of the test run of a single specification.
+
+==== `IBlockListener`
+
+The `org.spockframework.runtime.extension.IBlockListener` can be registered on a feature via, `FeatureInfo.addBlockListener(IBlockListener)` and will receive notifications about the progress of the feature.
+
+It will be called once when entering a block (`blockEntered`) and once when exiting a block (`blockExited`).
+
+When an exception is thrown in a block, the `blockExited` will not be called for that block.
+The failed block will be part of the `ErrorContext` in `ErrorInfo` that is passed to `IRunListener.error(ErrorInfo)`.
+If a `cleanup` block is present the cleanup block listener methods will still be called.

From ac96ff42ce171ff91f8d7d4895cd407a17927214 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 18 Dec 2024 15:02:50 +0100
Subject: [PATCH 22/26] Apply review comments

---
 .../spockframework/compiler/AstNodeCache.java | 12 +++----
 .../org/spockframework/compiler/AstUtil.java  |  8 +----
 .../spockframework/compiler/SpecParser.java   |  2 +-
 .../spockframework/compiler/SpecRewriter.java | 35 ++++++++++---------
 .../spockframework/runtime/SpockRuntime.java  |  8 ++---
 ...ceFeatureBody_can_render_everything.groovy |  8 ++---
 ...n_render_everything__Groovy_4_0_2__.groovy |  8 ++---
 ...thods_and_its_annotation_by_default.groovy |  4 +--
 ...ers_and_their_annotation_by_default.groovy |  4 +--
 ...servable_blocks_with_GString_labels.groovy | 16 ++++-----
 ...observable_blocks_with_empty_labels.groovy | 20 +++++------
 ...vable_blocks_with_labels_and_blocks.groovy | 24 ++++++-------
 ...rite_keeps_correct_method_reference.groovy | 12 +++----
 ...hod_reference_for_multi_assignments.groovy | 12 +++----
 .../DataAstSpec/multi_parameterization.groovy |  4 +--
 .../nested_multi_parameterization.groovy      |  4 +--
 ...ith__separators_can_be_combined-[0].groovy |  4 +--
 ...ith__separators_can_be_combined-[1].groovy |  4 +--
 ...ith__separators_can_be_combined-[2].groovy |  4 +--
 ...filter_block_becomes_its_own_method.groovy |  8 ++---
 ...tionsAsSet_is_transformed_correctly.groovy |  8 ++---
 ...InAnyOrder_is_transformed_correctly.groovy |  8 ++---
 ...onditions_are_transformed_correctly.groovy |  8 ++---
 ...onditions_are_transformed_correctly.groovy |  8 ++---
 ...rite_keeps_correct_method_reference.groovy |  8 ++---
 ...hod_reference_for_multi_assignments.groovy |  8 ++---
 .../MocksAstSpec/simple_interaction.groovy    | 12 +++----
 27 files changed, 127 insertions(+), 134 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
index f071596566..bbe9395a20 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstNodeCache.java
@@ -72,11 +72,11 @@ public class AstNodeCache {
   public final MethodNode SpockRuntime_DespreadList =
       SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.DESPREAD_LIST).get(0);
 
-  public final MethodNode SpockRuntime_CallEnterBlock =
-      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_ENTER_BLOCK).get(0);
+  public final MethodNode SpockRuntime_CallBlockEntered =
+      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_BLOCK_ENTERED).get(0);
 
-  public final MethodNode SpockRuntime_CallExitBlock =
-      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_EXIT_BLOCK).get(0);
+  public final MethodNode SpockRuntime_CallBlockExited =
+      SpockRuntime.getDeclaredMethods(org.spockframework.runtime.SpockRuntime.CALL_BLOCK_EXITED).get(0);
 
   public final MethodNode ValueRecorder_Reset =
       ValueRecorder.getDeclaredMethods(org.spockframework.runtime.ValueRecorder.RESET).get(0);
@@ -114,10 +114,10 @@ public class AstNodeCache {
   public final MethodNode SpecificationContext_GetSharedInstance =
       SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_SHARED_INSTANCE).get(0);
 
-  public final MethodNode SpecificationContext_GetBlockCurrentBlock =
+  public final MethodNode SpecificationContext_GetCurrentBlock =
       SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.GET_CURRENT_BLOCK).get(0);
 
-  public final MethodNode SpecificationContext_SetBlockCurrentBlock =
+  public final MethodNode SpecificationContext_SetCurrentBlock =
       SpecificationContext.getDeclaredMethods(org.spockframework.runtime.SpecificationContext.SET_CURRENT_BLOCK).get(0);
 
   public final MethodNode List_Get =
diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
index 6880b37de8..14554eacfe 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java
@@ -394,15 +394,9 @@ public static ConstantExpression primitiveConstExpression(boolean value) {
 
   public static BinaryExpression createVariableIsNotNullExpression(VariableExpression var) {
     return new BinaryExpression(
-      new VariableExpression(var),
+      var,
       Token.newSymbol(Types.COMPARE_NOT_EQUAL, -1, -1),
       new ConstantExpression(null));
   }
 
-  public static BinaryExpression createVariableIsNullExpression(VariableExpression var) {
-    return new BinaryExpression(
-      new VariableExpression(var),
-      Token.newSymbol(Types.COMPARE_EQUAL, -1, -1),
-      new ConstantExpression(null));
-  }
 }
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
index 6659d24d5b..f26d76efab 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecParser.java
@@ -200,7 +200,7 @@ private void buildBlocks(Method method) throws InvalidSpecCompileException {
     checkIsValidSuccessor(method, BlockParseInfo.METHOD_END,
         method.getAst().getLastLineNumber(), method.getAst().getLastColumnNumber());
 
-    // set the block metaData index for each block this must be equal to the index of the block in the @BlockMetadata annotation
+    // set the block metadata index for each block this must be equal to the index of the block in the @BlockMetadata annotation
     int i = -1;
     for (Block block : method.getBlocks()) {
       if(!block.hasBlockMetadata()) continue;
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index bb877578e2..bc89820a2c 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -31,9 +31,7 @@
 import org.spockframework.util.ReflectionUtil;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
@@ -49,6 +47,14 @@ public class SpecRewriter extends AbstractSpecVisitor implements IRewriteResourc
   // https://issues.apache.org/jira/browse/GROOVY-10403
   // needed for groovy-4 compatibility and only available since groovy-4
   private static final java.lang.reflect.Method GET_PLAIN_NODE_REFERENCE = ReflectionUtil.getMethodBySignature(ClassNode.class, "getPlainNodeReference", boolean.class);
+  private static final Set<BlockParseInfo> BLOCK_LISTENER_SUPPORTED_BLOCKS = Collections.unmodifiableSet(EnumSet.of(
+      BlockParseInfo.SETUP,
+      BlockParseInfo.GIVEN,
+      BlockParseInfo.EXPECT,
+      BlockParseInfo.WHEN,
+      BlockParseInfo.THEN,
+      BlockParseInfo.CLEANUP
+  ));
 
   private final AstNodeCache nodeCache;
   private final SourceLookup lookup;
@@ -421,15 +427,12 @@ public void visitMethodAgain(Method method) {
 
   private void addBlockListeners(Block block) {
     BlockParseInfo blockType = block.getParseInfo();
-    if (blockType == BlockParseInfo.WHERE
-      || blockType == BlockParseInfo.METHOD_END
-      || blockType == BlockParseInfo.COMBINED
-      || blockType == BlockParseInfo.ANONYMOUS) return;
+    if (!BLOCK_LISTENER_SUPPORTED_BLOCKS.contains(blockType)) return;
 
-    // SpockRuntime.enterBlock(getSpecificationContext(), new BlockInfo(blockKind, blockMetaDataIndex))
-    MethodCallExpression enterBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallEnterBlock);
-    // SpockRuntime.exitedBlock(getSpecificationContext(), new BlockInfo(blockKind, blockMetaDataIndex))
-    MethodCallExpression exitBlockCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallExitBlock);
+    // SpockRuntime.callBlockEntered(getSpecificationContext(), blockMetadataIndex)
+    MethodCallExpression blockEnteredCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockEntered);
+    // SpockRuntime.callBlockExited(getSpecificationContext(), blockMetadataIndex)
+    MethodCallExpression blockExitedCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockExited);
 
     if (blockType == BlockParseInfo.CLEANUP) {
       // In case of a cleanup block we need store a reference of the previously `currentBlock` in case that an exception occurred
@@ -438,21 +441,21 @@ private void addBlockListeners(Block block) {
       VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
       block.getAst().addAll(0, asList(
         ifThrowableIsNotNull(storeFailedBlock(failedBlock)),
-        new ExpressionStatement(enterBlockCall)
+        new ExpressionStatement(blockEnteredCall)
       ));
     } else {
-      block.getAst().add(0, new ExpressionStatement(enterBlockCall));
+      block.getAst().add(0, new ExpressionStatement(blockEnteredCall));
     }
-    block.getAst().add(new ExpressionStatement(exitBlockCall));
+    block.getAst().add(new ExpressionStatement(blockExitedCall));
   }
 
   private @NotNull Statement storeFailedBlock(VariableExpression failedBlock) {
-    MethodCallExpression getCurrentBlock = createDirectMethodCall(getSpecificationContext(), nodeCache.SpecificationContext_GetBlockCurrentBlock, ArgumentListExpression.EMPTY_ARGUMENTS);
+    MethodCallExpression getCurrentBlock = createDirectMethodCall(getSpecificationContext(), nodeCache.SpecificationContext_GetCurrentBlock, ArgumentListExpression.EMPTY_ARGUMENTS);
     return new ExpressionStatement(new BinaryExpression(failedBlock, Token.newSymbol(Types.ASSIGN, -1, -1), getCurrentBlock));
   }
 
   private @NotNull Statement restoreFailedBlock(VariableExpression failedBlock) {
-    return new ExpressionStatement(createDirectMethodCall(new CastExpression(nodeCache.SpecificationContext, getSpecificationContext()), nodeCache.SpecificationContext_SetBlockCurrentBlock, new ArgumentListExpression(failedBlock)));
+    return new ExpressionStatement(createDirectMethodCall(new CastExpression(nodeCache.SpecificationContext, getSpecificationContext()), nodeCache.SpecificationContext_SetCurrentBlock, new ArgumentListExpression(failedBlock)));
   }
 
   private IfStatement ifThrowableIsNotNull(Statement statement) {
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
index c7dabcff59..04a5163833 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpockRuntime.java
@@ -229,9 +229,9 @@ public static Object[] despreadList(Object[] args, Object[] spreads, int[] posit
     return GroovyRuntimeUtil.despreadList(args, spreads, positions);
   }
 
-  public static final String CALL_ENTER_BLOCK = "callEnterBlock";
+  public static final String CALL_BLOCK_ENTERED = "callBlockEntered";
 
-  public static void callEnterBlock(SpecificationContext context, int blockInfoIndex) {
+  public static void callBlockEntered(SpecificationContext context, int blockInfoIndex) {
     IterationInfo currentIteration = context.getCurrentIteration();
     BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex);
     context.setCurrentBlock(blockInfo);
@@ -244,9 +244,9 @@ private static void notifyBlockListener(IterationInfo currentIteration, Consumer
     blockListeners.forEach(consumer);
   }
 
-  public static final String CALL_EXIT_BLOCK = "callExitBlock";
+  public static final String CALL_BLOCK_EXITED = "callBlockExited";
 
-  public static void callExitBlock(SpecificationContext context, int blockInfoIndex) {
+  public static void callBlockExited(SpecificationContext context, int blockInfoIndex) {
     IterationInfo currentIteration = context.getCurrentIteration();
     BlockInfo blockInfo = context.getCurrentFeature().getBlocks().get(blockInfoIndex);
     notifyBlockListener(currentIteration, blockListener -> blockListener.blockExited(currentIteration, blockInfo));
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index 81d1ef8aa8..6ff76ce44a 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -1,16 +1,14 @@
 package apackage
 
-import spock.lang.*
-
 @org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
 public class apackage.ASpec extends spock.lang.Specification {
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
         java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
-}
\ No newline at end of file
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index e0945eb56a..685ded39c2 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -1,16 +1,14 @@
 package apackage
 
-import spock.lang.*
-
 @org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
 public class apackage.ASpec extends spock.lang.Specification implements groovy.lang.GroovyObject {
 
     @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
     public void $spock_feature_0_0() {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
         java.lang.Object nothing = null
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
-}
\ No newline at end of file
+}
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
index 872fb00ade..a690ad70c0 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_renders_only_methods_and_its_annotation_by_default.groovy
@@ -6,9 +6,9 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object nothing = null
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
index f15995b174..31d9795be5 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceSpecBody_renders_only_methods__fields__properties__object_initializers_and_their_annotation_by_default.groovy
@@ -12,9 +12,9 @@ private java.lang.Object $spock_initializeFields() {
 
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 3, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object nothing = null
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
index d93d268e55..00650532dc 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_GString_labels.groovy
@@ -9,10 +9,10 @@ public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
     java.lang.Integer idx = 0
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     "given ${( idx )++}"
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"expect \${idx++}\"', 3, 13, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}expect "))
     }
@@ -20,11 +20,11 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"expect \${idx++}\"', 3, 13, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
     "when ${( idx )++}"
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), '\"then \${idx++}\"', 5, 11, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), "${$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), ( idx )++)}then "))
     }
@@ -32,7 +32,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, '\"then \${idx++}\"', 5, 11, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
index b4ce663bde..bdf57f9c28 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_empty_labels.groovy
@@ -8,14 +8,14 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -27,8 +27,8 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 4)
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 4)
+            org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 4)
+            org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 4)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
index a4997557a9..69434818ef 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/BlocksAst/all_observable_blocks_with_labels_and_blocks.groovy
@@ -8,16 +8,16 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 3)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 3)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 4)
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 4)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 3)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 4)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 4)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -29,8 +29,8 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 5)
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 5)
+            org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 5)
+            org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 5)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
index 99460b34f4..12acd40e97 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference.groovy
@@ -13,10 +13,10 @@ public void $spock_feature_0_0() {
     java.lang.Object foobar
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
         foobar = this.foobar()
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
@@ -24,7 +24,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
         finally {
         }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -36,9 +36,9 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+            org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
             foobar.size()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+            org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 1ca7fb969e..9b4a45cfc3 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -13,10 +13,10 @@ public void $spock_feature_0_0() {
     def (java.lang.Object foobar, java.lang.Object b) = [null, null]
     java.lang.Throwable $spock_feature_throwable
     try {
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
         (foobar, b) = this.foobar()
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-        org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+        org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
         try {
             org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3)
         }
@@ -24,7 +24,7 @@ public void $spock_feature_0_0() {
             org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)}
         finally {
         }
-        org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+        org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     }
     catch (java.lang.Throwable $spock_tmp_throwable) {
         $spock_feature_throwable = $spock_tmp_throwable
@@ -36,9 +36,9 @@ public void $spock_feature_0_0() {
             if ( $spock_feature_throwable != null) {
                 $spock_failedBlock = this.getSpecificationContext().getCurrentBlock()
             }
-            org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+            org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
             foobar.size()
-            org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+            org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
         }
         catch (java.lang.Throwable $spock_tmp_throwable) {
             if ( $spock_feature_throwable != null) {
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
index 84e95ba26c..201d1c5fd8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/multi_parameterization.groovy
@@ -8,7 +8,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
@@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
index 8fbcbc5853..215215d6ce 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataAstSpec/nested_multi_parameterization.groovy
@@ -8,7 +8,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'a == b', 1, 83, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), a) == $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), b)))
     }
@@ -16,7 +16,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'a == b', 1, 83, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
index 18169b2e81..3ad733861b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[0].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
index 18169b2e81..3ad733861b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[1].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
index 18169b2e81..3ad733861b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/data_tables_with__separators_can_be_combined-[2].groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang.Object c) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,7 +15,7 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b, java.lang
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index b87d83b4cf..58585be1ef 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -7,7 +7,7 @@ class ASpec extends Specification {
 public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'true', 2, 7, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), true))
     }
@@ -15,9 +15,9 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'true', 2, 7, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
index 16faae41cb..e11a1a15e8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object x = [1]
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
index d925e60e44..6e11bd81a6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object x = [1]
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5)
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
index 8668795cda..00647881b6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_find_conditions_are_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object x = '[1]'
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ /\\d/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsAsSet($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), '\\d'))))
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ /\\d/', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
index 70dfef39f8..4ce13e20d9 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/regex_match_conditions_are_transformed_correctly.groovy
@@ -8,10 +8,10 @@ class ASpec extends Specification {
 public void $spock_feature_0_0() {
     org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE
     org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder()
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.lang.Object x = 'a1b'
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     try {
         org.spockframework.runtime.SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ /a\\db/', 4, 9, null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), org.spockframework.runtime.SpockRuntime.matchCollectionsInAnyOrder($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), 'a\\db'))))
     }
@@ -19,7 +19,7 @@ public void $spock_feature_0_0() {
         org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ /a\\db/', 4, 9, null, $spock_condition_throwable)}
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
index aae2ea5cf1..2a5315379b 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference.groovy
@@ -9,7 +9,7 @@ public java.lang.Object foobar() {
 
 public void $spock_feature_0_0() {
     java.lang.Object foobar
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     this.getSpecificationContext().setThrownException(null)
     try {
         foobar = this.foobar()
@@ -19,10 +19,10 @@ public void $spock_feature_0_0() {
     }
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     this.thrownImpl(null, null, java.lang.IllegalStateException)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
index 659981a807..9a592abdd8 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments.groovy
@@ -9,7 +9,7 @@ public java.lang.Object foobar() {
 
 public void $spock_feature_0_0() {
     def (java.lang.Object foobar, java.lang.Object b) = [null, null]
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     this.getSpecificationContext().setThrownException(null)
     try {
         (foobar, b) = this.foobar()
@@ -19,10 +19,10 @@ public void $spock_feature_0_0() {
     }
     finally {
     }
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     this.thrownImpl(null, null, java.lang.IllegalStateException)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
index aed9a88605..4a79964dc0 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/mock/MocksAstSpec/simple_interaction.groovy
@@ -6,17 +6,17 @@ class ASpec extends Specification {
 /*--------- tag::snapshot[] ---------*/
 @org.spockframework.runtime.model.FeatureMetadata(name = 'a feature', ordinal = 0, line = 1, blocks = [@org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.SETUP, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.WHEN, texts = []), @org.spockframework.runtime.model.BlockMetadata(kind = org.spockframework.runtime.model.BlockKind.THEN, texts = [])], parameterNames = [])
 public void $spock_feature_0_0() {
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 0)
     java.util.List list = this.MockImpl('list', java.util.List)
     this.getSpecificationContext().getMockController().enterScope()
     this.getSpecificationContext().getMockController().addInteraction(new org.spockframework.mock.runtime.InteractionBuilder(8, 5, '1 * list.add(1)').setFixedCount(1).addEqualTarget(list).addEqualMethodName('add').setArgListKind(true, false).addEqualArg(1).build())
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 1)
     list.add(1)
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 1)
-    org.spockframework.runtime.SpockRuntime.callEnterBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 1)
+    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
-    org.spockframework.runtime.SpockRuntime.callExitBlock(this.getSpecificationContext(), 2)
+    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 /*--------- end::snapshot[] ---------*/

From 379aefcd889d00caebfd6336aa9d715b4b37fb81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 18 Dec 2024 15:13:00 +0100
Subject: [PATCH 23/26] Apply review comments 2

---
 .../org/spockframework/compiler/SpecRewriter.java   | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index bc89820a2c..f34a97e29a 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -49,11 +49,12 @@ public class SpecRewriter extends AbstractSpecVisitor implements IRewriteResourc
   private static final java.lang.reflect.Method GET_PLAIN_NODE_REFERENCE = ReflectionUtil.getMethodBySignature(ClassNode.class, "getPlainNodeReference", boolean.class);
   private static final Set<BlockParseInfo> BLOCK_LISTENER_SUPPORTED_BLOCKS = Collections.unmodifiableSet(EnumSet.of(
       BlockParseInfo.SETUP,
-      BlockParseInfo.GIVEN,
+      BlockParseInfo.GIVEN, // is aliased to SETUP
       BlockParseInfo.EXPECT,
       BlockParseInfo.WHEN,
       BlockParseInfo.THEN,
-      BlockParseInfo.CLEANUP
+      BlockParseInfo.CLEANUP,
+      BlockParseInfo.AND // is aliased to the previous block
   ));
 
   private final AstNodeCache nodeCache;
@@ -434,17 +435,13 @@ private void addBlockListeners(Block block) {
     // SpockRuntime.callBlockExited(getSpecificationContext(), blockMetadataIndex)
     MethodCallExpression blockExitedCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockExited);
 
+    block.getAst().add(0, new ExpressionStatement(blockEnteredCall));
     if (blockType == BlockParseInfo.CLEANUP) {
       // In case of a cleanup block we need store a reference of the previously `currentBlock` in case that an exception occurred
       // and restore it at the end of the cleanup block, so that the correct `BlockInfo` is available for the `IErrorContext`.
       // The restoration happens in the `finally` statement created by `createCleanupTryCatch`.
       VariableExpression failedBlock = new VariableExpression(SpockNames.FAILED_BLOCK, nodeCache.BlockInfo);
-      block.getAst().addAll(0, asList(
-        ifThrowableIsNotNull(storeFailedBlock(failedBlock)),
-        new ExpressionStatement(blockEnteredCall)
-      ));
-    } else {
-      block.getAst().add(0, new ExpressionStatement(blockEnteredCall));
+      block.getAst().add(0, ifThrowableIsNotNull(storeFailedBlock(failedBlock)));
     }
     block.getAst().add(new ExpressionStatement(blockExitedCall));
   }

From a6bce08509406f39c872abd6b0b4b331513b3337 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 18 Dec 2024 15:24:25 +0100
Subject: [PATCH 24/26] Apply review comments 3

---
 .../spockframework/runtime/ErrorContext.java  | 22 +++++++++++++++----
 .../spockframework/runtime/IRunListener.java  |  1 -
 .../runtime/SpecificationContext.java         |  8 ++++---
 .../runtime/extension/IBlockListener.java     | 14 ++++++++++++
 .../runtime/model/BlockInfo.java              |  6 +++++
 .../runtime/model/IErrorContext.java          | 22 +++++++++++++++----
 .../runtime/BlockListenerSpec.groovy          | 14 ++++++++++++
 .../runtime/RunListenerSpec.groovy            |  6 ++---
 .../smoke/ast/mock/MocksAstSpec.groovy        | 14 ++++++++++++
 .../parameterization/DataProviders.groovy     |  4 ++--
 10 files changed, 94 insertions(+), 17 deletions(-)

diff --git a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
index 8beeff184e..3ddf52e76e 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/ErrorContext.java
@@ -1,3 +1,17 @@
+/*
+ *  Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
 package org.spockframework.runtime;
 
 import org.spockframework.runtime.model.*;
@@ -26,22 +40,22 @@ static ErrorContext from(SpecificationContext context) {
   }
 
   @Override
-  public SpecInfo getCurrentSpec() {
+  public SpecInfo getSpec() {
     return spec;
   }
 
   @Override
-  public FeatureInfo getCurrentFeature() {
+  public FeatureInfo getFeature() {
     return feature;
   }
 
   @Override
-  public IterationInfo getCurrentIteration() {
+  public IterationInfo getIteration() {
     return iteration;
   }
 
   @Override
-  public BlockInfo getCurrentBlock() {
+  public BlockInfo getBlock() {
     return block;
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java b/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
index 6c27252faa..34a9562c78 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/IRunListener.java
@@ -21,7 +21,6 @@
  * Listens to a spec run. Currently, only extensions can register listeners.
  * They do so by invoking <tt>SpecInfo.addListener()<tt>. See
  * {@link StepwiseExtension} for an example of how to use a listener.
- * <p>
  *
  * @see org.spockframework.runtime.extension.IBlockListener
  * @author Peter Niederwieser
diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
index b17b607150..a8249ae00c 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/SpecificationContext.java
@@ -52,7 +52,7 @@ FeatureInfo getCurrentFeatureOrNull() {
     return currentFeature;
   }
 
-  public void setCurrentFeature(FeatureInfo currentFeature) {
+  public void setCurrentFeature(@Nullable FeatureInfo currentFeature) {
     this.currentFeature = currentFeature;
   }
 
@@ -69,16 +69,18 @@ IterationInfo getCurrentIterationOrNull() {
     return currentIteration;
   }
 
-  public void setCurrentIteration(IterationInfo currentIteration) {
+  public void setCurrentIteration(@Nullable IterationInfo currentIteration) {
     this.currentIteration = currentIteration;
   }
 
   public static final String SET_CURRENT_BLOCK = "setCurrentBlock";
-  public void setCurrentBlock(BlockInfo blockInfo) {
+  public void setCurrentBlock(@Nullable BlockInfo blockInfo) {
     this.currentBlock = blockInfo;
   }
 
   public static final String GET_CURRENT_BLOCK = "getCurrentBlock";
+
+  @Nullable
   @Override
   public BlockInfo getCurrentBlock() {
     return currentBlock;
diff --git a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
index 32bd914922..df06c4c363 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/extension/IBlockListener.java
@@ -1,3 +1,17 @@
+/*
+ *  Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
 package org.spockframework.runtime.extension;
 
 import org.spockframework.runtime.model.BlockInfo;
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
index 3cac692cbf..4768b46604 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/BlockInfo.java
@@ -29,6 +29,12 @@ public class BlockInfo {
   private BlockKind kind;
   private List<String> texts;
 
+  /**
+   * Only for backwards compatibility.
+   * <p>
+   * @deprecated Use {@link #BlockInfo(BlockKind, List)} instead.
+   */
+  @Deprecated
   public BlockInfo() {
   }
 
diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
index 8e38d1fd5c..a2eb30c24b 100644
--- a/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
+++ b/spock-core/src/main/java/org/spockframework/runtime/model/IErrorContext.java
@@ -1,3 +1,17 @@
+/*
+ *  Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
 package org.spockframework.runtime.model;
 
 import org.spockframework.util.Beta;
@@ -13,14 +27,14 @@
 @Beta
 public interface IErrorContext {
   @Nullable
-  SpecInfo getCurrentSpec();
+  SpecInfo getSpec();
 
   @Nullable
-  FeatureInfo getCurrentFeature();
+  FeatureInfo getFeature();
 
   @Nullable
-  IterationInfo getCurrentIteration();
+  IterationInfo getIteration();
 
   @Nullable
-  BlockInfo getCurrentBlock();
+  BlockInfo getBlock();
 }
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
index d7a516d8c1..59326f9a02 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/BlockListenerSpec.groovy
@@ -1,3 +1,17 @@
+/*
+ *  Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
 package org.spockframework.runtime
 
 import org.spockframework.runtime.extension.IBlockListener
diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
index 4e0736a03e..e061e8e2aa 100644
--- a/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/runtime/RunListenerSpec.groovy
@@ -157,7 +157,7 @@ class ASpec extends Specification {
     1 * runListener.beforeIteration(_)
     then:
     1 * runListener.error(_) >> { ErrorInfo errorInfo ->
-      with(errorInfo.errorContext.currentBlock) {
+      with(errorInfo.errorContext.block) {
         it.kind == BlockKind.EXPECT
         it.texts == ["failing expect"]
       }
@@ -230,12 +230,12 @@ class ASpec extends Specification {
     1 * runListener.error(_) >> { ErrorInfo it -> errorInfo = it }
 
     if (block != null) {
-      with(errorInfo.errorContext.currentBlock) {
+      with(errorInfo.errorContext.block) {
         it.kind == block
         it.texts == blockTexts
       }
     } else {
-      assert errorInfo.errorContext.currentBlock == null
+      assert errorInfo.errorContext.block == null
     }
 
     cleanup:
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
index 0b01dc7216..001677a268 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/mock/MocksAstSpec.groovy
@@ -1,3 +1,17 @@
+/*
+ *  Copyright 2024 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
 package org.spockframework.smoke.ast.mock
 
 import org.spockframework.EmbeddedSpecification
diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
index 4ab7dba1db..e27abaaf18 100644
--- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
+++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataProviders.groovy
@@ -104,7 +104,7 @@ where: x << []
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
-  def "data provider with asserting closure produces error rethrower variable in data provider method"(@Snapshot SpockSnapshotter snapshotter) {
+  def "data provider with asserting closure produces error rethrower variable in data provider method"(@Snapshot(extension = 'groovy') SpockSnapshotter snapshotter) {
     given:
     snapshotter.specBody()
 
@@ -133,7 +133,7 @@ where: x << []
   }
 
   @Issue("https://github.com/spockframework/spock/issues/1287")
-  def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot SpockSnapshotter snapshotter) {
+  def "data variable with asserting closure produces error rethrower variable in data processor method"(@Snapshot(extension = 'groovy') SpockSnapshotter snapshotter) {
     given:
     snapshotter.specBody()
 

From a04dfae899dae61d64ef14f65d03cfffb893f8c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Wed, 18 Dec 2024 15:27:56 +0100
Subject: [PATCH 25/26] Update remaining snapshots

---
 ...es_are_used_in_AST_transformation_Groovy____3.txt | 12 ++++++------
 ...es_are_used_in_AST_transformation_Groovy____4.txt | 12 ++++++------
 ...tToSourceFeatureBody_can_render_everything.groovy |  4 +++-
 ...Body_can_render_everything__Groovy_4_0_2__.groovy |  4 +++-
 .../filter_block_becomes_its_own_method.groovy       |  2 --
 ...ethrower_variable_in_data_provider_method.groovy} |  0
 ...thrower_variable_in_data_processor_method.groovy} |  0
 7 files changed, 18 insertions(+), 16 deletions(-)
 rename spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/{data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt => data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy} (100%)
 rename spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/{data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt => data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy} (100%)

diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
index 8f16d4d117..50a2e480b4 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____3.txt
@@ -46,7 +46,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_0
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L0
@@ -102,7 +102,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_0
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -111,7 +111,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_1
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -163,7 +163,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_1
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -172,7 +172,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_2
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L17
@@ -192,7 +192,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
     CHECKCAST org/spockframework/runtime/SpecificationContext
     ICONST_2
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
index 32599b20df..0887d0dff6 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation_Groovy____4.txt
@@ -61,7 +61,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_0
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L0
@@ -121,7 +121,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_0
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -134,7 +134,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_1
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -195,7 +195,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_1
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
@@ -208,7 +208,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_2
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callEnterBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
    L17
@@ -235,7 +235,7 @@ public class apackage/TestSpec extends spock/lang/Specification implements groov
       0
     ]
     ICONST_2
-    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callExitBlock (Lorg/spockframework/runtime/SpecificationContext;I)V
+    INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lorg/spockframework/runtime/SpecificationContext;I)V
     ACONST_NULL
     POP
     ALOAD 0
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
index 6ff76ce44a..ce864b0430 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything.groovy
@@ -1,5 +1,7 @@
 package apackage
 
+import spock.lang.*
+
 @org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
 public class apackage.ASpec extends spock.lang.Specification {
 
@@ -11,4 +13,4 @@ public class apackage.ASpec extends spock.lang.Specification {
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
-}
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
index 685ded39c2..11ddc426bf 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/astToSourceFeatureBody_can_render_everything__Groovy_4_0_2__.groovy
@@ -1,5 +1,7 @@
 package apackage
 
+import spock.lang.*
+
 @org.spockframework.runtime.model.SpecMetadata(filename = 'script.groovy', line = 1)
 public class apackage.ASpec extends spock.lang.Specification implements groovy.lang.GroovyObject {
 
@@ -11,4 +13,4 @@ public class apackage.ASpec extends spock.lang.Specification implements groovy.l
         this.getSpecificationContext().getMockController().leaveScope()
     }
 
-}
+}
\ No newline at end of file
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
index 58585be1ef..eb157c19b4 100644
--- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
+++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/DataTablesAstSpec/filter_block_becomes_its_own_method.groovy
@@ -16,8 +16,6 @@ public void $spock_feature_0_0(java.lang.Object a, java.lang.Object b) {
     finally {
     }
     org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 0)
-    org.spockframework.runtime.SpockRuntime.callBlockEntered(this.getSpecificationContext(), 2)
-    org.spockframework.runtime.SpockRuntime.callBlockExited(this.getSpecificationContext(), 2)
     this.getSpecificationContext().getMockController().leaveScope()
 }
 
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
similarity index 100%
rename from spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.txt
rename to spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_provider_with_asserting_closure_produces_error_rethrower_variable_in_data_provider_method.groovy
diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy
similarity index 100%
rename from spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.txt
rename to spock-specs/src/test/resources/snapshots/org/spockframework/smoke/parameterization/DataProviders/data_variable_with_asserting_closure_produces_error_rethrower_variable_in_data_processor_method.groovy

From 19e2dc284da5c8de6934ee873f7a34ef9fcc2a77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonard=20Br=C3=BCnings?= <leonard.bruenings@gradle.com>
Date: Thu, 26 Dec 2024 16:58:43 +0100
Subject: [PATCH 26/26] Implement review feedback

---
 docs/extensions.adoc                          |  1 +
 docs/release_notes.adoc                       |  1 +
 .../spockframework/compiler/SpecRewriter.java | 11 +---
 .../compiler/model/BlockParseInfo.java        | 63 +++++++++++++++++++
 4 files changed, 66 insertions(+), 10 deletions(-)

diff --git a/docs/extensions.adoc b/docs/extensions.adoc
index 8e33bc7872..781b3ca5f8 100644
--- a/docs/extensions.adoc
+++ b/docs/extensions.adoc
@@ -1393,6 +1393,7 @@ Please consult the JavaDoc of the respective listener interfaces for more inform
 
 The `org.spockframework.runtime.IRunListener` can be registered via `SpecInfo.addListener(IRunListener)` and will receive notifications about the progress of the test run of a single specification.
 
+[#block-listener]
 ==== `IBlockListener`
 
 The `org.spockframework.runtime.extension.IBlockListener` can be registered on a feature via, `FeatureInfo.addBlockListener(IBlockListener)` and will receive notifications about the progress of the feature.
diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc
index 7f78d7aa30..26640cd989 100644
--- a/docs/release_notes.adoc
+++ b/docs/release_notes.adoc
@@ -9,6 +9,7 @@ include::include.adoc[]
 
 * Add support for combining two or more data providers using cartesian product spockIssue:1062[]
 * Add support for a `filter` block after a `where` block to filter out unwanted iterations spockPull:1927[]
+* Add <<extensions.adoc#block-listener,`IBlockListener`>> extension point to listen to block execution events within feature methods spockPull:1575[]
 
 === Misc
 
diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
index f34a97e29a..254d1d04ae 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java
@@ -47,15 +47,6 @@ public class SpecRewriter extends AbstractSpecVisitor implements IRewriteResourc
   // https://issues.apache.org/jira/browse/GROOVY-10403
   // needed for groovy-4 compatibility and only available since groovy-4
   private static final java.lang.reflect.Method GET_PLAIN_NODE_REFERENCE = ReflectionUtil.getMethodBySignature(ClassNode.class, "getPlainNodeReference", boolean.class);
-  private static final Set<BlockParseInfo> BLOCK_LISTENER_SUPPORTED_BLOCKS = Collections.unmodifiableSet(EnumSet.of(
-      BlockParseInfo.SETUP,
-      BlockParseInfo.GIVEN, // is aliased to SETUP
-      BlockParseInfo.EXPECT,
-      BlockParseInfo.WHEN,
-      BlockParseInfo.THEN,
-      BlockParseInfo.CLEANUP,
-      BlockParseInfo.AND // is aliased to the previous block
-  ));
 
   private final AstNodeCache nodeCache;
   private final SourceLookup lookup;
@@ -428,7 +419,7 @@ public void visitMethodAgain(Method method) {
 
   private void addBlockListeners(Block block) {
     BlockParseInfo blockType = block.getParseInfo();
-    if (!BLOCK_LISTENER_SUPPORTED_BLOCKS.contains(blockType)) return;
+    if (!blockType.isSupportingBlockListeners()) return;
 
     // SpockRuntime.callBlockEntered(getSpecificationContext(), blockMetadataIndex)
     MethodCallExpression blockEnteredCall = createBlockListenerCall(block, blockType, nodeCache.SpockRuntime_CallBlockEntered);
diff --git a/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java b/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java
index ebf8fc0288..d57ff267b6 100644
--- a/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java
+++ b/spock-core/src/main/java/org/spockframework/compiler/model/BlockParseInfo.java
@@ -18,6 +18,8 @@
 
 import java.util.*;
 
+import org.spockframework.util.InternalSpockError;
+
 /**
  *
  * @author Peter Niederwieser
@@ -32,6 +34,10 @@ public EnumSet<BlockParseInfo> getSuccessors(Method method) {
     public Block addNewBlock(Method method) {
       return method.getLastBlock();
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      throw new InternalSpockError("AND block should have been replaced by a more specific block");
+    }
   },
 
   ANONYMOUS {
@@ -43,6 +49,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(SETUP, GIVEN, EXPECT, WHEN, CLEANUP, WHERE, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return false;
+    }
   },
 
   SETUP {
@@ -54,6 +64,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, EXPECT, WHEN, CLEANUP, WHERE, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return true;
+    }
   },
 
   GIVEN {
@@ -65,6 +79,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return SETUP.getSuccessors(method);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      throw new InternalSpockError("GIVEN block should have been replaced by a SETUP block");
+    }
   },
 
   EXPECT {
@@ -76,6 +94,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, WHEN, CLEANUP, WHERE, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return true;
+    }
   },
 
   WHEN {
@@ -87,6 +109,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, THEN);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return true;
+    }
   },
 
   THEN {
@@ -98,6 +124,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, EXPECT, WHEN, THEN, CLEANUP, WHERE, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return true;
+    }
   },
 
   CLEANUP {
@@ -109,6 +139,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, WHERE, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return true;
+    }
   },
 
   WHERE {
@@ -120,6 +154,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, COMBINED, FILTER, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return false;
+    }
   },
 
   COMBINED {
@@ -131,6 +169,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return WHERE.getSuccessors(method);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return false;
+    }
   },
 
   FILTER {
@@ -142,6 +184,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       return EnumSet.of(AND, METHOD_END);
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return false;
+    }
   },
 
   METHOD_END {
@@ -153,6 +199,10 @@ public Block addNewBlock(Method method) {
     public EnumSet<BlockParseInfo> getSuccessors(Method method) {
       throw new UnsupportedOperationException("getSuccessors");
     }
+    @Override
+    public boolean isSupportingBlockListeners() {
+      return false;
+    }
     public String toString() {
       return "end-of-method";
     }
@@ -162,7 +212,20 @@ public String toString() {
     return super.toString().toLowerCase(Locale.ROOT);
   }
 
+  /**
+   * Adds a new block of this type to the given method.
+   * <p>
+   * Allows for a block to substitute another block type.
+   */
   public abstract Block addNewBlock(Method method);
 
+  /**
+   * Returns the block types that can follow this block type in the given method.
+   */
   public abstract EnumSet<BlockParseInfo> getSuccessors(Method method);
+
+  /**
+   * Indicates whether this block type supports block listeners.
+   */
+  public abstract boolean isSupportingBlockListeners();
 }
