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:35 UTC

[15/37] incubator-groovy git commit: Add support for generating classes through a MacroClass block * instead of using def ast = macro { class A {} }, use def ast=new MacroClass() { class A{} } * substitutions broken due to incorrect scoping of the

Add support for generating classes through a MacroClass block
   * instead of using def ast = macro { class A {} }, use def ast=new MacroClass() { class A{} }
   * substitutions broken due to incorrect scoping of the closure expression representing the substitution


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

Branch: refs/heads/master
Commit: 09758c34899f09285c8333e20f4e855ab28de2e6
Parents: 248e0fd
Author: Cedric Champeau <ce...@gmail.com>
Authored: Thu Oct 16 12:09:25 2014 +0200
Committer: Sergei Egorov <bs...@gmail.com>
Committed: Mon Sep 28 14:33:03 2015 +0300

----------------------------------------------------------------------
 .../codehaus/groovy/ast/tools/ClosureUtils.java |  24 +-
 .../codehaus/groovy/ast/tools/GeneralUtils.java |  39 +++
 .../groovy/macro/runtime/MacroBuilder.java      |  69 ++--
 .../groovy/macro/transform/MacroClass.java      |  19 ++
 .../macro/transform/MacroInvocationTrap.java    | 160 ++++++---
 .../macro/transform/MacroTransformation.java    |  22 +-
 .../macro/transform/TransformingMacroTrap.java  | 340 +++++++++++++++++++
 7 files changed, 585 insertions(+), 88 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/src/main/org/codehaus/groovy/ast/tools/ClosureUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/tools/ClosureUtils.java b/src/main/org/codehaus/groovy/ast/tools/ClosureUtils.java
