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 09:43:43 UTC

[groovy] branch danielsun/rewrite-gsrc-to-jsrc updated (b28c2e0 -> fe875c8)

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 b28c2e0  Rewrite Groovy source code in core to Java
     new fe875c8  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   (b28c2e0)
            \
             N -- N -- N   refs/heads/danielsun/rewrite-gsrc-to-jsrc (fe875c8)

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:
 .../transform/tailrec/GotoRecurHereException.java} | 13 ++--
 .../transform/tailrec/InWhileLoopWrapper.groovy    | 83 ----------------------
 .../transform/tailrec/InWhileLoopWrapper.java      | 60 ++++++++++++++++
 3 files changed, 64 insertions(+), 92 deletions(-)
 copy src/main/{java/groovy/io/FileType.java => groovy/org/codehaus/groovy/transform/tailrec/GotoRecurHereException.java} (74%)
 delete mode 100644 src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.groovy
 create mode 100644 src/main/groovy/org/codehaus/groovy/transform/tailrec/InWhileLoopWrapper.java

[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 fe875c8704d269c824a01b325f9529cbea2b7bde
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 ++-
 .../transform/tailrec/StatementReplacer.groovy     | 106 ----------
 .../transform/tailrec/StatementReplacer.java       | 169 ++++++++++++++++
 .../tailrec/VariableExpressionReplacer.groovy      | 163 ----------------
 .../tailrec/VariableExpressionReplacer.java        | 214 +++++++++++++++++++++
 .../tailrec/VariableExpressionTransformer.groovy   |   6 +-
 17 files changed, 815 insertions(+), 666 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/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/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..e400983
--- /dev/null
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionReplacer.java
@@ -0,0 +1,214 @@
+/*
+ *  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 {
+    @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
index 9683bc9..5aaaff6 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/tailrec/VariableExpressionTransformer.groovy
@@ -28,10 +28,14 @@ import org.codehaus.groovy.ast.expr.VariableExpression
  */
 @CompileStatic
 class VariableExpressionTransformer implements ExpressionTransformer {
-
     Closure<Boolean> when
     Closure<VariableExpression> replaceWith
 
+    VariableExpressionTransformer(Closure<Boolean> when, Closure<VariableExpression> replaceWith) {
+        this.when = when
+        this.replaceWith = replaceWith
+    }
+
     @Override
     @SuppressWarnings('Instanceof')
     Expression transform(Expression expr) {