You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2021/08/14 10:14:52 UTC

[groovy] branch danielsun/rewrite-gsrc-to-jsrc updated (dfa01cc -> 701bd3b)

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a change to branch danielsun/rewrite-gsrc-to-jsrc
in repository https://gitbox.apache.org/repos/asf/groovy.git.


 discard dfa01cc  Rewrite Groovy source code in core to Java
     new 701bd3b  Rewrite Groovy source code in core to Java

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (dfa01cc)
            \
             N -- N -- N   refs/heads/danielsun/rewrite-gsrc-to-jsrc (701bd3b)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../ReturnStatementToIterationConverter.groovy     | 147 ------------------
 .../ReturnStatementToIterationConverter.java       | 164 +++++++++++++++++++++
 .../tailrec/TailRecursiveASTTransformation.groovy  |   2 +-
 ...lacedListener.java => UsedVariableTracker.java} |  19 ++-
 .../transform/tailrec/VariableAccessReplacer.java  |   9 ++
 5 files changed, 188 insertions(+), 153 deletions(-)
 delete mode 100644 src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.groovy
 create mode 100644 src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.java
 copy src/main/groovy/org/codehaus/groovy/transform/tailrec/{VariableReplacedListener.java => UsedVariableTracker.java} (63%)

[groovy] 01/01: Rewrite Groovy source code in core to Java

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch danielsun/rewrite-gsrc-to-jsrc
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 701bd3b3a50398f7a27b7cb2c5221e20ed290a5e
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sat Aug 14 14:00:57 2021 +0800

    Rewrite Groovy source code in core to Java
---
 .../groovy/transform/tailrec/AstHelper.groovy      |  75 -------
 .../groovy/transform/tailrec/AstHelper.java        |  78 ++++++++
 .../transform/tailrec/CollectRecursiveCalls.groovy |  60 ------
 .../transform/tailrec/CollectRecursiveCalls.java   |  64 ++++++
 .../transform/tailrec/GotoRecurHereException.java  |  26 +++
 .../transform/tailrec/HasRecursiveCalls.groovy     |  61 ------
 .../transform/tailrec/HasRecursiveCalls.java       |  63 ++++++
 .../transform/tailrec/InWhileLoopWrapper.groovy    |  83 --------
 .../transform/tailrec/InWhileLoopWrapper.java      |  60 ++++++
 .../transform/tailrec/RecursivenessTester.groovy   | 101 ----------
 .../transform/tailrec/RecursivenessTester.java     | 121 ++++++++++++
 ...Closures.groovy => ReturnAdderForClosures.java} |  31 ++-
 .../ReturnStatementToIterationConverter.groovy     | 147 --------------
 .../ReturnStatementToIterationConverter.java       | 164 +++++++++++++++
 .../transform/tailrec/StatementReplacer.groovy     | 106 ----------
 .../transform/tailrec/StatementReplacer.java       | 169 ++++++++++++++++
 .../tailrec/TailRecursiveASTTransformation.groovy  |   2 +-
 ...r.groovy => TernaryToIfStatementConverter.java} |  28 ++-
 .../transform/tailrec/UsedVariableTracker.java     |  38 ++++
 .../tailrec/VariableAccessReplacer.groovy          |  69 -------
 .../transform/tailrec/VariableAccessReplacer.java  |  66 +++++++
 .../tailrec/VariableExpressionReplacer.groovy      | 163 ---------------
 .../tailrec/VariableExpressionReplacer.java        | 219 +++++++++++++++++++++
 .../tailrec/VariableExpressionTransformer.groovy   |  46 -----
 .../tailrec/VariableExpressionTransformer.java     |  66 +++++++
 .../tailrec/VariableReplacedListener.java          |  29 +++
 .../tailrec/VariableExpressionReplacerTest.groovy  |   2 +-
 27 files changed, 1193 insertions(+), 944 deletions(-)

diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy
deleted file mode 100644
index 35cf13f..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.groovy
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.ast.stmt.ContinueStatement
-import org.codehaus.groovy.ast.stmt.ExpressionStatement
-import org.codehaus.groovy.ast.stmt.Statement
-
-import java.lang.reflect.Modifier
-
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.declS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
-
-/**
- * Helping to create a few standard AST constructs
- */
-@CompileStatic
-class AstHelper {
-    static ExpressionStatement createVariableDefinition(String variableName, ClassNode variableType, Expression value, boolean variableShouldBeFinal = false) {
-        def newVariable = localVarX(variableName, variableType)
-        if (variableShouldBeFinal)
-            newVariable.modifiers = Modifier.FINAL
-        (ExpressionStatement) declS(newVariable, value)
-    }
-
-    static ExpressionStatement createVariableAlias(String aliasName, ClassNode variableType, String variableName) {
-        createVariableDefinition(aliasName, variableType, varX(variableName, variableType))
-    }
-
-    static VariableExpression createVariableReference(Map variableSpec) {
-        varX((String) variableSpec.name, (ClassNode) variableSpec.type)
-    }
-
-    /**
-     * This statement should make the code jump to surrounding while loop's start label
-     * Does not work from within Closures
-     */
-    static Statement recurStatement() {
-        //continue _RECUR_HERE_
-        new ContinueStatement(InWhileLoopWrapper.LOOP_LABEL)
-    }
-
-    /**
-     * This statement will throw exception which will be caught and redirected to jump to surrounding while loop's start label
-     * Also works from within Closures but is a tiny bit slower
-     */
-    static Statement recurByThrowStatement() {
-        // throw InWhileLoopWrapper.LOOP_EXCEPTION
-        throwS(propX(classX(InWhileLoopWrapper), 'LOOP_EXCEPTION'))
-    }
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.java
new file mode 100644
index 0000000..fc09f6c
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/AstHelper.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.declS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.localVarX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+
+/**
+ * Helping to create a few standard AST constructs
+ */
+class AstHelper {
+    public static ExpressionStatement createVariableDefinition(String variableName, ClassNode variableType, Expression value) {
+        return createVariableDefinition(variableName, variableType, value, false);
+    }
+
+    public static ExpressionStatement createVariableDefinition(String variableName, ClassNode variableType, Expression value, boolean variableShouldBeFinal) {
+        VariableExpression newVariable = localVarX(variableName, variableType);
+        if (variableShouldBeFinal)
+            newVariable.setModifiers(Modifier.FINAL);
+        return (ExpressionStatement) declS(newVariable, value);
+    }
+
+    public static ExpressionStatement createVariableAlias(String aliasName, ClassNode variableType, String variableName) {
+        return createVariableDefinition(aliasName, variableType, varX(variableName, variableType));
+    }
+
+    public static VariableExpression createVariableReference(Map<String, ?> variableSpec) {
+        return varX((String) variableSpec.get("name"), (ClassNode) variableSpec.get("type"));
+    }
+
+    /**
+     * This statement should make the code jump to surrounding while loop's start label
+     * Does not work from within Closures
+     */
+    public static Statement recurStatement() {
+        //continue _RECUR_HERE_
+        return new ContinueStatement(InWhileLoopWrapper.LOOP_LABEL);
+    }
+
+    /**
+     * This statement will throw exception which will be caught and redirected to jump to surrounding while loop's start label
+     * Also works from within Closures but is a tiny bit slower
+     */
+    public static Statement recurByThrowStatement() {
+        // throw InWhileLoopWrapper.LOOP_EXCEPTION
+        return throwS(propX(classX(InWhileLoopWrapper.class), "LOOP_EXCEPTION"));
+    }
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.groovy
deleted file mode 100644
index 3f586d8..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.groovy
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.CodeVisitorSupport
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
-
-/**
- * Collect all recursive calls within method
- */
-@CompileStatic
-class CollectRecursiveCalls extends CodeVisitorSupport {
-	MethodNode method
-	List<Expression> recursiveCalls = []
-
-	void visitMethodCallExpression(MethodCallExpression call) {
-		if (isRecursive(call)) {
-			recursiveCalls << call
-		}
-        super.visitMethodCallExpression(call)
-    }
-
-	void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
-		if (isRecursive(call)) {
-            recursiveCalls << call
-        }
-		super.visitStaticMethodCallExpression(call)
-	}
-	
-	private boolean isRecursive(call) {
-		new RecursivenessTester().isRecursive(method: method, call: call)
-	}
-	
-	synchronized List<Expression> collect(MethodNode method) {
-		recursiveCalls.clear()
-		this.method = method
-		this.method.code.visit(this)
-		recursiveCalls
-	}
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.java
new file mode 100644
index 0000000..e996a86
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/CollectRecursiveCalls.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.apache.groovy.util.Maps;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Collect all recursive calls within method
+ */
+class CollectRecursiveCalls extends CodeVisitorSupport {
+	private final List<Expression> recursiveCalls = new ArrayList<>();
+	private MethodNode method;
+
+	@Override
+	public void visitMethodCallExpression(MethodCallExpression call) {
+		if (isRecursive(call)) {
+			recursiveCalls.add(call);
+		}
+        super.visitMethodCallExpression(call);
+    }
+
+	@Override
+	public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
+		if (isRecursive(call)) {
+            recursiveCalls.add(call);
+        }
+		super.visitStaticMethodCallExpression(call);
+	}
+	
+	public synchronized List<Expression> collect(MethodNode method) {
+		recursiveCalls.clear();
+		this.method = method;
+		this.method.getCode().visit(this);
+		return recursiveCalls;
+	}
+
+	private boolean isRecursive(Expression call) {
+		return new RecursivenessTester().isRecursive(Maps.of("method", method, "call", call));
+	}
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/GotoRecurHereException.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/GotoRecurHereException.java
new file mode 100644
index 0000000..54375ca
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/GotoRecurHereException.java
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+/**
+ * Exception will be thrown by recursive calls in closures and caught in while loop to continue to LOOP_LABEL
+ */
+public class GotoRecurHereException extends Exception {
+    private static final long serialVersionUID = -193137033604506378L;
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.groovy
deleted file mode 100644
index e8799c3..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.groovy
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.CodeVisitorSupport
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
-
-/**
- * Check if there are any recursive calls in a method
- */
-@CompileStatic
-class HasRecursiveCalls extends CodeVisitorSupport {
-    MethodNode method
-    boolean hasRecursiveCalls = false
-
-    void visitMethodCallExpression(MethodCallExpression call) {
-        if (isRecursive(call)) {
-            hasRecursiveCalls = true
-        } else {
-            super.visitMethodCallExpression(call)
-        }
-    }
-
-    void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
-        if (isRecursive(call)) {
-            hasRecursiveCalls = true
-        } else {
-            super.visitStaticMethodCallExpression(call)
-        }
-    }
-
-    private boolean isRecursive(call) {
-        new RecursivenessTester().isRecursive(method: method, call: call)
-    }
-
-    synchronized boolean test(MethodNode method) {
-        hasRecursiveCalls = false
-        this.method = method
-        this.method.code.visit(this)
-        hasRecursiveCalls
-    }
-}
\ No newline at end of file
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.java
new file mode 100644
index 0000000..9042ed2
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/HasRecursiveCalls.java
@@ -0,0 +1,63 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.apache.groovy.util.Maps;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+
+/**
+ * Check if there are any recursive calls in a method
+ */
+class HasRecursiveCalls extends CodeVisitorSupport {
+    private MethodNode method;
+    private boolean hasRecursiveCalls = false;
+
+    @Override
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        if (isRecursive(call)) {
+            hasRecursiveCalls = true;
+        } else {
+            super.visitMethodCallExpression(call);
+        }
+    }
+
+    @Override
+    public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
+        if (isRecursive(call)) {
+            hasRecursiveCalls = true;
+        } else {
+            super.visitStaticMethodCallExpression(call);
+        }
+    }
+
+    public synchronized boolean test(MethodNode method) {
+        hasRecursiveCalls = false;
+        this.method = method;
+        this.method.getCode().visit(this);
+        return hasRecursiveCalls;
+    }
+
+    private boolean isRecursive(Expression call) {
+        return new RecursivenessTester().isRecursive(Maps.of("method", method, "call", call));
+    }
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.groovy
deleted file mode 100644
index d622dc5..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.groovy
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.VariableScope
-import org.codehaus.groovy.ast.stmt.BlockStatement
-import org.codehaus.groovy.ast.stmt.ContinueStatement
-import org.codehaus.groovy.ast.stmt.EmptyStatement
-import org.codehaus.groovy.ast.stmt.Statement
-import org.codehaus.groovy.ast.stmt.TryCatchStatement
-import org.codehaus.groovy.ast.stmt.WhileStatement
-
-import static org.codehaus.groovy.ast.tools.GeneralUtils.block
-import static org.codehaus.groovy.ast.tools.GeneralUtils.boolX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.catchS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.constX
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param
-import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS
-
-/**
- * Wrap the body of a method in a while loop, nested in a try-catch.
- * This is the first step in making a tail recursive method iterative.
- *
- * There are two ways to invoke the next iteration step:
- * <ol>
- * <li>"continue _RECUR_HERE_" is used by recursive calls outside of closures</li>
- * <li>"throw LOOP_EXCEPTION" is used by recursive calls within closures b/c you cannot invoke "continue" from there</li>
- * </ol>
- */
-@CompileStatic
-class InWhileLoopWrapper {
-	static final String LOOP_LABEL = '_RECUR_HERE_'
-    static final GotoRecurHereException LOOP_EXCEPTION = new GotoRecurHereException()
-
-	void wrap(MethodNode method) {
-		BlockStatement oldBody = method.code as BlockStatement
-        TryCatchStatement tryCatchStatement = tryCatchS(
-                oldBody,
-                EmptyStatement.INSTANCE,
-                catchS(
-                        param(ClassHelper.make(GotoRecurHereException), 'ignore'),
-                        new ContinueStatement(InWhileLoopWrapper.LOOP_LABEL)
-                ))
-
-        WhileStatement whileLoop = new WhileStatement(
-                boolX(constX(true)),
-                block(new VariableScope(method.variableScope), tryCatchStatement)
-        )
-        List<Statement> whileLoopStatements = ((BlockStatement) whileLoop.loopBlock).statements
-        if (whileLoopStatements.size() > 0)
-            whileLoopStatements[0].statementLabel = LOOP_LABEL
-		BlockStatement newBody = block(new VariableScope(method.variableScope))
-		newBody.addStatement(whileLoop)
-		method.code = newBody
-	}
-}
-
-/**
- * Exception will be thrown by recursive calls in closures and caught in while loop to continue to LOOP_LABEL
- */
-@CompileStatic
-class GotoRecurHereException extends Exception {
-    private static final long serialVersionUID = -193137033604506378L
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.java
new file mode 100644
index 0000000..a4d8f09
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.ast.tools.GeneralUtils;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.List;
+
+/**
+ * Wrap the body of a method in a while loop, nested in a try-catch.
+ * This is the first step in making a tail recursive method iterative.
+ * <p>
+ * There are two ways to invoke the next iteration step:
+ * <ol>
+ * <li>"continue _RECUR_HERE_" is used by recursive calls outside of closures</li>
+ * <li>"throw LOOP_EXCEPTION" is used by recursive calls within closures b/c you cannot invoke "continue" from there</li>
+ * </ol>
+ */
+class InWhileLoopWrapper {
+    public void wrap(MethodNode method) {
+        BlockStatement oldBody = DefaultGroovyMethods.asType(method.getCode(), BlockStatement.class);
+        TryCatchStatement tryCatchStatement = GeneralUtils.tryCatchS(oldBody, EmptyStatement.INSTANCE, GeneralUtils.catchS(GeneralUtils.param(ClassHelper.make(GotoRecurHereException.class), "ignore"), new ContinueStatement(InWhileLoopWrapper.LOOP_LABEL)));
+
+        WhileStatement whileLoop = new WhileStatement(GeneralUtils.boolX(GeneralUtils.constX(true)), GeneralUtils.block(new VariableScope(method.getVariableScope()), tryCatchStatement));
+        List<Statement> whileLoopStatements = ((BlockStatement) whileLoop.getLoopBlock()).getStatements();
+        if (whileLoopStatements.size() > 0) whileLoopStatements.get(0).setStatementLabel(LOOP_LABEL);
+        BlockStatement newBody = GeneralUtils.block(new VariableScope(method.getVariableScope()));
+        newBody.addStatement(whileLoop);
+        method.setCode(newBody);
+    }
+
+    public static final String LOOP_LABEL = "_RECUR_HERE_";
+    public static final GotoRecurHereException LOOP_EXCEPTION = new GotoRecurHereException();
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.groovy
deleted file mode 100644
index 15e24e4..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.groovy
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.expr.ConstantExpression
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
-import org.codehaus.groovy.ast.expr.VariableExpression
-
-/**
- * Test if a method call is recursive if called within a given method node.
- * Handles static calls as well.
- * 
- * Currently known simplifications:
- * <ul>
- * <li>Does not check for method overloading or overridden methods</li>
- * <li>Does not check for matching return types; even void and any object type are considered to be compatible</li>
- * <li>Argument type matching could be more specific in case of static compilation</li>
- * <li>Method names via a GString are never considered to be recursive</li>
- * </ul>
- */
-class RecursivenessTester {
-	boolean isRecursive(params) {
-		assert params.method.class == MethodNode
-		assert params.call.class == MethodCallExpression || StaticMethodCallExpression
-
-		isRecursive(params.method, params.call)
-	}
-
-    @SuppressWarnings('Instanceof')
-	boolean isRecursive(MethodNode method, MethodCallExpression call) {
-		if (!isCallToThis(call))
-			return false
-        // Could be a GStringExpression
-        if (! (call.method instanceof ConstantExpression))
-            return false
-		if (call.method.value != method.name)
-			return false
-		methodParamsMatchCallArgs(method, call)
-	}
-
-    boolean isRecursive(MethodNode method, StaticMethodCallExpression call) {
-        if (!method.isStatic())
-            return false
-        if (method.declaringClass != call.ownerType)
-            return false
-        if (call.method != method.name)
-            return false
-        methodParamsMatchCallArgs(method, call)
-    }
-
-    @SuppressWarnings('Instanceof')
-	private boolean isCallToThis(MethodCallExpression call) {
-		if (call.objectExpression == null)
-			return call.isImplicitThis()
-        if (! (call.objectExpression instanceof VariableExpression)) {
-            return false
-        }
-		call.objectExpression.isThisExpression()
-	}
-	
-	private boolean methodParamsMatchCallArgs(method, call) {
-        if (method.parameters.size() != call.arguments.expressions.size())
-            return false
-        def classNodePairs = [method.parameters*.type, call.arguments*.type].transpose()
-        classNodePairs.every { ClassNode paramType, ClassNode argType  ->
-            areTypesCallCompatible(argType, paramType)
-        }
-	}
-
-    /**
-     * Parameter type and calling argument type can both be derived from the other since typing information is
-     * optional in Groovy.
-     * Since int is not derived from Integer (nor the other way around) we compare the boxed types
-     */
-    private areTypesCallCompatible(ClassNode argType, ClassNode paramType) {
-        ClassNode boxedArg = ClassHelper.getWrapper(argType)
-        ClassNode boxedParam = ClassHelper.getWrapper(paramType)
-        boxedArg.isDerivedFrom(boxedParam) || boxedParam.isDerivedFrom(boxedArg)
-    }
-
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.java
new file mode 100644
index 0000000..d88d88e
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/RecursivenessTester.java
@@ -0,0 +1,121 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.transpose;
+
+/**
+ * Test if a method call is recursive if called within a given method node.
+ * Handles static calls as well.
+ * <p>
+ * Currently known simplifications:
+ * <ul>
+ * <li>Does not check for method overloading or overridden methods</li>
+ * <li>Does not check for matching return types; even void and any object type are considered to be compatible</li>
+ * <li>Argument type matching could be more specific in case of static compilation</li>
+ * <li>Method names via a GString are never considered to be recursive</li>
+ * </ul>
+ */
+class RecursivenessTester {
+    public boolean isRecursive(Map<String, ASTNode> params) {
+        ASTNode method = params.get("method");
+        assert MethodNode.class.equals(method.getClass());
+        ASTNode call = params.get("call");
+        Class<? extends ASTNode> callClass = call.getClass();
+        assert MethodCallExpression.class.equals(callClass) || StaticMethodCallExpression.class.equals(callClass);
+
+        if (callClass == MethodCallExpression.class) {
+            return isRecursive((MethodNode) method, (MethodCallExpression) call);
+        }
+        return isRecursive((MethodNode) method, (StaticMethodCallExpression) call);
+    }
+
+    @SuppressWarnings("Instanceof")
+    public boolean isRecursive(MethodNode method, MethodCallExpression call) {
+        if (!isCallToThis(call)) return false;
+        // Could be a GStringExpression
+        if (!(call.getMethod() instanceof ConstantExpression)) return false;
+        if (!((ConstantExpression) call.getMethod()).getValue().equals(method.getName())) return false;
+        return methodParamsMatchCallArgs(method, call);
+    }
+
+    public boolean isRecursive(MethodNode method, StaticMethodCallExpression call) {
+        if (!method.isStatic()) return false;
+        if (!method.getDeclaringClass().equals(call.getOwnerType())) return false;
+        if (!call.getMethod().equals(method.getName())) return false;
+        return methodParamsMatchCallArgs(method, call);
+    }
+
+    @SuppressWarnings("Instanceof")
+    private boolean isCallToThis(MethodCallExpression call) {
+        if (call.getObjectExpression() == null) return call.isImplicitThis();
+        if (!(call.getObjectExpression() instanceof VariableExpression)) {
+            return false;
+        }
+
+        return ((boolean) (DefaultGroovyMethods.invokeMethod(call.getObjectExpression(), "isThisExpression", new Object[0])));
+    }
+
+    private boolean methodParamsMatchCallArgs(MethodNode method, Expression call) {
+        TupleExpression arguments;
+        if (call instanceof MethodCallExpression) {
+            arguments = ((TupleExpression) ((MethodCallExpression) call).getArguments());
+        } else {
+            arguments = ((TupleExpression) ((StaticMethodCallExpression) call).getArguments());
+        }
+
+        if (method.getParameters().length != arguments.getExpressions().size())
+            return false;
+
+        List<List<ClassNode>> classNodePairs =
+                transpose(Arrays.asList(
+                        Arrays.stream(method.getParameters()).map(Parameter::getType).collect(Collectors.toList()),
+                        arguments.getExpressions().stream().map(Expression::getType).collect(Collectors.toList())));
+        return classNodePairs.stream().allMatch(t -> areTypesCallCompatible(t.get(0), t.get(1)));
+    }
+
+    /**
+     * Parameter type and calling argument type can both be derived from the other since typing information is
+     * optional in Groovy.
+     * Since int is not derived from Integer (nor the other way around) we compare the boxed types
+     */
+    private Boolean areTypesCallCompatible(ClassNode argType, ClassNode paramType) {
+        ClassNode boxedArg = ClassHelper.getWrapper(argType);
+        ClassNode boxedParam = ClassHelper.getWrapper(paramType);
+        return boxedArg.isDerivedFrom(boxedParam) || boxedParam.isDerivedFrom(boxedArg);
+    }
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.java
similarity index 63%
rename from src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.groovy
rename to src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.java
index 62c4aa9..2dc6196 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnAdderForClosures.java
@@ -16,31 +16,30 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.codehaus.groovy.transform.tailrec
+package org.codehaus.groovy.transform.tailrec;
 
-import org.codehaus.groovy.ast.ClassHelper
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.CodeVisitorSupport
-import org.codehaus.groovy.ast.MethodNode
-import org.codehaus.groovy.ast.Parameter
-import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.classgen.ReturnAdder
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.classgen.ReturnAdder;
 
 /**
  * Adds explicit return statements to implicit return points in a closure. This is necessary since
  * tail-recursion is detected by having the recursive call within the return statement.
  */
 class ReturnAdderForClosures extends CodeVisitorSupport {
-
-    synchronized void visitMethod(MethodNode method) {
-        method.code.visit(this)
+    public synchronized void visitMethod(MethodNode method) {
+        method.getCode().visit(this);
     }
 
-    void visitClosureExpression(ClosureExpression expression) {
+    @Override
+    public void visitClosureExpression(ClosureExpression expression) {
         //Create a dummy method with the closure's code as the method's code. Then user ReturnAdder, which only works for methods.
-        MethodNode node = new MethodNode('dummy', 0, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, expression.code)
-        new ReturnAdder().visitMethod(node)
-        super.visitClosureExpression(expression)
+        MethodNode node = new MethodNode("dummy", 0, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, expression.getCode());
+        new ReturnAdder().visitMethod(node);
+        super.visitClosureExpression(expression);
     }
-
 }
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.groovy
deleted file mode 100644
index 5ebac87..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.groovy
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ClassNode
-import org.codehaus.groovy.ast.expr.BinaryExpression
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.MethodCallExpression
-import org.codehaus.groovy.ast.expr.StaticMethodCallExpression
-import org.codehaus.groovy.ast.expr.TupleExpression
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.ast.stmt.BlockStatement
-import org.codehaus.groovy.ast.stmt.ExpressionStatement
-import org.codehaus.groovy.ast.stmt.ReturnStatement
-import org.codehaus.groovy.ast.stmt.Statement
-
-import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.varX
-
-/**
- * Translates all return statements into an invocation of the next iteration. This can be either
- * - "continue LOOP_LABEL": Outside closures
- * - "throw LOOP_EXCEPTION": Inside closures
- *
- * Moreover, before adding the recur statement the iteration parameters (originally the method args)
- * are set to their new value. To prevent variable aliasing parameters will be copied into temp vars
- * before they are changes so that their current iteration value can be used when setting other params.
- *
- * There's probably place for optimizing the amount of variable copying being done, e.g.
- * parameters that are only handed through must not be copied at all.
- */
-@CompileStatic
-class ReturnStatementToIterationConverter {
-
-    Statement recurStatement = AstHelper.recurStatement()
-
-    Statement convert(ReturnStatement statement, Map<Integer, Map> positionMapping) {
-        Expression recursiveCall = statement.expression
-        if (!isAMethodCalls(recursiveCall))
-            return statement
-
-        Map<String, Map> tempMapping = [:]
-        Map tempDeclarations = [:]
-        List<ExpressionStatement> argAssignments = []
-
-        BlockStatement result = new BlockStatement()
-        result.copyStatementLabels(statement)
-
-        /* Create temp declarations for all method arguments.
-         * Add the declarations and var mapping to tempMapping and tempDeclarations for further reference.
-         */
-        getArguments(recursiveCall).eachWithIndex { Expression expression, int index ->
-            ExpressionStatement tempDeclaration = createTempDeclaration(index, positionMapping, tempMapping, tempDeclarations)
-            result.addStatement(tempDeclaration)
-        }
-
-        /*
-         * Assign the iteration variables their new value before recuring
-         */
-        getArguments(recursiveCall).eachWithIndex { Expression expression, int index ->
-            ExpressionStatement argAssignment = createAssignmentToIterationVariable(expression, index, positionMapping)
-            argAssignments.add(argAssignment)
-            result.addStatement(argAssignment)
-        }
-
-        Set<String> unusedTemps = replaceAllArgUsages(argAssignments, tempMapping)
-        for (String temp : unusedTemps) {
-            result.statements.remove(tempDeclarations[temp])
-        }
-        result.addStatement(recurStatement)
-
-        result
-    }
-
-    private ExpressionStatement createAssignmentToIterationVariable(Expression expression, int index, Map<Integer, Map> positionMapping) {
-        String argName = positionMapping[index]['name']
-        ClassNode argAndTempType = positionMapping[index]['type'] as ClassNode
-        ExpressionStatement argAssignment = (ExpressionStatement) assignS(varX(argName, argAndTempType), expression)
-        argAssignment
-    }
-
-    private ExpressionStatement createTempDeclaration(int index, Map<Integer, Map> positionMapping, Map<String, Map> tempMapping, Map tempDeclarations) {
-        String argName = positionMapping[index]['name']
-        String tempName = "_${argName}_"
-        ClassNode argAndTempType = positionMapping[index]['type'] as ClassNode
-        ExpressionStatement tempDeclaration = AstHelper.createVariableAlias(tempName, argAndTempType, argName)
-        tempMapping[argName] = [name: tempName, type: argAndTempType]
-        tempDeclarations[tempName] = tempDeclaration
-        tempDeclaration
-    }
-
-    @SuppressWarnings('Instanceof')
-    private List<Expression> getArguments(Expression recursiveCall) {
-        if (recursiveCall instanceof MethodCallExpression)
-            return ((TupleExpression) ((MethodCallExpression) recursiveCall).arguments).expressions
-        if (recursiveCall instanceof StaticMethodCallExpression)
-            return ((TupleExpression) ((StaticMethodCallExpression) recursiveCall).arguments).expressions
-    }
-
-    private boolean isAMethodCalls(Expression expression) {
-        expression.class in [MethodCallExpression, StaticMethodCallExpression]
-    }
-
-    private Set<String> replaceAllArgUsages(List<ExpressionStatement> iterationVariablesAssignmentNodes, Map<String, Map> tempMapping) {
-        Set<String> unusedTempNames = tempMapping.values().collect { Map nameAndType -> (String) nameAndType['name'] } as Set<String>
-        VariableReplacedListener tracker = new UsedVariableTracker()
-        for (ExpressionStatement statement : iterationVariablesAssignmentNodes) {
-            replaceArgUsageByTempUsage((BinaryExpression) statement.expression, tempMapping, tracker)
-        }
-        unusedTempNames = unusedTempNames - tracker.usedVariableNames
-        unusedTempNames
-    }
-
-    private void replaceArgUsageByTempUsage(BinaryExpression binary, Map tempMapping, UsedVariableTracker tracker) {
-        VariableAccessReplacer replacer = new VariableAccessReplacer(nameAndTypeMapping: tempMapping, listener: tracker)
-        // Replacement must only happen in binary.rightExpression. It's a hack in VariableExpressionReplacer which takes care of that.
-        replacer.replaceIn(binary)
-    }
-}
-
-@CompileStatic
-class UsedVariableTracker implements VariableReplacedListener {
-
-    final Set<String> usedVariableNames = [] as Set
-
-    @Override
-    void variableReplaced(VariableExpression oldVar, VariableExpression newVar) {
-        usedVariableNames.add(newVar.name)
-    }
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.java
new file mode 100644
index 0000000..a9e116e
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/ReturnStatementToIterationConverter.java
@@ -0,0 +1,164 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.tools.GeneralUtils;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Translates all return statements into an invocation of the next iteration. This can be either
+ * - "continue LOOP_LABEL": Outside closures
+ * - "throw LOOP_EXCEPTION": Inside closures
+ * <p>
+ * Moreover, before adding the recur statement the iteration parameters (originally the method args)
+ * are set to their new value. To prevent variable aliasing parameters will be copied into temp vars
+ * before they are changes so that their current iteration value can be used when setting other params.
+ * <p>
+ * There's probably place for optimizing the amount of variable copying being done, e.g.
+ * parameters that are only handed through must not be copied at all.
+ */
+public class ReturnStatementToIterationConverter {
+    public Statement convert(ReturnStatement statement, final Map<Integer, Map> positionMapping) {
+        Expression recursiveCall = statement.getExpression();
+        if (!isAMethodCalls(recursiveCall)) return statement;
+
+        final Map<String, Map> tempMapping = new LinkedHashMap<String, Map>();
+        final Map tempDeclarations = new LinkedHashMap<Object, Object>();
+        final List<ExpressionStatement> argAssignments = new ArrayList<ExpressionStatement>();
+
+        final BlockStatement result = new BlockStatement();
+        result.copyStatementLabels(statement);
+
+        /* Create temp declarations for all method arguments.
+         * Add the declarations and var mapping to tempMapping and tempDeclarations for further reference.
+         */
+        DefaultGroovyMethods.eachWithIndex(getArguments(recursiveCall), new Closure<Void>(this, this) {
+            public void doCall(Expression expression, int index) {
+                ExpressionStatement tempDeclaration = createTempDeclaration(index, positionMapping, tempMapping, tempDeclarations);
+                result.addStatement(tempDeclaration);
+            }
+
+        });
+
+        /*
+         * Assign the iteration variables their new value before recuring
+         */
+        DefaultGroovyMethods.eachWithIndex(getArguments(recursiveCall), new Closure<Void>(this, this) {
+            public void doCall(Expression expression, int index) {
+                ExpressionStatement argAssignment = createAssignmentToIterationVariable(expression, index, positionMapping);
+                argAssignments.add(argAssignment);
+                result.addStatement(argAssignment);
+            }
+
+        });
+
+        Set<String> unusedTemps = replaceAllArgUsages(argAssignments, tempMapping);
+        for (String temp : unusedTemps) {
+            result.getStatements().remove(tempDeclarations.get(temp));
+        }
+
+        result.addStatement(recurStatement);
+
+        return result;
+    }
+
+    private ExpressionStatement createAssignmentToIterationVariable(Expression expression, int index, Map<Integer, Map> positionMapping) {
+        String argName = (String) positionMapping.get(index).get("name");
+        ClassNode argAndTempType = DefaultGroovyMethods.asType(positionMapping.get(index).get("type"), ClassNode.class);
+        ExpressionStatement argAssignment = (ExpressionStatement) GeneralUtils.assignS(GeneralUtils.varX(argName, argAndTempType), expression);
+        return argAssignment;
+    }
+
+    private ExpressionStatement createTempDeclaration(int index, Map<Integer, Map> positionMapping, Map<String, Map> tempMapping, Map tempDeclarations) {
+        final String argName = (String) positionMapping.get(index).get("name");
+        String tempName = (String) "_" + argName + "_";
+        ClassNode argAndTempType = DefaultGroovyMethods.asType(positionMapping.get(index).get("type"), ClassNode.class);
+        ExpressionStatement tempDeclaration = AstHelper.createVariableAlias(tempName, argAndTempType, argName);
+        LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(2);
+        map.put("name", tempName);
+        map.put("type", argAndTempType);
+        tempMapping.put(argName, map);
+        tempDeclarations.put(tempName, tempDeclaration);
+        return tempDeclaration;
+    }
+
+    @SuppressWarnings("Instanceof")
+    private List<Expression> getArguments(Expression recursiveCall) {
+        if (recursiveCall instanceof MethodCallExpression)
+            return ((TupleExpression) ((MethodCallExpression) recursiveCall).getArguments()).getExpressions();
+        if (recursiveCall instanceof StaticMethodCallExpression)
+            return ((TupleExpression) ((StaticMethodCallExpression) recursiveCall).getArguments()).getExpressions();
+        return null;
+    }
+
+    private boolean isAMethodCalls(Expression expression) {
+        Class<?> clazz = expression.getClass();
+        return MethodCallExpression.class == clazz || StaticMethodCallExpression.class == clazz;
+    }
+
+    private Set<String> replaceAllArgUsages(List<ExpressionStatement> iterationVariablesAssignmentNodes, Map<String, Map> tempMapping) {
+        Set<String> unusedTempNames = DefaultGroovyMethods.asType(DefaultGroovyMethods.collect(tempMapping.values(), new Closure<String>(this, this) {
+            public String doCall(Map nameAndType) {
+                return (String) nameAndType.get("name");
+            }
+
+        }), Set.class);
+        VariableReplacedListener tracker = new UsedVariableTracker();
+        for (ExpressionStatement statement : iterationVariablesAssignmentNodes) {
+            replaceArgUsageByTempUsage((BinaryExpression) statement.getExpression(), tempMapping, (UsedVariableTracker) tracker);
+        }
+
+        unusedTempNames = DefaultGroovyMethods.minus(unusedTempNames, ((UsedVariableTracker) tracker).getUsedVariableNames());
+        return unusedTempNames;
+    }
+
+    private void replaceArgUsageByTempUsage(BinaryExpression binary, Map tempMapping, UsedVariableTracker tracker) {
+        VariableAccessReplacer replacer = new VariableAccessReplacer(tempMapping, tracker);
+        // Replacement must only happen in binary.rightExpression. It's a hack in VariableExpressionReplacer which takes care of that.
+        replacer.replaceIn(binary);
+    }
+
+    public Statement getRecurStatement() {
+        return recurStatement;
+    }
+
+    public void setRecurStatement(Statement recurStatement) {
+        this.recurStatement = recurStatement;
+    }
+
+    private Statement recurStatement = AstHelper.recurStatement();
+}
+
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.groovy
deleted file mode 100644
index 8c7a55a..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.groovy
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.CodeVisitorSupport
-import org.codehaus.groovy.ast.expr.ClosureExpression
-import org.codehaus.groovy.ast.stmt.BlockStatement
-import org.codehaus.groovy.ast.stmt.DoWhileStatement
-import org.codehaus.groovy.ast.stmt.ForStatement
-import org.codehaus.groovy.ast.stmt.IfStatement
-import org.codehaus.groovy.ast.stmt.Statement
-import org.codehaus.groovy.ast.stmt.WhileStatement
-
-/**
- * Tool for replacing Statement objects in an AST by other Statement instances.
- *
- * Within @TailRecursive it is used to swap ReturnStatements with looping back to RECUR label
- */
-@CompileStatic
-class StatementReplacer extends CodeVisitorSupport {
-
-    Closure<Boolean> when = { Statement node -> false }
-    Closure<Statement> replaceWith = { Statement statement -> statement }
-    int closureLevel = 0
-
-    void replaceIn(ASTNode root) {
-        root.visit(this)
-    }
-
-    void visitClosureExpression(ClosureExpression expression) {
-        closureLevel++
-        try {
-            super.visitClosureExpression(expression)
-        } finally {
-            closureLevel--
-        }
-    }
-
-    void visitBlockStatement(BlockStatement block) {
-        List<Statement> copyOfStatements = new ArrayList<Statement>(block.statements)
-        copyOfStatements.eachWithIndex { Statement statement, int index ->
-            replaceIfNecessary(statement) { Statement node -> block.statements[index] = node }
-        }
-        super.visitBlockStatement(block)
-    }
-
-    void visitIfElse(IfStatement ifElse) {
-        replaceIfNecessary(ifElse.ifBlock) { Statement s -> ifElse.ifBlock = s }
-        replaceIfNecessary(ifElse.elseBlock) { Statement s -> ifElse.elseBlock = s }
-        super.visitIfElse(ifElse)
-    }
-
-    void visitForLoop(ForStatement forLoop) {
-        replaceIfNecessary(forLoop.loopBlock) { Statement s -> forLoop.loopBlock = s }
-        super.visitForLoop(forLoop)
-    }
-
-    void visitWhileLoop(WhileStatement loop) {
-        replaceIfNecessary(loop.loopBlock) { Statement s -> loop.loopBlock = s }
-        super.visitWhileLoop(loop)
-    }
-
-    void visitDoWhileLoop(DoWhileStatement loop) {
-        replaceIfNecessary(loop.loopBlock) { Statement s -> loop.loopBlock = s }
-        super.visitDoWhileLoop(loop)
-    }
-
-
-    private void replaceIfNecessary(Statement nodeToCheck, Closure replacementCode) {
-        if (conditionFulfilled(nodeToCheck)) {
-            ASTNode replacement = replaceWith(nodeToCheck)
-            replacement.sourcePosition = nodeToCheck
-            replacement.copyNodeMetaData(nodeToCheck)
-            replacementCode(replacement)
-        }
-    }
-
-    private boolean conditionFulfilled(ASTNode nodeToCheck) {
-        if (when.maximumNumberOfParameters < 2)
-            return when(nodeToCheck)
-        when(nodeToCheck, isInClosure())
-    }
-
-    private boolean isInClosure() {
-        closureLevel > 0
-    }
-
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.java
new file mode 100644
index 0000000..a3f1c61
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/StatementReplacer.java
@@ -0,0 +1,169 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tool for replacing Statement objects in an AST by other Statement instances.
+ * <p>
+ * Within @TailRecursive it is used to swap ReturnStatements with looping back to RECUR label
+ */
+class StatementReplacer extends CodeVisitorSupport {
+    public void replaceIn(ASTNode root) {
+        root.visit(this);
+    }
+
+    public void visitClosureExpression(ClosureExpression expression) {
+        closureLevel++;
+        try {
+            super.visitClosureExpression(expression);
+        } finally {
+            closureLevel--;
+        }
+    }
+
+    public void visitBlockStatement(final BlockStatement block) {
+        List<Statement> copyOfStatements = new ArrayList<Statement>(block.getStatements());
+        DefaultGroovyMethods.eachWithIndex(copyOfStatements, new Closure<Void>(this, this) {
+            public void doCall(Statement statement, final int index) {
+                replaceIfNecessary(statement, new Closure<Statement>(StatementReplacer.this, StatementReplacer.this) {
+                    public Statement doCall(Statement node) {
+                        block.getStatements().set(index, node);
+                        return node;
+                    }
+                });
+            }
+        });
+        super.visitBlockStatement(block);
+    }
+
+    public void visitIfElse(final IfStatement ifElse) {
+        replaceIfNecessary(ifElse.getIfBlock(), new Closure<Statement>(this, this) {
+            public Statement doCall(Statement s) {
+                ifElse.setIfBlock(s);
+                return s;
+            }
+        });
+        replaceIfNecessary(ifElse.getElseBlock(), new Closure<Statement>(this, this) {
+            public Statement doCall(Statement s) {
+                ifElse.setElseBlock(s);
+                return s;
+            }
+        });
+        super.visitIfElse(ifElse);
+    }
+
+    public void visitForLoop(final ForStatement forLoop) {
+        replaceIfNecessary(forLoop.getLoopBlock(), new Closure<Statement>(this, this) {
+            public Statement doCall(Statement s) {
+                forLoop.setLoopBlock(s);
+                return s;
+            }
+        });
+        super.visitForLoop(forLoop);
+    }
+
+    public void visitWhileLoop(final WhileStatement loop) {
+        replaceIfNecessary(loop.getLoopBlock(), new Closure<Statement>(this, this) {
+            public Statement doCall(Statement s) {
+                loop.setLoopBlock(s);
+                return s;
+            }
+        });
+        super.visitWhileLoop(loop);
+    }
+
+    public void visitDoWhileLoop(final DoWhileStatement loop) {
+        replaceIfNecessary(loop.getLoopBlock(), new Closure<Statement>(this, this) {
+            public Statement doCall(Statement s) {
+                loop.setLoopBlock(s);
+                return s;
+            }
+        });
+        super.visitDoWhileLoop(loop);
+    }
+
+    private void replaceIfNecessary(Statement nodeToCheck, Closure replacementCode) {
+        if (conditionFulfilled(nodeToCheck)) {
+            Statement replacement = replaceWith.call(nodeToCheck);
+            replacement.setSourcePosition(nodeToCheck);
+            replacement.copyNodeMetaData(nodeToCheck);
+            replacementCode.call(replacement);
+        }
+    }
+
+    private boolean conditionFulfilled(ASTNode nodeToCheck) {
+        if (when.getMaximumNumberOfParameters() < 2) return when.call(nodeToCheck);
+        return when.call(nodeToCheck, isInClosure());
+    }
+
+    private boolean isInClosure() {
+        return closureLevel > 0;
+    }
+
+    public Closure<Boolean> getWhen() {
+        return when;
+    }
+
+    public void setWhen(Closure<Boolean> when) {
+        this.when = when;
+    }
+
+    public Closure<Statement> getReplaceWith() {
+        return replaceWith;
+    }
+
+    public void setReplaceWith(Closure<Statement> replaceWith) {
+        this.replaceWith = replaceWith;
+    }
+
+    public int getClosureLevel() {
+        return closureLevel;
+    }
+
+    public void setClosureLevel(int closureLevel) {
+        this.closureLevel = closureLevel;
+    }
+
+    private Closure<Boolean> when = new Closure<Boolean>(this, this) {
+        public Boolean doCall(Statement node) {
+            return false;
+        }
+    };
+    private Closure<Statement> replaceWith = new Closure<Statement>(this, this) {
+        public Statement doCall(Statement statement) {
+            return statement;
+        }
+    };
+    private int closureLevel = 0;
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/TailRecursiveASTTransformation.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/TailRecursiveASTTransformation.groovy
index 0b71062..bc82b56 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/TailRecursiveASTTransformation.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/TailRecursiveASTTransformation.groovy
@@ -155,7 +155,7 @@ class TailRecursiveASTTransformation extends AbstractASTTransformation {
     }
 
     private void replaceAllAccessToParams(MethodNode method, Map<String, Map> nameAndTypeMapping) {
-        new VariableAccessReplacer(nameAndTypeMapping: nameAndTypeMapping).replaceIn(method.code)
+        new VariableAccessReplacer(nameAndTypeMapping).replaceIn(method.code)
     }
 
     // Public b/c there are tests for this method
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.java
similarity index 60%
rename from src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.groovy
rename to src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.java
index 165650f..4016c5c 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/TernaryToIfStatementConverter.java
@@ -16,28 +16,26 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.codehaus.groovy.transform.tailrec
+package org.codehaus.groovy.transform.tailrec;
 
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.expr.TernaryExpression
-import org.codehaus.groovy.ast.stmt.ReturnStatement
-import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.ast.expr.TernaryExpression;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
 
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS
-import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifElseS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asType;
 
 /**
  * Since a ternary statement has more than one exit point tail-recursiveness testing cannot be easily done.
  * Therefore this class translates a ternary statement (or Elvis operator) into the equivalent if-else statement.
  */
-@CompileStatic
 class TernaryToIfStatementConverter {
-
-    @SuppressWarnings('Instanceof')
-    Statement convert(ReturnStatement statementWithInnerTernaryExpression) {
-        if (!(statementWithInnerTernaryExpression.expression instanceof TernaryExpression))
-            return statementWithInnerTernaryExpression
-        TernaryExpression ternary = statementWithInnerTernaryExpression.expression as TernaryExpression
-        ifElseS(ternary.booleanExpression, returnS(ternary.trueExpression), returnS(ternary.falseExpression))
+    @SuppressWarnings("Instanceof")
+    public Statement convert(ReturnStatement statementWithInnerTernaryExpression) {
+        if (!(statementWithInnerTernaryExpression.getExpression() instanceof TernaryExpression))
+            return statementWithInnerTernaryExpression;
+        TernaryExpression ternary = asType(statementWithInnerTernaryExpression.getExpression(), TernaryExpression.class);
+        return ifElseS(ternary.getBooleanExpression(), returnS(ternary.getTrueExpression()), returnS(ternary.getFalseExpression()));
     }
 }
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/UsedVariableTracker.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/UsedVariableTracker.java
new file mode 100644
index 0000000..6f08f96
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/UsedVariableTracker.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+class UsedVariableTracker implements VariableReplacedListener {
+    @Override
+    public void variableReplaced(VariableExpression oldVar, VariableExpression newVar) {
+        usedVariableNames.add(newVar.getName());
+    }
+
+    public Set<String> getUsedVariableNames() {
+        return usedVariableNames;
+    }
+
+    private final Set<String> usedVariableNames = DefaultGroovyMethods.asType(new ArrayList(), Set.class);
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.groovy
deleted file mode 100644
index b53b5bd..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.groovy
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.expr.VariableExpression
-
-/**
- * Replace all access to variables and args by new variables.
- * The variable names to replace as well as their replacement name and type have to be configured
- * in nameAndTypeMapping before calling replaceIn().
- *
- * The VariableReplacedListener can be set if clients want to react to variable replacement.
- */
-@CompileStatic
-class VariableAccessReplacer {
-
-    /**
-     * Nested map of variable accesses to replace
-     * e.g.: [
-     *          'varToReplace': [name: 'newVar', type: TypeOfVar],
-     *          'varToReplace2': [name: 'newVar2', type: TypeOfVar2],
-     *       ]
-     */
-    Map<String, Map> nameAndTypeMapping = [:]
-
-    VariableReplacedListener listener = VariableReplacedListener.NULL
-
-    void replaceIn(ASTNode root) {
-        Closure<Boolean> whenParam = { VariableExpression expr ->
-            return nameAndTypeMapping.containsKey(expr.name)
-        }
-        Closure<VariableExpression> replaceWithLocalVariable = { VariableExpression expr ->
-            def newVar = AstHelper.createVariableReference(nameAndTypeMapping[expr.name])
-            listener.variableReplaced(expr, newVar)
-            return newVar
-        }
-        new VariableExpressionReplacer(when: whenParam, replaceWith: replaceWithLocalVariable).replaceIn(root)
-    }
-}
-
-@CompileStatic
-interface VariableReplacedListener {
-    void variableReplaced(VariableExpression oldVar, VariableExpression newVar)
-
-    public static VariableReplacedListener NULL = new VariableReplacedListener() {
-        @Override
-        void variableReplaced(VariableExpression oldVar, VariableExpression newVar) {
-            //do nothing
-        }
-    }
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.java
new file mode 100644
index 0000000..8225aa4
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableAccessReplacer.java
@@ -0,0 +1,66 @@
+package org.codehaus.groovy.transform.tailrec;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Replace all access to variables and args by new variables.
+ * The variable names to replace as well as their replacement name and type have to be configured
+ * in nameAndTypeMapping before calling replaceIn().
+ * <p>
+ * The VariableReplacedListener can be set if clients want to react to variable replacement.
+ */
+class VariableAccessReplacer {
+    public VariableAccessReplacer(Map<String, Map> nameAndTypeMapping) {
+        this.nameAndTypeMapping = nameAndTypeMapping;
+    }
+
+    public VariableAccessReplacer(Map<String, Map> nameAndTypeMapping, VariableReplacedListener listener) {
+        this.nameAndTypeMapping = nameAndTypeMapping;
+        this.listener = listener;
+    }
+
+    public void replaceIn(ASTNode root) {
+        Closure<Boolean> whenParam = new Closure<Boolean>(this, this) {
+            public Boolean doCall(VariableExpression expr) {
+                return nameAndTypeMapping.containsKey(expr.getName());
+            }
+
+        };
+        Closure<VariableExpression> replaceWithLocalVariable = new Closure<VariableExpression>(this, this) {
+            public VariableExpression doCall(VariableExpression expr) {
+                VariableExpression newVar = AstHelper.createVariableReference(nameAndTypeMapping.get(expr.getName()));
+                getListener().variableReplaced(expr, newVar);
+                return newVar;
+            }
+
+        };
+        new VariableExpressionReplacer(whenParam, replaceWithLocalVariable).replaceIn(root);
+    }
+
+    public void setNameAndTypeMapping(Map<String, Map> nameAndTypeMapping) {
+        this.nameAndTypeMapping = nameAndTypeMapping;
+    }
+
+    public VariableReplacedListener getListener() {
+        return listener;
+    }
+
+    public void setListener(VariableReplacedListener listener) {
+        this.listener = listener;
+    }
+
+    /**
+     * Nested map of variable accesses to replace
+     * e.g.: [
+     * 'varToReplace': [name: 'newVar', type: TypeOfVar],
+     * 'varToReplace2': [name: 'newVar2', type: TypeOfVar2],
+     * ]
+     */
+    private Map<String, Map> nameAndTypeMapping = new LinkedHashMap<>();
+    private VariableReplacedListener listener = VariableReplacedListener.NULL;
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.groovy
deleted file mode 100644
index 9dbb801..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.groovy
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.AutoFinal
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.ASTNode
-import org.codehaus.groovy.ast.CodeVisitorSupport
-import org.codehaus.groovy.ast.expr.BinaryExpression
-import org.codehaus.groovy.ast.expr.BooleanExpression
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.ExpressionTransformer
-import org.codehaus.groovy.ast.expr.VariableExpression
-import org.codehaus.groovy.ast.stmt.AssertStatement
-import org.codehaus.groovy.ast.stmt.CaseStatement
-import org.codehaus.groovy.ast.stmt.DoWhileStatement
-import org.codehaus.groovy.ast.stmt.ExpressionStatement
-import org.codehaus.groovy.ast.stmt.ForStatement
-import org.codehaus.groovy.ast.stmt.IfStatement
-import org.codehaus.groovy.ast.stmt.ReturnStatement
-import org.codehaus.groovy.ast.stmt.SwitchStatement
-import org.codehaus.groovy.ast.stmt.SynchronizedStatement
-import org.codehaus.groovy.ast.stmt.ThrowStatement
-import org.codehaus.groovy.ast.stmt.WhileStatement
-import org.codehaus.groovy.ast.tools.GeneralUtils
-
-import java.lang.reflect.Method
-
-/**
- * Tool for replacing VariableExpression instances in an AST by other VariableExpression instances.
- * Regardless of a real change taking place in nested expressions, all considered expression (trees) will be replaced.
- * This could be optimized to accelerate compilation.
- *
- * Within @TailRecursive it is used
- * - to swap the access of method args with the access to iteration variables
- * - to swap the access of iteration variables with the access of temp vars
- */
-@AutoFinal @CompileStatic
-class VariableExpressionReplacer extends CodeVisitorSupport {
-
-    Closure<Boolean> when = { VariableExpression node -> false }
-    Closure<VariableExpression> replaceWith = { VariableExpression variableExpression -> variableExpression }
-
-    private ExpressionTransformer transformer
-
-    synchronized void replaceIn(ASTNode root) {
-        transformer = new VariableExpressionTransformer(when: when, replaceWith: replaceWith)
-        root.visit(this)
-    }
-
-    void visitReturnStatement(ReturnStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitReturnStatement(statement)
-    }
-
-    void visitIfElse(IfStatement ifElse) {
-        replaceExpressionPropertyWhenNecessary(ifElse, 'booleanExpression', BooleanExpression)
-        super.visitIfElse(ifElse)
-    }
-
-    void visitForLoop(ForStatement forLoop) {
-        replaceExpressionPropertyWhenNecessary(forLoop, 'collectionExpression')
-        super.visitForLoop(forLoop)
-    }
-
-    /**
-     * It's the only Expression type in which replacing is considered.
-     * That's an abuse of the class, but I couldn't think of a better way.
-     */
-    void visitBinaryExpression(BinaryExpression expression) {
-        //A hack: Only replace right expression b/c ReturnStatementToIterationConverter needs it that way :-/
-        replaceExpressionPropertyWhenNecessary(expression, 'rightExpression')
-        expression.rightExpression.visit(this)
-        super.visitBinaryExpression(expression)
-    }
-
-    void visitWhileLoop(WhileStatement loop) {
-        replaceExpressionPropertyWhenNecessary(loop, 'booleanExpression', BooleanExpression)
-        super.visitWhileLoop(loop)
-    }
-
-    void visitDoWhileLoop(DoWhileStatement loop) {
-        replaceExpressionPropertyWhenNecessary(loop, 'booleanExpression', BooleanExpression)
-        super.visitDoWhileLoop(loop)
-    }
-
-    void visitSwitch(SwitchStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitSwitch(statement)
-    }
-
-    void visitCaseStatement(CaseStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitCaseStatement(statement)
-    }
-
-    void visitExpressionStatement(ExpressionStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitExpressionStatement(statement)
-    }
-
-    void visitThrowStatement(ThrowStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitThrowStatement(statement)
-    }
-
-    void visitAssertStatement(AssertStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement, 'booleanExpression', BooleanExpression)
-        replaceExpressionPropertyWhenNecessary(statement, 'messageExpression')
-        super.visitAssertStatement(statement)
-    }
-
-    void visitSynchronizedStatement(SynchronizedStatement statement) {
-        replaceExpressionPropertyWhenNecessary(statement)
-        super.visitSynchronizedStatement(statement)
-    }
-
-    private void replaceExpressionPropertyWhenNecessary(ASTNode node, String propName = 'expression', Class propClass = Expression) {
-        Expression expr = getExpression(node, propName)
-
-        if (expr instanceof VariableExpression) {
-            if (when(expr)) {
-                VariableExpression newExpr = replaceWith(expr)
-                replaceExpression(node, propName, propClass, expr, newExpr)
-            }
-        } else {
-            Expression newExpr = transformer.transform(expr)
-            replaceExpression(node, propName, propClass, expr, newExpr)
-        }
-    }
-
-    private void replaceExpression(ASTNode node, String propName, Class propClass, Expression oldExpr, Expression newExpr) {
-        //Use reflection to enable CompileStatic
-        String setterName = GeneralUtils.getSetterName(propName)
-        Method setExpressionMethod = node.class.getMethod(setterName, propClass)
-        newExpr.copyNodeMetaData(oldExpr)
-        newExpr.setSourcePosition(oldExpr)
-        setExpressionMethod.invoke(node, newExpr)
-    }
-
-    private Expression getExpression(ASTNode node, String propName) {
-        //Use reflection to enable CompileStatic
-        String getterName = GeneralUtils.getGetterName(propName)
-        Method getExpressionMethod = node.class.getMethod(getterName)
-        getExpressionMethod.invoke(node) as Expression
-    }
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.java
new file mode 100644
index 0000000..14aa777
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.java
@@ -0,0 +1,219 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import groovy.lang.Closure;
+import org.apache.groovy.internal.util.UncheckedThrow;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.ast.tools.GeneralUtils;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.lang.reflect.Method;
+
+/**
+ * Tool for replacing VariableExpression instances in an AST by other VariableExpression instances.
+ * Regardless of a real change taking place in nested expressions, all considered expression (trees) will be replaced.
+ * This could be optimized to accelerate compilation.
+ * <p>
+ * Within @TailRecursive it is used
+ * - to swap the access of method args with the access to iteration variables
+ * - to swap the access of iteration variables with the access of temp vars
+ */
+class VariableExpressionReplacer extends CodeVisitorSupport {
+    VariableExpressionReplacer(Closure<Boolean> when, Closure<VariableExpression> replaceWith) {
+        this.when = when;
+        this.replaceWith = replaceWith;
+    }
+
+    @Override
+    public void visitReturnStatement(final ReturnStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitReturnStatement(statement);
+    }
+
+    @Override
+    public void visitIfElse(final IfStatement ifElse) {
+        replaceExpressionPropertyWhenNecessary(ifElse, "booleanExpression", BooleanExpression.class);
+        super.visitIfElse(ifElse);
+    }
+
+    @Override
+    public void visitForLoop(final ForStatement forLoop) {
+        replaceExpressionPropertyWhenNecessary(forLoop, "collectionExpression");
+        super.visitForLoop(forLoop);
+    }
+
+    /**
+     * It's the only Expression type in which replacing is considered.
+     * That's an abuse of the class, but I couldn't think of a better way.
+     */
+    @Override
+    public void visitBinaryExpression(final BinaryExpression expression) {
+        //A hack: Only replace right expression b/c ReturnStatementToIterationConverter needs it that way :-/
+        replaceExpressionPropertyWhenNecessary(expression, "rightExpression");
+        expression.getRightExpression().visit(this);
+        super.visitBinaryExpression(expression);
+    }
+
+    @Override
+    public void visitWhileLoop(final WhileStatement loop) {
+        replaceExpressionPropertyWhenNecessary(loop, "booleanExpression", BooleanExpression.class);
+        super.visitWhileLoop(loop);
+    }
+
+    @Override
+    public void visitDoWhileLoop(final DoWhileStatement loop) {
+        replaceExpressionPropertyWhenNecessary(loop, "booleanExpression", BooleanExpression.class);
+        super.visitDoWhileLoop(loop);
+    }
+
+    @Override
+    public void visitSwitch(final SwitchStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitSwitch(statement);
+    }
+
+    @Override
+    public void visitCaseStatement(final CaseStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitCaseStatement(statement);
+    }
+
+    @Override
+    public void visitExpressionStatement(final ExpressionStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitExpressionStatement(statement);
+    }
+
+    @Override
+    public void visitThrowStatement(final ThrowStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitThrowStatement(statement);
+    }
+
+    @Override
+    public void visitAssertStatement(final AssertStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement, "booleanExpression", BooleanExpression.class);
+        replaceExpressionPropertyWhenNecessary(statement, "messageExpression");
+        super.visitAssertStatement(statement);
+    }
+
+    @Override
+    public void visitSynchronizedStatement(final SynchronizedStatement statement) {
+        replaceExpressionPropertyWhenNecessary(statement);
+        super.visitSynchronizedStatement(statement);
+    }
+
+    public synchronized void replaceIn(final ASTNode root) {
+        transformer = new VariableExpressionTransformer(when, replaceWith);
+        root.visit(this);
+    }
+
+    private void replaceExpressionPropertyWhenNecessary(final ASTNode node, final String propName, final Class propClass) {
+        Expression expr = getExpression(node, propName);
+
+        if (expr instanceof VariableExpression) {
+            if (when.call(expr)) {
+                VariableExpression newExpr = replaceWith.call(expr);
+                replaceExpression(node, propName, propClass, expr, newExpr);
+            }
+        } else {
+            Expression newExpr = transformer.transform(expr);
+            replaceExpression(node, propName, propClass, expr, newExpr);
+        }
+    }
+
+    private void replaceExpressionPropertyWhenNecessary(final ASTNode node, final String propName) {
+        replaceExpressionPropertyWhenNecessary(node, propName, Expression.class);
+    }
+
+    private void replaceExpressionPropertyWhenNecessary(final ASTNode node) {
+        replaceExpressionPropertyWhenNecessary(node, "expression", Expression.class);
+    }
+
+    private void replaceExpression(final ASTNode node, final String propName, final Class propClass, final Expression oldExpr, final Expression newExpr) {
+        try {
+            //Use reflection to enable CompileStatic
+            String setterName = GeneralUtils.getSetterName(propName);
+            Method setExpressionMethod = node.getClass().getMethod(setterName, propClass);
+            newExpr.copyNodeMetaData(oldExpr);
+            newExpr.setSourcePosition(oldExpr);
+            setExpressionMethod.invoke(node, newExpr);
+        } catch (Throwable t) {
+            UncheckedThrow.rethrow(t);
+        }
+    }
+
+    private Expression getExpression(final ASTNode node, final String propName) {
+        try {
+            //Use reflection to enable CompileStatic
+            String getterName = GeneralUtils.getGetterName(propName);
+            Method getExpressionMethod = node.getClass().getMethod(getterName);
+            return DefaultGroovyMethods.asType(getExpressionMethod.invoke(node), Expression.class);
+        } catch (Throwable t) {
+            UncheckedThrow.rethrow(t);
+            return null;
+        }
+    }
+
+    public Closure<Boolean> getWhen() {
+        return when;
+    }
+
+    public void setWhen(Closure<Boolean> when) {
+        this.when = when;
+    }
+
+    public Closure<VariableExpression> getReplaceWith() {
+        return replaceWith;
+    }
+
+    public void setReplaceWith(Closure<VariableExpression> replaceWith) {
+        this.replaceWith = replaceWith;
+    }
+
+    private Closure<Boolean> when = new Closure<Boolean>(this, this) {
+        public Boolean doCall(final VariableExpression node) {
+            return false;
+        }
+    };
+    private Closure<VariableExpression> replaceWith = new Closure<VariableExpression>(this, this) {
+        public VariableExpression doCall(final VariableExpression variableExpression) {
+            return variableExpression;
+        }
+    };
+    private ExpressionTransformer transformer;
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.groovy b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.groovy
deleted file mode 100644
index 9683bc9..0000000
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.groovy
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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
- *
- *    http://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.codehaus.groovy.transform.tailrec
-
-import groovy.transform.CompileStatic
-import org.codehaus.groovy.ast.expr.Expression
-import org.codehaus.groovy.ast.expr.ExpressionTransformer
-import org.codehaus.groovy.ast.expr.VariableExpression
-
-/**
- * An expression transformer used in the process of replacing the access to variables
- */
-@CompileStatic
-class VariableExpressionTransformer implements ExpressionTransformer {
-
-    Closure<Boolean> when
-    Closure<VariableExpression> replaceWith
-
-    @Override
-    @SuppressWarnings('Instanceof')
-    Expression transform(Expression expr) {
-        if ((expr instanceof VariableExpression) && when(expr)) {
-            VariableExpression newExpr = replaceWith(expr)
-            newExpr.sourcePosition = expr
-            newExpr.copyNodeMetaData(expr)
-            return newExpr
-        }
-        expr.transformExpression(this)
-    }
-}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.java
new file mode 100644
index 0000000..78a8360
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.java
@@ -0,0 +1,66 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+
+/**
+ * An expression transformer used in the process of replacing the access to variables
+ */
+class VariableExpressionTransformer implements ExpressionTransformer {
+    public VariableExpressionTransformer(Closure<Boolean> when, Closure<VariableExpression> replaceWith) {
+        this.when = when;
+        this.replaceWith = replaceWith;
+    }
+
+    @Override
+    @SuppressWarnings("Instanceof")
+    public Expression transform(Expression expr) {
+        if ((expr instanceof VariableExpression) && when.call(expr)) {
+            VariableExpression newExpr = replaceWith.call(expr);
+            newExpr.setSourcePosition(expr);
+            newExpr.copyNodeMetaData(expr);
+            return newExpr;
+        }
+
+        return expr.transformExpression(this);
+    }
+
+    public Closure<Boolean> getWhen() {
+        return when;
+    }
+
+    public void setWhen(Closure<Boolean> when) {
+        this.when = when;
+    }
+
+    public Closure<VariableExpression> getReplaceWith() {
+        return replaceWith;
+    }
+
+    public void setReplaceWith(Closure<VariableExpression> replaceWith) {
+        this.replaceWith = replaceWith;
+    }
+
+    private Closure<Boolean> when;
+    private Closure<VariableExpression> replaceWith;
+}
diff --git a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableReplacedListener.java b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableReplacedListener.java
new file mode 100644
index 0000000..37da93b
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableReplacedListener.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *    http://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.codehaus.groovy.transform.tailrec;
+
+import org.codehaus.groovy.ast.expr.VariableExpression;
+
+interface VariableReplacedListener {
+    VariableReplacedListener NULL = (oldVar, newVar) -> {
+        //do nothing
+    };
+
+    void variableReplaced(VariableExpression oldVar, VariableExpression newVar);
+}
diff --git a/src/test/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacerTest.groovy b/src/test/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacerTest.groovy
index cca4408..9ae4559 100644
--- a/src/test/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacerTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacerTest.groovy
@@ -53,7 +53,7 @@ class VariableExpressionReplacerTest {
 
     @Before
     void init() {
-        replacer = new VariableExpressionReplacer(when: when, replaceWith: replaceWith)
+        replacer = new VariableExpressionReplacer(when, replaceWith)
     }
 
     @Test