You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2015/10/07 21:26:22 UTC

[02/37] incubator-groovy git commit: shared code between MacroGroovy and AstBuilderTransformation

shared code between MacroGroovy and AstBuilderTransformation


Project: http://git-wip-us.apache.org/repos/asf/incubator-groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-groovy/commit/546868aa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-groovy/tree/546868aa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-groovy/diff/546868aa

Branch: refs/heads/master
Commit: 546868aaae30335b9b6fc1a67bb6cf1dc2b84cff
Parents: 0b5f0b0
Author: Sergey Egorov <bs...@gmail.com>
Authored: Thu Jul 3 12:45:29 2014 +0300
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:32:03 2015 +0300

----------------------------------------------------------------------
 .../groovy/ast/MethodCallTransformation.java    | 113 ++++++++++++
 .../groovy/ast/MethodInvocationTrap.java        |  94 ++++++++++
 .../ast/builder/AstBuilderTransformation.java   | 181 +++----------------
 .../macro/transform/MacroInvocationTrap.java    | 148 +++++----------
 .../macro/transform/MacroTransformation.java    |  41 +----
 5 files changed, 289 insertions(+), 288 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/546868aa/src/main/org/codehaus/groovy/ast/MethodCallTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/MethodCallTransformation.java b/src/main/org/codehaus/groovy/ast/MethodCallTransformation.java
