code: @Override
    public void visitCleanupBlock(CleanupBlock block) {
      for (Block b : method.getBlocks()) {
        if (b == block) break;
        moveVariableDeclarations(b.getAst(), method.getStatements());
      }
  
      VariableExpression featureThrowableVar =
          new VariableExpression("$spock_feature_throwable", nodeCache.Throwable);
      method.getStatements().add(createVariableDeclarationStatement(featureThrowableVar));
  
      List<Statement> featureStats = new ArrayList<>();
      for (Block b : method.getBlocks()) {
        if (b == block) break;
        featureStats.addAll(b.getAst());
      }
  
      CatchStatement featureCatchStat = createThrowableAssignmentAndRethrowCatchStatement(featureThrowableVar);
  
      List<Statement> cleanupStats = Collections.<Statement>singletonList(
          createCleanupTryCatch(block, featureThrowableVar));
  
      TryCatchStatement tryFinally =
          new TryCatchStatement(
              new BlockStatement(featureStats, new VariableScope()),
              new BlockStatement(cleanupStats, new VariableScope()));
      tryFinally.addCatch(featureCatchStat);
  
      method.getStatements().add(tryFinally);
  
      // a cleanup-block may only be followed by a where-block, whose
      // statements are copied to newly generated methods rather than
      // the original method
      movedStatsBackToMethod = true;
    }
docs: /*
  This emulates Java 7 try-with-resources exception handling
  
  Throwable $spock_feature_throwable = null;
  
  try {
  feature statements (setup, when/then, expect, etc. blocks)
  } catch (Throwable $spock_tmp_throwable) {
  $spock_feature_throwable = $spock_tmp_throwable;
  throw $spock_tmp_throwable;
  } finally {
  try {
  feature cleanup statements (cleanup block)
  } catch (Throwable $spock_tmp_throwable) {
  if($spock_feature_throwable != null) {
  $spock_feature_throwable.addSuppressed($spock_tmp_throwable);
  } else {
  throw $spock_tmp_throwable;
  }
  }
  }