index c13c31c..06d4995 100644
--- a/src/main/org/codehaus/groovy/ast/tools/ClosureUtils.java
+++ b/src/main/org/codehaus/groovy/ast/tools/ClosureUtils.java
@@ -37,32 +37,10 @@ public class ClosureUtils {
      * @throws java.lang.Exception when closure can't be read from source
      */
     public static String convertClosureToSource(ReaderSource readerSource, ClosureExpression expression) throws Exception {
-        if (expression == null) throw new IllegalArgumentException("Null: expression");
-
-        StringBuilder result = new StringBuilder();
-        for (int x = expression.getLineNumber(); x <= expression.getLastLineNumber(); x++) {
-            String line = readerSource.getLine(x, null);
-            if (line == null) {
-                throw new Exception(
-                        "Error calculating source code for expression. Trying to read line " + x + " from " + readerSource.getClass()
-                );
-            }
-            if (x == expression.getLastLineNumber()) {
-                line = line.substring(0, expression.getLastColumnNumber() - 1);
-            }
-            if (x == expression.getLineNumber()) {
-                line = line.substring(expression.getColumnNumber() - 1);
-            }
-            //restoring line breaks is important b/c of lack of semicolons
-            result.append(line).append('\n');
-        }
-
-
-        String source = result.toString().trim();
+        String source = GeneralUtils.convertASTToSource(readerSource, expression);
         if (!source.startsWith("{")) {
             throw new Exception("Error converting ClosureExpression into source code. Closures must start with {. Found: " + source);
         }
-
         return source;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java b/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java
index ce4b19e..9d8508f 100644
--- a/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java
+++ b/src/main/org/codehaus/groovy/ast/tools/GeneralUtils.java
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.ast.tools;
 
+import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.ClassHelper;
@@ -53,6 +54,7 @@ import org.codehaus.groovy.ast.stmt.IfStatement;
 import org.codehaus.groovy.ast.stmt.ReturnStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
 import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.io.ReaderSource;
 import org.codehaus.groovy.runtime.GeneratedClosure;
 import org.codehaus.groovy.runtime.MetaClassHelper;
 import org.codehaus.groovy.syntax.Token;
@@ -680,4 +682,41 @@ public class GeneralUtils {
         }
         return propX(receiver, pNode.getName());
     }
+
+    /**
+     * Converts an expression into the String source. Only some specific expressions like closure expression
+     * support this.
+     *
+     * @param readerSource a source
+     * @param expression an expression. Can't be null
+     * @return the source the closure was created from
+     * @throws java.lang.IllegalArgumentException when expression is null
+     * @throws java.lang.Exception when closure can't be read from source
+     */
+    public static String convertASTToSource(ReaderSource readerSource, ASTNode expression) throws Exception {
+        if (expression == null) throw new IllegalArgumentException("Null: expression");
+
+        StringBuilder result = new StringBuilder();
+        for (int x = expression.getLineNumber(); x <= expression.getLastLineNumber(); x++) {
+            String line = readerSource.getLine(x, null);
+            if (line == null) {
+                throw new Exception(
+                        "Error calculating source code for expression. Trying to read line " + x + " from " + readerSource.getClass()
+                );
+            }
+            if (x == expression.getLastLineNumber()) {
+                line = line.substring(0, expression.getLastColumnNumber() - 1);
+            }
+            if (x == expression.getLineNumber()) {
+                line = line.substring(expression.getColumnNumber() - 1);
+            }
+            //restoring line breaks is important b/c of lack of semicolons
+            result.append(line).append('\n');
+        }
+
+
+        String source = result.toString().trim();
+
+        return source;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
index ae82081..1edbcba 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/runtime/MacroBuilder.java
@@ -18,6 +18,7 @@ package org.codehaus.groovy.macro.runtime;
 import groovy.lang.Closure;
 import org.codehaus.groovy.ast.ASTNode;
 import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.builder.AstBuilder;
 import org.codehaus.groovy.ast.expr.Expression;
 import org.codehaus.groovy.ast.expr.MethodCallExpression;
@@ -53,46 +54,64 @@ public enum MacroBuilder {
 
     @SuppressWarnings("unchecked")
     public <T> T macro(CompilePhase compilePhase, boolean asIs, String source, final Map<MacroSubstitutionKey, Closure<Expression>> context, Class<T> resultClass) {
-        final String label = "__synthesized__label__" + System.currentTimeMillis() + "__:";
+        boolean isClosure = source.startsWith("{");
+        final String label = isClosure ?"__synthesized__label__" + System.currentTimeMillis() + "__:":"";
         final String labelledSource = label + source;
         final int linesOffset = 1;
-        final int columnsOffset = label.length() + 1; // +1 because of {
-        
+        final int columnsOffset = label.length() + (isClosure?1:0); // +1 because of {
+
         List<ASTNode> nodes = (new AstBuilder()).buildFromString(compilePhase, true, labelledSource);
 
         for(ASTNode node : nodes) {
             if (node instanceof BlockStatement) {
 
-                BlockStatement closureBlock = (BlockStatement) ((BlockStatement)node).getStatements().get(0);
+                List<Statement> statements = ((BlockStatement) node).getStatements();
+                if (!statements.isEmpty()) {
+                    BlockStatement closureBlock = (BlockStatement) statements.get(0);
 
-                (new ClassCodeExpressionTransformer() {
-                    public Expression transform(Expression expression) {
-                        if(!(expression instanceof MethodCallExpression)) {
-                            return super.transform(expression);
-                        }
+                    performSubstitutions(linesOffset, columnsOffset, context, closureBlock);
 
-                        MethodCallExpression call = (MethodCallExpression) expression;
 
-                        if(!MacroInvocationTrap.isBuildInvocation(call, MacroTransformation.DOLLAR_VALUE)) {
-                            return super.transform(expression);
-                        }
+                    return (T) getMacroValue(closureBlock, asIs);
+                }
+            }
+            if (node instanceof ClassNode) {
+                performSubstitutions(linesOffset, columnsOffset, context, node);
+                return (T) node;
+            }
+        }
+        return null;
+    }
 
-                        MacroSubstitutionKey key = new MacroSubstitutionKey(call, linesOffset, columnsOffset);
-                        
-                        return context.get(key).call();
-                    }
+    private void performSubstitutions(final int linesOffset, final int columnsOffset, final Map<MacroSubstitutionKey, Closure<Expression>> context, final ASTNode astNode) {
+        ClassCodeExpressionTransformer trn = new ClassCodeExpressionTransformer() {
+            public Expression transform(Expression expression) {
+                if (!(expression instanceof MethodCallExpression)) {
+                    return super.transform(expression);
+                }
 
-                    @Override
-                    protected SourceUnit getSourceUnit() {
-                        // Could be null if there are no errors
-                        return null;
-                    }
-                }).visitBlockStatement(closureBlock);
+                MethodCallExpression call = (MethodCallExpression) expression;
 
-                return (T) getMacroValue(closureBlock, asIs);
+                if (!MacroInvocationTrap.isBuildInvocation(call, MacroTransformation.DOLLAR_VALUE)) {
+                    return super.transform(expression);
+                }
+
+                MacroSubstitutionKey key = new MacroSubstitutionKey(call, linesOffset, columnsOffset);
+
+                return context.get(key).call();
             }
+
+            @Override
+            protected SourceUnit getSourceUnit() {
+                // Could be null if there are no errors
+                return null;
+            }
+        };
+        if (astNode instanceof BlockStatement) {
+            trn.visitBlockStatement((BlockStatement) astNode);
+        } else if (astNode instanceof ClassNode) {
+            trn.visitClass((ClassNode) astNode);
         }
-        return null;
     }
 
     public static ASTNode getMacroValue(BlockStatement closureBlock, boolean asIs) {

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroClass.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroClass.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroClass.java
new file mode 100644
index 0000000..46dc062
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroClass.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2003-2014 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.macro.transform;
+
+public abstract class MacroClass {
+}

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
index 127fb4b..5a7c275 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroInvocationTrap.java
@@ -15,64 +15,104 @@
  */
 package org.codehaus.groovy.macro.transform;
 
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
 import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
 import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodInvocationTrap;
-import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
 import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.tools.GeneralUtils;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.io.ReaderSource;
 import org.codehaus.groovy.macro.runtime.MacroBuilder;
 import org.codehaus.groovy.macro.runtime.MacroSubstitutionKey;
 
-import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 
 import static org.codehaus.groovy.ast.expr.VariableExpression.THIS_EXPRESSION;
 
 /**
- *
  * @author Sergei Egorov <bs...@gmail.com>
  */
 public class MacroInvocationTrap extends MethodInvocationTrap {
 
+    private final static ClassNode MACROCLASS_TYPE = ClassHelper.make(MacroClass.class);
+    private final ReaderSource readerSource;
+
     public MacroInvocationTrap(ReaderSource source, SourceUnit sourceUnit) {
         super(source, sourceUnit);
+        this.readerSource = source;
+    }
+
+    @Override
+    public void visitConstructorCallExpression(final ConstructorCallExpression call) {
+        ClassNode type = call.getType();
+        if (type instanceof InnerClassNode) {
+            if (((InnerClassNode) type).isAnonymous() &&
+                    MACROCLASS_TYPE.getNameWithoutPackage().equals(type.getSuperClass().getNameWithoutPackage())) {
+                //System.out.println("call = " + call.getText());
+                try {
+                    String source = convertInnerClassToSource(type);
+                    List<Expression> macroArgumentsExpressions = new LinkedList<Expression>();
+                    macroArgumentsExpressions.add(new ConstantExpression(source));
+                    macroArgumentsExpressions.add(buildSubstitutionMap(type));
+                    macroArgumentsExpressions.add(new ClassExpression(ClassHelper.make(ClassNode.class)));
+
+                    MethodCallExpression macroCall = new MethodCallExpression(
+                            new PropertyExpression(new ClassExpression(ClassHelper.makeWithoutCaching(MacroBuilder.class, false)), "INSTANCE"),
+                            MacroTransformation.MACRO_METHOD,
+                            new ArgumentListExpression(macroArgumentsExpressions)
+                    );
+
+                    macroCall.setSpreadSafe(false);
+                    macroCall.setSafe(false);
+                    macroCall.setImplicitThis(false);
+                    call.putNodeMetaData(MacroTransformation.class, macroCall);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return;
+            }
+        }
+        super.visitConstructorCallExpression(call);
+
+    }
+
+    private String convertInnerClassToSource(final ClassNode type) throws Exception {
+        String source = GeneralUtils.convertASTToSource(readerSource, type);
+        // we need to remove the leading "{" and trailing "}"
+        source = source.substring(source.indexOf('{')+1, source.lastIndexOf('}')-1);
+        return source;
     }
 
     @Override
     protected boolean handleTargetMethodCallExpression(MethodCallExpression macroCall) {
         final ClosureExpression closureExpression = getClosureArgument(macroCall);
 
-        if(closureExpression == null) {
+        if (closureExpression == null) {
             return true;
         }
 
-        if(closureExpression.getParameters() != null && closureExpression.getParameters().length > 0) {
+        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);
+        final MapExpression mapExpression = buildSubstitutionMap(closureExpression);
 
         String source = convertClosureToSource(closureExpression);
 
@@ -82,18 +122,18 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
 
         TupleExpression macroArguments = getMacroArguments(macroCall);
 
-        if(macroArguments == null) {
+        if (macroArguments == null) {
             return true;
         }
 
         List<Expression> macroArgumentsExpressions = macroArguments.getExpressions();
 
-        if(macroArgumentsExpressions.size() == 2 || macroArgumentsExpressions.size() == 3) {
+        if (macroArgumentsExpressions.size() == 2 || macroArgumentsExpressions.size() == 3) {
             Expression asIsArgumentExpression = macroArgumentsExpressions.get(macroArgumentsExpressions.size() - 2);
-            if((asIsArgumentExpression instanceof ConstantExpression)) {
+            if ((asIsArgumentExpression instanceof ConstantExpression)) {
                 ConstantExpression asIsConstantExpression = (ConstantExpression) asIsArgumentExpression;
 
-                if(!(asIsConstantExpression.getValue() instanceof Boolean)) {
+                if (!(asIsConstantExpression.getValue() instanceof Boolean)) {
                     addError("AsIs argument value should be boolean", asIsConstantExpression);
                     return true;
                 }
@@ -112,10 +152,54 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
         macroCall.setSpreadSafe(false);
         macroCall.setSafe(false);
         macroCall.setImplicitThis(false);
-        
+
         return true;
     }
 
+    private MapExpression buildSubstitutionMap(final ASTNode expr) {
+        final MapExpression mapExpression = new MapExpression();
+
+        ClassCodeVisitorSupport visitor = new ClassCodeVisitorSupport() {
+            @Override
+            protected SourceUnit getSourceUnit() {
+                return null;
+            }
+
+            @Override
+            public void visitClass(final ClassNode node) {
+                super.visitClass(node);
+                Iterator<InnerClassNode> it = node.getInnerClasses();
+                while (it.hasNext()) {
+                    InnerClassNode next = it.next();
+                    visitClass(next);
+                }
+            }
+
+            @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, expr.getLineNumber(), expr.getColumnNumber());
+
+                    mapExpression.addMapEntryExpression(key.toConstructorCallExpression(), substitutionClosureExpression);
+                }
+            }
+        };
+        if (expr instanceof ClassNode) {
+            visitor.visitClass((ClassNode) expr);
+        } else {
+            expr.visit(visitor);
+        }
+        return mapExpression;
+    }
+
     @Override
     protected boolean isBuildInvocation(MethodCallExpression call) {
         return isBuildInvocation(call, MacroTransformation.MACRO_METHOD);
@@ -123,20 +207,20 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
 
     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 (methodName == null) throw new IllegalArgumentException("Null: methodName");
 
-        if(!(call.getMethod() instanceof ConstantExpression)) {
+        if (!(call.getMethod() instanceof ConstantExpression)) {
             return false;
         }
 
-        if(!(methodName.equals(call.getMethodAsString()))) {
+        if (!(methodName.equals(call.getMethodAsString()))) {
             return false;
         }
 
         // is method object correct type?
         return call.getObjectExpression() == THIS_EXPRESSION;
     }
-    
+
     protected TupleExpression getMacroArguments(MethodCallExpression call) {
         Expression macroCallArguments = call.getArguments();
         if (macroCallArguments == null) {
@@ -144,7 +228,7 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
             return null;
         }
 
-        if(!(macroCallArguments instanceof TupleExpression)) {
+        if (!(macroCallArguments instanceof TupleExpression)) {
             addError("Call should have TupleExpression as arguments", macroCallArguments);
             return null;
         }
@@ -155,14 +239,14 @@ public class MacroInvocationTrap extends MethodInvocationTrap {
             addError("Call arguments should have expressions", tupleArguments);
             return null;
         }
-        
+
         return tupleArguments;
     }
-    
+
     protected ClosureExpression getClosureArgument(MethodCallExpression call) {
         TupleExpression tupleArguments = getMacroArguments(call);
 
-        if(tupleArguments.getExpressions().size() < 1) {
+        if (tupleArguments.getExpressions().size() < 1) {
             addError("Call arguments should have at least one argument", tupleArguments);
             return null;
         }

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
index 65284b5..7920e2e 100644
--- a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/MacroTransformation.java
@@ -16,6 +16,7 @@
 package org.codehaus.groovy.macro.transform;
 
 import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
 import org.codehaus.groovy.control.CompilePhase;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.transform.GroovyASTTransformation;
@@ -32,8 +33,25 @@ public class MacroTransformation extends MethodCallTransformation {
     public static final String MACRO_METHOD = "macro";
 
     @Override
-    protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) {
-        return new MacroInvocationTrap(sourceUnit.getSource(), sourceUnit);
+    protected GroovyCodeVisitor getTransformer(final ASTNode[] nodes, final SourceUnit sourceUnit) {
+        final ClassCodeExpressionTransformer trn = new ClassCodeExpressionTransformer() {
+            @Override
+            protected SourceUnit getSourceUnit() {
+                return sourceUnit;
+            }
+
+            @Override
+            public Expression transform(final Expression exp) {
+                if (exp instanceof ConstructorCallExpression) {
+                    MethodCallExpression call = exp.getNodeMetaData(MacroTransformation.class);
+                    if (call!=null) {
+                        return call;
+                    }
+                }
+                return super.transform(exp);
+            }
+        };
+        return new TransformingMacroTrap(sourceUnit, trn);
     }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-groovy/blob/09758c34/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/TransformingMacroTrap.java
----------------------------------------------------------------------
diff --git a/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/TransformingMacroTrap.java b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/TransformingMacroTrap.java
new file mode 100644
index 0000000..35ddf55
--- /dev/null
+++ b/subprojects/groovy-macro/src/main/groovy/org/codehaus/groovy/macro/transform/TransformingMacroTrap.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2003-2014 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.macro.transform;
+
+import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+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.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.codehaus.groovy.control.SourceUnit;
+
+class TransformingMacroTrap extends MacroInvocationTrap {
+    private final ClassCodeExpressionTransformer trn;
+
+    public TransformingMacroTrap(final SourceUnit sourceUnit, final ClassCodeExpressionTransformer trn) {
+        super(sourceUnit.getSource(), sourceUnit);
+        this.trn = trn;
+    }
+
+    @Override
+    public void visitBlockStatement(final BlockStatement block) {
+        super.visitBlockStatement(block);
+        trn.visitBlockStatement(block);
+    }
+
+    @Override
+    public void visitForLoop(final ForStatement forLoop) {
+        super.visitForLoop(forLoop);
+        trn.visitForLoop(forLoop);
+    }
+
+    @Override
+    public void visitWhileLoop(final WhileStatement loop) {
+        super.visitWhileLoop(loop);
+        trn.visitWhileLoop(loop);
+    }
+
+    @Override
+    public void visitDoWhileLoop(final DoWhileStatement loop) {
+        super.visitDoWhileLoop(loop);
+        trn.visitDoWhileLoop(loop);
+    }
+
+    @Override
+    public void visitIfElse(final IfStatement ifElse) {
+        super.visitIfElse(ifElse);
+        trn.visitIfElse(ifElse);
+    }
+
+    @Override
+    public void visitExpressionStatement(final ExpressionStatement statement) {
+        super.visitExpressionStatement(statement);
+        trn.visitExpressionStatement(statement);
+    }
+
+    @Override
+    public void visitReturnStatement(final ReturnStatement statement) {
+        super.visitReturnStatement(statement);
+        trn.visitReturnStatement(statement);
+    }
+
+    @Override
+    public void visitAssertStatement(final AssertStatement statement) {
+        super.visitAssertStatement(statement);
+        trn.visitAssertStatement(statement);
+    }
+
+    @Override
+    public void visitTryCatchFinally(final TryCatchStatement statement) {
+        super.visitTryCatchFinally(statement);
+        trn.visitTryCatchFinally(statement);
+    }
+
+    @Override
+    public void visitSwitch(final SwitchStatement statement) {
+        super.visitSwitch(statement);
+        trn.visitSwitch(statement);
+    }
+
+    @Override
+    public void visitCaseStatement(final CaseStatement statement) {
+        super.visitCaseStatement(statement);
+        trn.visitCaseStatement(statement);
+    }
+
+    @Override
+    public void visitBreakStatement(final BreakStatement statement) {
+        super.visitBreakStatement(statement);
+        trn.visitBreakStatement(statement);
+    }
+
+    @Override
+    public void visitContinueStatement(final ContinueStatement statement) {
+        super.visitContinueStatement(statement);
+        trn.visitContinueStatement(statement);
+    }
+
+    @Override
+    public void visitSynchronizedStatement(final SynchronizedStatement statement) {
+        super.visitSynchronizedStatement(statement);
+        trn.visitSynchronizedStatement(statement);
+    }
+
+    @Override
+    public void visitThrowStatement(final ThrowStatement statement) {
+        super.visitThrowStatement(statement);
+        trn.visitThrowStatement(statement);
+    }
+
+    @Override
+    public void visitStaticMethodCallExpression(final StaticMethodCallExpression call) {
+        super.visitStaticMethodCallExpression(call);
+        trn.visitStaticMethodCallExpression(call);
+    }
+
+    @Override
+    public void visitBinaryExpression(final BinaryExpression expression) {
+        super.visitBinaryExpression(expression);
+        trn.visitBinaryExpression(expression);
+    }
+
+    @Override
+    public void visitTernaryExpression(final TernaryExpression expression) {
+        super.visitTernaryExpression(expression);
+        trn.visitTernaryExpression(expression);
+    }
+
+    @Override
+    public void visitShortTernaryExpression(final ElvisOperatorExpression expression) {
+        super.visitShortTernaryExpression(expression);
+        trn.visitShortTernaryExpression(expression);
+    }
+
+    @Override
+    public void visitPostfixExpression(final PostfixExpression expression) {
+        super.visitPostfixExpression(expression);
+        trn.visitPostfixExpression(expression);
+    }
+
+    @Override
+    public void visitPrefixExpression(final PrefixExpression expression) {
+        super.visitPrefixExpression(expression);
+        trn.visitPrefixExpression(expression);
+    }
+
+    @Override
+    public void visitBooleanExpression(final BooleanExpression expression) {
+        super.visitBooleanExpression(expression);
+        trn.visitBooleanExpression(expression);
+    }
+
+    @Override
+    public void visitNotExpression(final NotExpression expression) {
+        super.visitNotExpression(expression);
+        trn.visitNotExpression(expression);
+    }
+
+    @Override
+    public void visitClosureExpression(final ClosureExpression expression) {
+        super.visitClosureExpression(expression);
+        trn.visitClosureExpression(expression);
+    }
+
+    @Override
+    public void visitTupleExpression(final TupleExpression expression) {
+        super.visitTupleExpression(expression);
+        trn.visitTupleExpression(expression);
+    }
+
+    @Override
+    public void visitListExpression(final ListExpression expression) {
+        super.visitListExpression(expression);
+        trn.visitListExpression(expression);
+    }
+
+    @Override
+    public void visitArrayExpression(final ArrayExpression expression) {
+        super.visitArrayExpression(expression);
+        trn.visitArrayExpression(expression);
+    }
+
+    @Override
+    public void visitMapExpression(final MapExpression expression) {
+        super.visitMapExpression(expression);
+        trn.visitMapExpression(expression);
+    }
+
+    @Override
+    public void visitMapEntryExpression(final MapEntryExpression expression) {
+        super.visitMapEntryExpression(expression);
+        trn.visitMapEntryExpression(expression);
+    }
+
+    @Override
+    public void visitRangeExpression(final RangeExpression expression) {
+        super.visitRangeExpression(expression);
+        trn.visitRangeExpression(expression);
+    }
+
+    @Override
+    public void visitSpreadExpression(final SpreadExpression expression) {
+        super.visitSpreadExpression(expression);
+        trn.visitSpreadExpression(expression);
+    }
+
+    @Override
+    public void visitSpreadMapExpression(final SpreadMapExpression expression) {
+        super.visitSpreadMapExpression(expression);
+        trn.visitSpreadMapExpression(expression);
+    }
+
+    @Override
+    public void visitMethodPointerExpression(final MethodPointerExpression expression) {
+        super.visitMethodPointerExpression(expression);
+        trn.visitMethodPointerExpression(expression);
+    }
+
+    @Override
+    public void visitUnaryMinusExpression(final UnaryMinusExpression expression) {
+        super.visitUnaryMinusExpression(expression);
+        trn.visitUnaryMinusExpression(expression);
+    }
+
+    @Override
+    public void visitUnaryPlusExpression(final UnaryPlusExpression expression) {
+        super.visitUnaryPlusExpression(expression);
+        trn.visitUnaryPlusExpression(expression);
+    }
+
+    @Override
+    public void visitBitwiseNegationExpression(final BitwiseNegationExpression expression) {
+        super.visitBitwiseNegationExpression(expression);
+        trn.visitBitwiseNegationExpression(expression);
+    }
+
+    @Override
+    public void visitCastExpression(final CastExpression expression) {
+        super.visitCastExpression(expression);
+        trn.visitCastExpression(expression);
+    }
+
+    @Override
+    public void visitConstantExpression(final ConstantExpression expression) {
+        super.visitConstantExpression(expression);
+        trn.visitConstantExpression(expression);
+    }
+
+    @Override
+    public void visitClassExpression(final ClassExpression expression) {
+        super.visitClassExpression(expression);
+        trn.visitClassExpression(expression);
+    }
+
+    @Override
+    public void visitVariableExpression(final VariableExpression expression) {
+        super.visitVariableExpression(expression);
+        trn.visitVariableExpression(expression);
+    }
+
+    @Override
+    public void visitDeclarationExpression(final DeclarationExpression expression) {
+        super.visitDeclarationExpression(expression);
+        trn.visitDeclarationExpression(expression);
+    }
+
+    @Override
+    public void visitPropertyExpression(final PropertyExpression expression) {
+        super.visitPropertyExpression(expression);
+        trn.visitPropertyExpression(expression);
+    }
+
+    @Override
+    public void visitAttributeExpression(final AttributeExpression expression) {
+        super.visitAttributeExpression(expression);
+        trn.visitAttributeExpression(expression);
+    }
+
+    @Override
+    public void visitFieldExpression(final FieldExpression expression) {
+        super.visitFieldExpression(expression);
+        trn.visitFieldExpression(expression);
+    }
+
+    @Override
+    public void visitGStringExpression(final GStringExpression expression) {
+        super.visitGStringExpression(expression);
+        trn.visitGStringExpression(expression);
+    }
+
+    @Override
+    public void visitCatchStatement(final CatchStatement statement) {
+        super.visitCatchStatement(statement);
+        trn.visitCatchStatement(statement);
+    }
+
+    @Override
+    public void visitArgumentlistExpression(final ArgumentListExpression ale) {
+        super.visitArgumentlistExpression(ale);
+        trn.visitArgumentlistExpression(ale);
+    }
+
+    @Override
+    public void visitClosureListExpression(final ClosureListExpression cle) {
+        super.visitClosureListExpression(cle);
+        trn.visitClosureListExpression(cle);
+    }
+
+    @Override
+    public void visitBytecodeExpression(final BytecodeExpression cle) {
+        super.visitBytecodeExpression(cle);
+        trn.visitBytecodeExpression(cle);
+    }
+}