new file mode 100644
index 0000000..85c6f2c
--- /dev/null
+++ b/src/main/org/codehaus/groovy/ast/MethodCallTransformation.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     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.ast;
+
+import groovy.lang.MissingPropertyException;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.ASTTransformation;
+
+/**
+ *
+ * @author Hamlet D'Arcy
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+
+public abstract class MethodCallTransformation implements ASTTransformation {
+
+    public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+
+        GroovyCodeVisitor transformer = getTransformer(nodes, sourceUnit);
+
+        if (nodes != null) {
+            for (ASTNode it : nodes) {
+                if (!(it instanceof AnnotationNode) && !(it instanceof ClassNode)) {
+                    it.visit(transformer);
+                }
+            }
+        }
+        if (sourceUnit.getAST() != null) {
+            sourceUnit.getAST().visit(transformer);
+            if (sourceUnit.getAST().getStatementBlock() != null) {
+                sourceUnit.getAST().getStatementBlock().visit(transformer);
+            }
+            if (sourceUnit.getAST().getClasses() != null) {
+                for (ClassNode classNode : sourceUnit.getAST().getClasses()) {
+                    if (classNode.getMethods() != null) {
+                        for (MethodNode node : classNode.getMethods()) {
+                            if (node != null && node.getCode() != null) {
+                                node.getCode().visit(transformer);
+                            }
+                        }
+                    }
+
+                    try {
+                        if (classNode.getDeclaredConstructors() != null) {
+                            for (MethodNode node : classNode.getDeclaredConstructors()) {
+                                if (node != null && node.getCode() != null) {
+                                    node.getCode().visit(transformer);
+                                }
+                            }
+                        }
+                    } catch (MissingPropertyException ignored) {
+                        // todo: inner class nodes don't have a constructors field available
+                    }
+
+                    // all properties are also always fields
+                    if (classNode.getFields() != null) {
+                        for (FieldNode node : classNode.getFields()) {
+                            if (node.getInitialValueExpression() != null) {
+                                node.getInitialValueExpression().visit(transformer);
+                            }
+                        }
+                    }
+
+                    try {
+                        if (classNode.getObjectInitializerStatements() != null) {
+                            for (Statement node : classNode.getObjectInitializerStatements()) {
+                                if (node != null) {
+                                    node.visit(transformer);
+                                }
+                            }
+                        }
+                    } catch (MissingPropertyException ignored) {
+                        // todo: inner class nodes don't have a objectInitializers field available
+                    }
+
+                    // todo: is there anything to do with the module ???
+                }
+            }
+            if (sourceUnit.getAST().getMethods() != null) {
+                for (MethodNode node : sourceUnit.getAST().getMethods()) {
+                    if (node != null) {
+                        if (node.getParameters() != null) {
+                            for (Parameter parameter : node.getParameters()) {
+                                if (parameter != null && parameter.getInitialExpression() != null) {
+                                    parameter.getInitialExpression().visit(transformer);
+                                }
+                            }
+                        }
+                        if (node.getCode() != null) {
+                            node.getCode().visit(transformer);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    
+    protected abstract GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit);
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/546868aa/src/main/org/codehaus/groovy/ast/MethodInvocationTrap.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/MethodInvocationTrap.java b/src/main/org/codehaus/groovy/ast/MethodInvocationTrap.java
new file mode 100644
index 0000000..6d51a5c
--- /dev/null
+++ b/src/main/org/codehaus/groovy/ast/MethodInvocationTrap.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2003-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     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.ast;
+
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.tools.ClosureUtils;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+/**
+ *
+ * @author Hamlet D'Arcy
+ * @author Sergei Egorov <bs...@gmail.com>
+ */
+public abstract class MethodInvocationTrap extends CodeVisitorSupport {
+
+    protected final ReaderSource source;
+    protected final SourceUnit sourceUnit;
+
+    public MethodInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
+        if (source == null) throw new IllegalArgumentException("Null: source");
+        if (sourceUnit == null) throw new IllegalArgumentException("Null: sourceUnit");
+        this.source = source;
+        this.sourceUnit = sourceUnit;
+    }
+
+    /**
+     * Attempts to find AstBuilder 'from code' invocations. When found, converts them into calls
+     * to the 'from string' approach.
+     *
+     * @param call the method call expression that may or may not be an AstBuilder 'from code' invocation.
+     */
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        boolean shouldContinueWalking = true;
+        
+        if (isBuildInvocation(call)) {
+            shouldContinueWalking = handleTargetMethodCallExpression(call);
+        }
+        
+        if(shouldContinueWalking) {
+            // continue normal tree walking
+            call.getObjectExpression().visit(this);
+            call.getMethod().visit(this);
+            call.getArguments().visit(this);
+        }
+    }
+
+    /**
+     * Reports an error back to the source unit.
+     *
+     * @param msg  the error message
+     * @param expr the expression that caused the error message.
+     */
+    protected void addError(String msg, ASTNode expr) {
+        sourceUnit.getErrorCollector().addErrorAndContinue(
+                new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)
+        );
+    }
+
+    /**
+     * Converts a ClosureExpression into the String source.
+     *
+     * @param expression a closure
+     * @return the source the closure was created from
+     */
+    protected String convertClosureToSource(ClosureExpression expression) {
+        try {
+            return ClosureUtils.convertClosureToSource(source, expression);
+        } catch(Exception e) {
+            addError(e.getMessage(), expression);
+        }
+        return null;
+    }
+
+    protected abstract boolean handleTargetMethodCallExpression(MethodCallExpression call);
+
+    protected abstract boolean isBuildInvocation(MethodCallExpression call);
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/546868aa/src/main/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java b/src/main/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java
index 20a4aea..4143e34 100644
--- a/src/main/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java
+++ b/src/main/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java
@@ -18,17 +18,11 @@
  */
 package org.codehaus.groovy.ast.builder;
 
-import groovy.lang.MissingPropertyException;
 import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.ast.expr.*;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.tools.ClosureUtils;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.io.ReaderSource;
-import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
-import org.codehaus.groovy.syntax.SyntaxException;
-import org.codehaus.groovy.transform.ASTTransformation;
 import org.codehaus.groovy.transform.GroovyASTTransformation;
 
 import java.util.ArrayList;
@@ -49,92 +43,17 @@ import java.util.List;
  */
 
 @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
-public class AstBuilderTransformation implements ASTTransformation {
-
-    public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+public class AstBuilderTransformation extends MethodCallTransformation {
 
+    @Override
+    protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
         // todo : are there other import types that can be specified?
-        AstBuilderInvocationTrap transformer = new AstBuilderInvocationTrap(
-                sourceUnit.getAST().getImports(),
-                sourceUnit.getAST().getStarImports(),
-                sourceUnit.getSource(),
-                sourceUnit
+        return new AstBuilderInvocationTrap(
+            sourceUnit.getAST().getImports(),
+            sourceUnit.getAST().getStarImports(),
+            sourceUnit.getSource(),
+            sourceUnit
         );
-        if (nodes != null) {
-            for (ASTNode it : nodes) {
-                if (!(it instanceof AnnotationNode) && !(it instanceof ClassNode)) {
-                    it.visit(transformer);
-                }
-            }
-        }
-        if (sourceUnit.getAST() != null) {
-            sourceUnit.getAST().visit(transformer);
-            if (sourceUnit.getAST().getStatementBlock() != null) {
-                sourceUnit.getAST().getStatementBlock().visit(transformer);
-            }
-            if (sourceUnit.getAST().getClasses() != null) {
-                for (ClassNode classNode : sourceUnit.getAST().getClasses()) {
-                    if (classNode.getMethods() != null) {
-                        for (MethodNode node : classNode.getMethods()) {
-                            if (node != null && node.getCode() != null) {
-                                node.getCode().visit(transformer);
-                            }
-                        }
-                    }
-
-                    try {
-                        if (classNode.getDeclaredConstructors() != null) {
-                            for (MethodNode node : classNode.getDeclaredConstructors()) {
-                                if (node != null && node.getCode() != null) {
-                                    node.getCode().visit(transformer);
-                                }
-                            }
-                        }
-                    } catch (MissingPropertyException ignored) {
-                        // todo: inner class nodes don't have a constructors field available
-                    }
-
-                    // all properties are also always fields
-                    if (classNode.getFields() != null) {
-                        for (FieldNode node : classNode.getFields()) {
-                            if (node.getInitialValueExpression() != null) {
-                                node.getInitialValueExpression().visit(transformer);
-                            }
-                        }
-                    }
-
-                    try {
-                        if (classNode.getObjectInitializerStatements() != null) {
-                            for (Statement node : classNode.getObjectInitializerStatements()) {
-                                if (node != null) {
-                                    node.visit(transformer);
-                                }
-                            }
-                        }
-                    } catch (MissingPropertyException ignored) {
-                        // todo: inner class nodes don't have a objectInitializers field available
-                    }
-
-                    // todo: is there anything to do with the module ???
-                }
-            }
-            if (sourceUnit.getAST().getMethods() != null) {
-                for (MethodNode node : sourceUnit.getAST().getMethods()) {
-                    if (node != null) {
-                        if (node.getParameters() != null) {
-                            for (Parameter parameter : node.getParameters()) {
-                                if (parameter != null && parameter.getInitialExpression() != null) {
-                                    parameter.getInitialExpression().visit(transformer);
-                                }
-                            }
-                        }
-                        if (node.getCode() != null) {
-                            node.getCode().visit(transformer);
-                        }
-                    }
-                }
-            }
-        }
     }
 
     /**
@@ -142,11 +61,9 @@ public class AstBuilderTransformation implements ASTTransformation {
      * the contents of the closure into expressions by reading the source of the Closure and sending
      * that as a String to AstBuilder.build(String, CompilePhase, boolean) at runtime.
      */
-    private static class AstBuilderInvocationTrap extends CodeVisitorSupport {
+    private static class AstBuilderInvocationTrap extends MethodInvocationTrap {
 
         private final List<String> factoryTargets = new ArrayList<String>();
-        private final ReaderSource source;
-        private final SourceUnit sourceUnit;
 
         /**
          * Creates the trap and captures all the ways in which a class may be referenced via imports.
@@ -157,10 +74,7 @@ public class AstBuilderTransformation implements ASTTransformation {
          * @param sourceUnit     the source unit being compiled. Used for error messages.
          */
         AstBuilderInvocationTrap(List<ImportNode> imports, List<ImportNode> importPackages, ReaderSource source, SourceUnit sourceUnit) {
-            if (source == null) throw new IllegalArgumentException("Null: source");
-            if (sourceUnit == null) throw new IllegalArgumentException("Null: sourceUnit");
-            this.source = source;
-            this.sourceUnit = sourceUnit;
+            super(source, sourceUnit);
 
             // factory type may be references as fully qualified, an import, or an alias
             factoryTargets.add("org.codehaus.groovy.ast.builder.AstBuilder");//default package
@@ -182,47 +96,22 @@ public class AstBuilderTransformation implements ASTTransformation {
                 }
             }
         }
-
-        /**
-         * Reports an error back to the source unit.
-         *
-         * @param msg  the error message
-         * @param expr the expression that caused the error message.
-         */
-        private void addError(String msg, ASTNode expr) {
-            sourceUnit.getErrorCollector().addErrorAndContinue(
-                    new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)
-            );
-        }
-
-
-        /**
-         * Attempts to find AstBuilder 'from code' invocations. When found, converts them into calls
-         * to the 'from string' approach.
-         *
-         * @param call the method call expression that may or may not be an AstBuilder 'from code' invocation.
-         */
-        public void visitMethodCallExpression(MethodCallExpression call) {
-
-            if (isBuildInvocation(call)) {
-
-                ClosureExpression closureExpression = getClosureArgument(call);
-                List<Expression> otherArgs = getNonClosureArguments(call);
-                String source = convertClosureToSource(closureExpression);
-
-                // parameter order is build(CompilePhase, boolean, String)
-                otherArgs.add(new ConstantExpression(source));
-                call.setArguments(new ArgumentListExpression(otherArgs));
-                call.setMethod(new ConstantExpression("buildFromBlock"));
-                call.setSpreadSafe(false);
-                call.setSafe(false);
-                call.setImplicitThis(false);
-            } else {
-                // continue normal tree walking
-                call.getObjectExpression().visit(this);
-                call.getMethod().visit(this);
-                call.getArguments().visit(this);
-            }
+        
+        @Override
+        protected boolean handleTargetMethodCallExpression(MethodCallExpression call) {
+            ClosureExpression closureExpression = getClosureArgument(call);
+            List<Expression> otherArgs = getNonClosureArguments(call);
+            String source = convertClosureToSource(closureExpression);
+
+            // parameter order is build(CompilePhase, boolean, String)
+            otherArgs.add(new ConstantExpression(source));
+            call.setArguments(new ArgumentListExpression(otherArgs));
+            call.setMethod(new ConstantExpression("buildFromBlock"));
+            call.setSpreadSafe(false);
+            call.setSafe(false);
+            call.setImplicitThis(false);
+            
+            return false;
         }
 
         private List<Expression> getNonClosureArguments(MethodCallExpression call) {
@@ -255,7 +144,8 @@ public class AstBuilderTransformation implements ASTTransformation {
          *
          * @param call the method call expression, may not be null
          */
-        private boolean isBuildInvocation(MethodCallExpression call) {
+        @Override
+        protected boolean isBuildInvocation(MethodCallExpression call) {
             if (call == null) throw new IllegalArgumentException("Null: call");
 
             // is method name correct?
@@ -281,21 +171,6 @@ public class AstBuilderTransformation implements ASTTransformation {
             }
             return false;
         }
-
-        /**
-         * Converts a ClosureExpression into the String source.
-         *
-         * @param expression a closure
-         * @return the source the closure was created from
-         */
-        private String convertClosureToSource(ClosureExpression expression) {
-            try {
-                return ClosureUtils.convertClosureToSource(source, expression);
-            } catch(Exception e) {
-                addError(e.getMessage(), expression);
-            }
-            return null;
-        }
     }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/546868aa/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
index 7ac10df..b082e8b 100644
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
@@ -15,18 +15,15 @@
  */
 package org.codehaus.groovy.macro.transform;
 
-import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.ClassHelper;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.MethodInvocationTrap;
 import org.codehaus.groovy.ast.expr.*;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.tools.ClosureUtils;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.io.ReaderSource;
-import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
 import org.codehaus.groovy.macro.runtime.MacroBuilder;
 import org.codehaus.groovy.macro.runtime.MacroSubstitutionKey;
-import org.codehaus.groovy.syntax.SyntaxException;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -37,106 +34,73 @@ import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
  *
  * @author Sergei Egorov <bs...@gmail.com>
  */
-public class MacroInvocationTrap {
-
-    private final ReaderSource source;
-    private final SourceUnit sourceUnit;
-
-    /**
-     * Creates the trap and captures all macro method calls.
-     *
-     * @param source         the reader source that contains source for the SourceUnit
-     * @param sourceUnit     the source unit being compiled. Used for error messages.
-     */
-    MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
-        if (source == null) throw new IllegalArgumentException("Null: source");
-        if (sourceUnit == null) throw new IllegalArgumentException("Null: sourceUnit");
-        this.source = source;
-        this.sourceUnit = sourceUnit;
-    }
+public class MacroInvocationTrap extends MethodInvocationTrap {
 
-    /**
-     * Reports an error back to the source unit.
-     *
-     * @param msg  the error message
-     * @param expr the expression that caused the error message.
-     */
-    private void addError(String msg, ASTNode expr) {
-        sourceUnit.getErrorCollector().addErrorAndContinue(
-                new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)
-        );
+    public MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
+        super(source, sourceUnit);
     }
 
-    /**
-     * Attempts to find 'macro' invocations. When found, converts them into calls
-     * to the 'from string' approach.
-     *
-     * @param macroCall the method call expression that may or may not be a 'macro' invocation.
-     */
-    public void visitMethodCallExpression(final MethodCallExpression macroCall) {
-
-        if (!isBuildInvocation(macroCall, MacroTransformation.MACRO_METHOD)) {
-            return;
-        }
-
+    @Override
+    protected boolean handleTargetMethodCallExpression(MethodCallExpression macroCall) {
         final ClosureExpression closureExpression = getClosureArgument(macroCall);
-        
+
         if(closureExpression == null) {
-            return;
+            return true;
         }
 
         if(closureExpression.getParameters() != null && closureExpression.getParameters().length > 0) {
             addError("Macro closure arguments are not allowed", closureExpression);
+            return true;
         }
-        
+
         final MapExpression mapExpression = new MapExpression();
-        
+
         (new CodeVisitorSupport() {
             @Override
             public void visitMethodCallExpression(MethodCallExpression call) {
                 super.visitMethodCallExpression(call);
-                
+
                 if(isBuildInvocation(call, MacroTransformation.DOLLAR_VALUE)) {
                     ClosureExpression substitutionClosureExpression = getClosureArgument(call);
-                    
+
                     if(substitutionClosureExpression == null) {
                         return;
                     }
 
                     MacroSubstitutionKey key = new MacroSubstitutionKey(call, closureExpression.getLineNumber(), closureExpression.getColumnNumber());
-                    
+
                     mapExpression.addMapEntryExpression(key.toConstructorCallExpression(), substitutionClosureExpression);
                 }
             }
         }).visitClosureExpression(closureExpression);
-        
-        String source = convertClosureToSource(this.source, closureExpression);
+
+        String source = convertClosureToSource(closureExpression);
 
         BlockStatement closureBlock = (BlockStatement) closureExpression.getCode();
-        
+
         Boolean asIs = false;
-        
+
         TupleExpression macroArguments = getMacroArguments(macroCall);
-        
+
         if(macroArguments == null) {
-            return;
+            return true;
         }
 
         List<Expression> macroArgumentsExpressions = macroArguments.getExpressions();
-        
+
         if(macroArgumentsExpressions.size() > 1) {
             Expression firstArgument = macroArgumentsExpressions.get(0);
-            
+
             if(!(firstArgument instanceof ConstantExpression)) {
                 addError("AsIs argument value should be constant(true or false)", firstArgument);
-                return;
+                return true;
             }
-            
+
             ConstantExpression asIsConstantExpression = (ConstantExpression) firstArgument;
-            
+
             if(!(asIsConstantExpression.getValue() instanceof Boolean)) {
                 addError("AsIs argument value should be boolean", asIsConstantExpression);
-                return;
+                return true;
             }
 
             asIs = (Boolean) asIsConstantExpression.getValue();
@@ -153,6 +117,29 @@ public class MacroInvocationTrap {
         macroCall.setSpreadSafe(false);
         macroCall.setSafe(false);
         macroCall.setImplicitThis(false);
+        
+        return true;
+    }
+
+    @Override
+    protected boolean isBuildInvocation(MethodCallExpression call) {
+        return isBuildInvocation(call, MacroTransformation.MACRO_METHOD);
+    }
+
+    public static boolean isBuildInvocation(MethodCallExpression call, String methodName) {
+        if (call == null) throw new IllegalArgumentException("Null: call");
+        if(methodName == null) throw new IllegalArgumentException("Null: methodName");
+
+        if(!(call.getMethod() instanceof ConstantExpression)) {
+            return false;
+        }
+
+        if(!(methodName.equals(call.getMethodAsString()))) {
+            return false;
+        }
+
+        // is method object correct type?
+        return call.getObjectExpression() == THIS_EXPRESSION;
     }
     
     protected TupleExpression getMacroArguments(MethodCallExpression call) {
@@ -193,41 +180,4 @@ public class MacroInvocationTrap {
 
         return (ClosureExpression) result;
     }
-
-    /**
-     * Looks for 'macro' method calls.
-     *
-     * @param call the method call expression, may not be null
-     */
-    public static boolean isBuildInvocation(MethodCallExpression call, String methodName) {
-        if (call == null) throw new IllegalArgumentException("Null: call");
-        if(methodName == null) throw new IllegalArgumentException("Null: methodName");
-        
-        if(!(call.getMethod() instanceof ConstantExpression)) {
-            return false;
-        }
-        
-        if(!(methodName.equals(call.getMethodAsString()))) {
-            return false;
-        }
-
-        // is method object correct type?
-        return call.getObjectExpression() == THIS_EXPRESSION;
-    }
-
-    /**
-     * Converts a ClosureExpression into the String source.
-     *
-     * @param expression a closure
-     * @return the source the closure was created from
-     */
-    private String convertClosureToSource(ReaderSource source, ClosureExpression expression) {
-
-        try {
-            return ClosureUtils.convertClosureToSource(source, expression);
-        } catch (Exception e) {
-            addError(e.getMessage(), expression);
-        }
-        return null;
-    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/546868aa/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
index cdf0f07..65284b5 100644
--- a/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
+++ b/subprojects/groovy-macro/src/main/java/org/codehaus/groovy/macro/transform/MacroTransformation.java
@@ -15,56 +15,25 @@
  */
 package org.codehaus.groovy.macro.transform;
 
-import org.codehaus.groovy.ast.ASTNode;
-import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.*;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
-import org.codehaus.groovy.transform.ASTTransformation;
 import org.codehaus.groovy.transform.GroovyASTTransformation;
 
 /**
  *
  * @author Sergei Egorov <bs...@gmail.com>
  */
+
 @GroovyASTTransformation(phase = CompilePhase.CONVERSION)
-public class MacroTransformation extends ClassCodeVisitorSupport implements ASTTransformation {
+public class MacroTransformation extends MethodCallTransformation {
 
     public static final String DOLLAR_VALUE = "$v";
     public static final String MACRO_METHOD = "macro";
 
-    SourceUnit source;
-    
-    MacroInvocationTrap transformer;
-
-    public void visit(ASTNode[] nodes, SourceUnit source) {
-        this.source = source;
-
-        transformer = new MacroInvocationTrap(source.getSource(), source);
-
-        for(ASTNode node : nodes) {
-            if(node instanceof ClassNode) {
-                visitClass((ClassNode) node);
-            } else if(node instanceof ModuleNode) {
-                ModuleNode moduleNode = (ModuleNode) node;
-                for (ClassNode classNode : moduleNode.getClasses()) {
-                    visitClass(classNode);
-                }
-            }
-        }
-    }
-
-    @Override
-    public void visitMethodCallExpression(MethodCallExpression call) {
-        transformer.visitMethodCallExpression(call);
-        super.visitMethodCallExpression(call);
-    }
-
     @Override
-    protected SourceUnit getSourceUnit() {
-        return source;
+    protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
+        return new MacroInvocationTrap(sourceUnit.getSource(), sourceUnit);
     }
 }