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 2017/12/14 21:56:53 UTC

[28/57] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/BytecodeExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/BytecodeExpression.java b/src/main/java/org/codehaus/groovy/classgen/BytecodeExpression.java
new file mode 100644
index 0000000..ca8030f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/BytecodeExpression.java
@@ -0,0 +1,55 @@
+/*
+ *  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.classgen;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Represents some custom bytecode generation by the compiler
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public abstract class BytecodeExpression extends Expression {
+    public static final BytecodeExpression NOP = new BytecodeExpression() {
+        public void visit(MethodVisitor visitor) {
+            //do nothing             
+        }
+    };
+
+    public BytecodeExpression() {
+    }
+    
+    public BytecodeExpression(ClassNode type) {
+        super.setType(type);
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBytecodeExpression(this);
+    }
+
+    public abstract void visit(MethodVisitor mv);
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/BytecodeInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/BytecodeInstruction.java b/src/main/java/org/codehaus/groovy/classgen/BytecodeInstruction.java
new file mode 100644
index 0000000..e390654
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/BytecodeInstruction.java
@@ -0,0 +1,32 @@
+/*
+ *  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.classgen;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Helper class used by the class generator. Usually
+ * an inner class is produced, that contains bytecode
+ * creation code in the visit method.
+ *
+ * @author Jochen Theodorou
+ */
+public abstract class BytecodeInstruction {
+    public abstract void visit(MethodVisitor mv);
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/BytecodeSequence.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/BytecodeSequence.java b/src/main/java/org/codehaus/groovy/classgen/BytecodeSequence.java
new file mode 100644
index 0000000..c5a6645
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/BytecodeSequence.java
@@ -0,0 +1,81 @@
+/*
+ *  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.classgen;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class represents a sequence of BytecodeInstructions
+ * or ASTNodes. The evaluation is depending on the type of 
+ * the visitor.
+ * 
+ * @see BytecodeInstruction
+ * @see ASTNode
+ */
+
+public class BytecodeSequence extends Statement {
+    private final List<BytecodeInstruction> instructions;
+
+    public BytecodeSequence(List instructions) {
+        this.instructions = instructions;
+    }
+    
+    public BytecodeSequence(BytecodeInstruction instruction) {
+        this.instructions = new ArrayList(1);
+        this.instructions.add(instruction);
+    }
+
+    /**
+     * Delegates to the visit method used for this class.
+     * If the visitor is a ClassGenerator, then 
+     * {@link ClassGenerator#visitBytecodeSequence(BytecodeSequence)}
+     * is called with this instance. If the visitor is no 
+     * ClassGenerator, then this method will call visit on
+     * each ASTNode element sorted by this class. If one
+     * element is a BytecodeInstruction, then it will be skipped
+     * as it is no ASTNode. 
+     * 
+     * @param visitor the visitor
+     * @see ClassGenerator
+     */
+    public void visit(GroovyCodeVisitor visitor) {
+        if (visitor instanceof ClassGenerator) {
+            ClassGenerator gen = (ClassGenerator) visitor;
+            gen.visitBytecodeSequence(this);
+            return;
+        }
+        for (Iterator iterator = instructions.iterator(); iterator.hasNext();) {
+            Object part = (Object) iterator.next();
+            if (part instanceof ASTNode) {
+                ((ASTNode)part).visit(visitor);
+            }
+        }
+    }
+
+    public List getInstructions() {
+        return instructions;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
new file mode 100644
index 0000000..b05f6bd
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -0,0 +1,746 @@
+/*
+ *  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.classgen;
+
+import org.apache.groovy.ast.tools.ClassNodeUtils;
+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.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.GStringExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+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.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.tools.GeneralUtils;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.syntax.Types;
+import org.codehaus.groovy.transform.trait.Traits;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import static java.lang.reflect.Modifier.isAbstract;
+import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isInterface;
+import static java.lang.reflect.Modifier.isNative;
+import static java.lang.reflect.Modifier.isPrivate;
+import static java.lang.reflect.Modifier.isStatic;
+import static java.lang.reflect.Modifier.isStrict;
+import static java.lang.reflect.Modifier.isSynchronized;
+import static java.lang.reflect.Modifier.isTransient;
+import static java.lang.reflect.Modifier.isVolatile;
+import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
+import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
+import static org.objectweb.asm.Opcodes.ACC_NATIVE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_STRICT;
+import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED;
+import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
+import static org.objectweb.asm.Opcodes.ACC_VOLATILE;
+/**
+ * Checks that a class satisfies various conditions including:
+ * <ul>
+ *     <li>Incorrect class or method access modifiers</li>
+ *     <li>No abstract methods appear in a non-abstract class</li>
+ *     <li>Existence and correct visibility for inherited members</li>
+ *     <li>Invalid attempts to override final members</li>
+ * </ul>
+ */
+public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
+    private static final String[] INVALID_NAME_CHARS = {".", ":", "/", ";", "[", "<", ">"};
+    // the groovy.compiler.strictNames system property is experimental and may change default value or be removed in a future version of Groovy
+    private final boolean strictNames = Boolean.parseBoolean(System.getProperty("groovy.compiler.strictNames", "false"));
+    private ClassNode currentClass;
+    private final SourceUnit source;
+    private boolean inConstructor = false;
+    private boolean inStaticConstructor = false;
+
+    public ClassCompletionVerifier(SourceUnit source) {
+        this.source = source;
+    }
+
+    public ClassNode getClassNode() {
+        return currentClass;
+    }
+
+    public void visitClass(ClassNode node) {
+        ClassNode oldClass = currentClass;
+        currentClass = node;
+        checkImplementsAndExtends(node);
+        if (source != null && !source.getErrorCollector().hasErrors()) {
+            checkClassForIncorrectModifiers(node);
+            checkInterfaceMethodVisibility(node);
+            checkAbstractMethodVisibility(node);
+            checkClassForOverwritingFinal(node);
+            checkMethodsForIncorrectModifiers(node);
+            checkMethodsForIncorrectName(node);
+            checkMethodsForWeakerAccess(node);
+            checkMethodsForOverridingFinal(node);
+            checkNoAbstractMethodsNonabstractClass(node);
+            checkClassExtendsAllSelfTypes(node);
+            checkNoStaticMethodWithSameSignatureAsNonStatic(node);
+            checkGenericsUsage(node, node.getUnresolvedInterfaces());
+            checkGenericsUsage(node, node.getUnresolvedSuperClass());
+        }
+        super.visitClass(node);
+        currentClass = oldClass;
+    }
+
+    private void checkNoStaticMethodWithSameSignatureAsNonStatic(final ClassNode node) {
+        ClassNode parent = node.getSuperClass();
+        Map<String, MethodNode> result;
+        // start with methods from the parent if any
+        if (parent != null) {
+            result = parent.getDeclaredMethodsMap();
+        } else {
+            result = new HashMap<String, MethodNode>();
+        }
+        // add in unimplemented abstract methods from the interfaces
+        ClassNodeUtils.addDeclaredMethodsFromInterfaces(node, result);
+        for (MethodNode methodNode : node.getMethods()) {
+            MethodNode mn = result.get(methodNode.getTypeDescriptor());
+            if (mn != null && (mn.isStatic() ^ methodNode.isStatic()) && !methodNode.isStaticConstructor()) {
+                if (!mn.isAbstract()) continue;
+                ClassNode declaringClass = mn.getDeclaringClass();
+                ClassNode cn = declaringClass.getOuterClass();
+                if (cn == null && declaringClass.isResolved()) {
+                    // in case of a precompiled class, the outerclass is unknown
+                    Class typeClass = declaringClass.getTypeClass();
+                    typeClass = typeClass.getEnclosingClass();
+                    if (typeClass != null) {
+                        cn = ClassHelper.make(typeClass);
+                    }
+                }
+                if (cn == null || !Traits.isTrait(cn)) {
+                    ASTNode errorNode = methodNode;
+                    String name = mn.getName();
+                    if (errorNode.getLineNumber() == -1) {
+                        // try to get a better error message location based on the property
+                        for (PropertyNode propertyNode : node.getProperties()) {
+                            if (name.startsWith("set") || name.startsWith("get") || name.startsWith("is")) {
+                                String propName = Verifier.capitalize(propertyNode.getField().getName());
+                                String shortName = name.substring(name.startsWith("is") ? 2 : 3);
+                                if (propName.equals(shortName)) {
+                                    errorNode = propertyNode;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    addError("The " + getDescription(methodNode) + " is already defined in " + getDescription(node) +
+                            ". You cannot have both a static and an instance method with the same signature", errorNode);
+                }
+            }
+            result.put(methodNode.getTypeDescriptor(), methodNode);
+        }
+    }
+
+    private void checkInterfaceMethodVisibility(ClassNode node) {
+        if (!node.isInterface()) return;
+        for (MethodNode method : node.getMethods()) {
+            if (method.isPrivate()) {
+                addError("Method '" + method.getName() + "' is private but should be public in " + getDescription(currentClass) + ".", method);
+            } else if (method.isProtected()) {
+                addError("Method '" + method.getName() + "' is protected but should be public in " + getDescription(currentClass) + ".", method);
+            }
+        }
+    }
+
+    private void checkAbstractMethodVisibility(ClassNode node) {
+        // we only do check abstract classes (including enums), no interfaces or non-abstract classes
+        if (!isAbstract(node.getModifiers()) || isInterface(node.getModifiers())) return;
+
+        List<MethodNode> abstractMethods = node.getAbstractMethods();
+        if (abstractMethods == null || abstractMethods.isEmpty()) return;
+
+        for (MethodNode method : abstractMethods) {
+            if (method.isPrivate()) {
+                addError("Method '" + method.getName() + "' from " + getDescription(node) +
+                        " must not be private as it is declared as an abstract method.", method);
+            }
+        }
+    }
+
+    private void checkNoAbstractMethodsNonabstractClass(ClassNode node) {
+        if (isAbstract(node.getModifiers())) return;
+        List<MethodNode> abstractMethods = node.getAbstractMethods();
+        if (abstractMethods == null) return;
+        for (MethodNode method : abstractMethods) {
+            MethodNode sameArgsMethod = node.getMethod(method.getName(), method.getParameters());
+            if (sameArgsMethod==null || method.getReturnType().equals(sameArgsMethod.getReturnType())) {
+                addError("Can't have an abstract method in a non-abstract class." +
+                        " The " + getDescription(node) + " must be declared abstract or" +
+                        " the " + getDescription(method) + " must be implemented.", node);
+            } else {
+                addError("Abstract "+getDescription(method)+" is not implemented but a " +
+                                "method of the same name but different return type is defined: "+
+                                (sameArgsMethod.isStatic()?"static ":"")+
+                                getDescription(sameArgsMethod), method
+                );
+            }
+        }
+    }
+
+    private void checkClassExtendsAllSelfTypes(ClassNode node) {
+        int modifiers = node.getModifiers();
+        if (!isInterface(modifiers)) {
+            for (ClassNode anInterface : GeneralUtils.getInterfacesAndSuperInterfaces(node)) {
+                if (Traits.isTrait(anInterface)) {
+                    LinkedHashSet<ClassNode> selfTypes = new LinkedHashSet<ClassNode>();
+                    for (ClassNode type : Traits.collectSelfTypes(anInterface, selfTypes, true, false)) {
+                        if (type.isInterface() && !node.implementsInterface(type)) {
+                            addError(getDescription(node)
+                                    + " implements " + getDescription(anInterface)
+                                    + " but does not implement self type " + getDescription(type),
+                                    anInterface);
+                        } else if (!type.isInterface() && !node.isDerivedFrom(type)) {
+                            addError(getDescription(node)
+                                            + " implements " + getDescription(anInterface)
+                                            + " but does not extend self type " + getDescription(type),
+                                    anInterface);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkClassForIncorrectModifiers(ClassNode node) {
+        checkClassForAbstractAndFinal(node);
+        checkClassForOtherModifiers(node);
+    }
+
+    private void checkClassForAbstractAndFinal(ClassNode node) {
+        if (!isAbstract(node.getModifiers())) return;
+        if (!isFinal(node.getModifiers())) return;
+        if (node.isInterface()) {
+            addError("The " + getDescription(node) + " must not be final. It is by definition abstract.", node);
+        } else {
+            addError("The " + getDescription(node) + " must not be both final and abstract.", node);
+        }
+    }
+
+    private void checkClassForOtherModifiers(ClassNode node) {
+        checkClassForModifier(node, isTransient(node.getModifiers()), "transient");
+        checkClassForModifier(node, isVolatile(node.getModifiers()), "volatile");
+        checkClassForModifier(node, isNative(node.getModifiers()), "native");
+        if (!(node instanceof InnerClassNode)) {
+            checkClassForModifier(node, isStatic(node.getModifiers()), "static");
+            checkClassForModifier(node, isPrivate(node.getModifiers()), "private");
+        }
+        // don't check synchronized here as it overlaps with ACC_SUPER
+    }
+
+    private void checkMethodForModifier(MethodNode node, boolean condition, String modifierName) {
+        if (!condition) return;
+        addError("The " + getDescription(node) + " has an incorrect modifier " + modifierName + ".", node);
+    }
+
+    private void checkClassForModifier(ClassNode node, boolean condition, String modifierName) {
+        if (!condition) return;
+        addError("The " + getDescription(node) + " has an incorrect modifier " + modifierName + ".", node);
+    }
+
+    private static String getDescription(ClassNode node) {
+        return (node.isInterface() ? (Traits.isTrait(node)?"trait":"interface") : "class") + " '" + node.getName() + "'";
+    }
+
+    private static String getDescription(MethodNode node) {
+        return "method '" + node.getTypeDescriptor() + "'";
+    }
+
+    private static String getDescription(FieldNode node) {
+        return "field '" + node.getName() + "'";
+    }
+
+    private static String getDescription(Parameter node) {
+        return "parameter '" + node.getName() + "'";
+    }
+
+    private void checkAbstractDeclaration(MethodNode methodNode) {
+        if (!methodNode.isAbstract()) return;
+        if (isAbstract(currentClass.getModifiers())) return;
+        addError("Can't have an abstract method in a non-abstract class." +
+                " The " + getDescription(currentClass) + " must be declared abstract or the method '" +
+                methodNode.getTypeDescriptor() + "' must not be abstract.", methodNode);
+    }
+
+    private void checkClassForOverwritingFinal(ClassNode cn) {
+        ClassNode superCN = cn.getSuperClass();
+        if (superCN == null) return;
+        if (!isFinal(superCN.getModifiers())) return;
+        String msg = "You are not allowed to overwrite the final " + getDescription(superCN) + ".";
+        addError(msg, cn);
+    }
+
+    private void checkImplementsAndExtends(ClassNode node) {
+        ClassNode cn = node.getSuperClass();
+        if (cn.isInterface() && !node.isInterface()) {
+            addError("You are not allowed to extend the " + getDescription(cn) + ", use implements instead.", node);
+        }
+        for (ClassNode anInterface : node.getInterfaces()) {
+            cn = anInterface;
+            if (!cn.isInterface()) {
+                addError("You are not allowed to implement the " + getDescription(cn) + ", use extends instead.", node);
+            }
+        }
+    }
+
+    private void checkMethodsForIncorrectName(ClassNode cn) {
+        if (!strictNames) return;
+        List<MethodNode> methods = cn.getAllDeclaredMethods();
+        for (MethodNode mNode : methods) {
+            String name = mNode.getName();
+            if (name.equals("<init>") || name.equals("<clinit>")) continue;
+            // Groovy allows more characters than Character.isValidJavaIdentifier() would allow
+            // if we find a good way to encode special chars we could remove (some of) these checks
+            for (String ch : INVALID_NAME_CHARS) {
+                if (name.contains(ch)) {
+                    addError("You are not allowed to have '" + ch + "' in a method name", mNode);
+                }
+            }
+        }
+    }
+
+    private void checkMethodsForIncorrectModifiers(ClassNode cn) {
+        if (!cn.isInterface()) return;
+        for (MethodNode method : cn.getMethods()) {
+            if (method.isFinal()) {
+                addError("The " + getDescription(method) + " from " + getDescription(cn) +
+                        " must not be final. It is by definition abstract.", method);
+            }
+            if (method.isStatic() && !isConstructor(method)) {
+                addError("The " + getDescription(method) + " from " + getDescription(cn) +
+                        " must not be static. Only fields may be static in an interface.", method);
+            }
+        }
+    }
+
+    private void checkMethodsForWeakerAccess(ClassNode cn) {
+        for (MethodNode method : cn.getMethods()) {
+            checkMethodForWeakerAccessPrivileges(method, cn);
+        }
+    }
+
+    private static boolean isConstructor(MethodNode method) {
+        return method.getName().equals("<clinit>");
+    }
+
+    private void checkMethodsForOverridingFinal(ClassNode cn) {
+        for (MethodNode method : cn.getMethods()) {
+            Parameter[] params = method.getParameters();
+            for (MethodNode superMethod : cn.getSuperClass().getMethods(method.getName())) {
+                Parameter[] superParams = superMethod.getParameters();
+                if (!hasEqualParameterTypes(params, superParams)) continue;
+                if (!superMethod.isFinal()) break;
+                addInvalidUseOfFinalError(method, params, superMethod.getDeclaringClass());
+                return;
+            }
+        }
+    }
+
+    private void addInvalidUseOfFinalError(MethodNode method, Parameter[] parameters, ClassNode superCN) {
+        StringBuilder msg = new StringBuilder();
+        msg.append("You are not allowed to override the final method ").append(method.getName());
+        appendParamsDescription(parameters, msg);
+        msg.append(" from ").append(getDescription(superCN));
+        msg.append(".");
+        addError(msg.toString(), method);
+    }
+
+    private void appendParamsDescription(Parameter[] parameters, StringBuilder msg) {
+        msg.append("(");
+        boolean needsComma = false;
+        for (Parameter parameter : parameters) {
+            if (needsComma) {
+                msg.append(",");
+            } else {
+                needsComma = true;
+            }
+            msg.append(parameter.getType());
+        }
+        msg.append(")");
+    }
+
+    private void addWeakerAccessError(ClassNode cn, MethodNode method, Parameter[] parameters, MethodNode superMethod) {
+        StringBuilder msg = new StringBuilder();
+        msg.append(method.getName());
+        appendParamsDescription(parameters, msg);
+        msg.append(" in ");
+        msg.append(cn.getName());
+        msg.append(" cannot override ");
+        msg.append(superMethod.getName());
+        msg.append(" in ");
+        msg.append(superMethod.getDeclaringClass().getName());
+        msg.append("; attempting to assign weaker access privileges; was ");
+        msg.append(superMethod.isPublic() ? "public" : "protected");
+        addError(msg.toString(), method);
+    }
+
+    private static boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) {
+        if (first.length != second.length) return false;
+        for (int i = 0; i < first.length; i++) {
+            String ft = first[i].getType().getName();
+            String st = second[i].getType().getName();
+            if (ft.equals(st)) continue;
+            return false;
+        }
+        return true;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+
+    public void visitMethod(MethodNode node) {
+        inConstructor = false;
+        inStaticConstructor = node.isStaticConstructor();
+        checkAbstractDeclaration(node);
+        checkRepetitiveMethod(node);
+        checkOverloadingPrivateAndPublic(node);
+        checkMethodModifiers(node);
+        checkGenericsUsage(node, node.getParameters());
+        checkGenericsUsage(node, node.getReturnType());
+        for (Parameter param : node.getParameters()) {
+            if (param.getType().equals(VOID_TYPE)) {
+                addError("The " + getDescription(param) + " in " +  getDescription(node) + " has invalid type void", param);
+            }
+        }
+        super.visitMethod(node);
+    }
+
+    private void checkMethodModifiers(MethodNode node) {
+        // don't check volatile here as it overlaps with ACC_BRIDGE
+        // additional modifiers not allowed for interfaces
+        if ((this.currentClass.getModifiers() & ACC_INTERFACE) != 0) {
+            checkMethodForModifier(node, isStrict(node.getModifiers()), "strictfp");
+            checkMethodForModifier(node, isSynchronized(node.getModifiers()), "synchronized");
+            checkMethodForModifier(node, isNative(node.getModifiers()), "native");
+        }
+    }
+
+    private void checkMethodForWeakerAccessPrivileges(MethodNode mn, ClassNode cn) {
+        if (mn.isPublic()) return;
+        Parameter[] params = mn.getParameters();
+        for (MethodNode superMethod : cn.getSuperClass().getMethods(mn.getName())) {
+            Parameter[] superParams = superMethod.getParameters();
+            if (!hasEqualParameterTypes(params, superParams)) continue;
+            if ((mn.isPrivate() && !superMethod.isPrivate()) ||
+                    (mn.isProtected() && superMethod.isPublic())) {
+                addWeakerAccessError(cn, mn, params, superMethod);
+                return;
+            }
+        }
+    }
+
+    private void checkOverloadingPrivateAndPublic(MethodNode node) {
+        if (isConstructor(node)) return;
+        boolean hasPrivate = node.isPrivate();
+        boolean hasPublic = node.isPublic();
+        for (MethodNode method : currentClass.getMethods(node.getName())) {
+            if (method == node) continue;
+            if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue;
+            if (method.isPublic() || method.isProtected()) {
+                hasPublic = true;
+            } else {
+                hasPrivate = true;
+            }
+            if (hasPrivate && hasPublic) break;
+        }
+        if (hasPrivate && hasPublic) {
+            addError("Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour. Renaming the private methods will solve the problem.", node);
+        }
+    }
+
+    private void checkRepetitiveMethod(MethodNode node) {
+        if (isConstructor(node)) return;
+        for (MethodNode method : currentClass.getMethods(node.getName())) {
+            if (method == node) continue;
+            if (!method.getDeclaringClass().equals(node.getDeclaringClass())) continue;
+            Parameter[] p1 = node.getParameters();
+            Parameter[] p2 = method.getParameters();
+            if (p1.length != p2.length) continue;
+            addErrorIfParamsAndReturnTypeEqual(p2, p1, node, method);
+        }
+    }
+
+    private void addErrorIfParamsAndReturnTypeEqual(Parameter[] p2, Parameter[] p1,
+                                                    MethodNode node, MethodNode element) {
+        boolean isEqual = true;
+        for (int i = 0; i < p2.length; i++) {
+            isEqual &= p1[i].getType().equals(p2[i].getType());
+            if (!isEqual) break;
+        }
+        isEqual &= node.getReturnType().equals(element.getReturnType());
+        if (isEqual) {
+            addError("Repetitive method name/signature for " + getDescription(node) +
+                    " in " + getDescription(currentClass) + ".", node);
+        }
+    }
+
+    public void visitField(FieldNode node) {
+        if (currentClass.getDeclaredField(node.getName()) != node) {
+            addError("The " + getDescription(node) + " is declared multiple times.", node);
+        }
+        checkInterfaceFieldModifiers(node);
+        checkGenericsUsage(node, node.getType());
+        if (node.getType().equals(VOID_TYPE)) {
+            addError("The " + getDescription(node) + " has invalid type void", node);
+        }
+        super.visitField(node);
+    }
+
+    public void visitProperty(PropertyNode node) {
+        checkDuplicateProperties(node);
+        checkGenericsUsage(node, node.getType());
+        super.visitProperty(node);
+    }
+
+    private void checkDuplicateProperties(PropertyNode node) {
+        ClassNode cn = node.getDeclaringClass();
+        String name = node.getName();
+        String getterName = "get" + MetaClassHelper.capitalize(name);
+        if (Character.isUpperCase(name.charAt(0))) {
+            for (PropertyNode propNode : cn.getProperties()) {
+                String otherName = propNode.getField().getName();
+                String otherGetterName = "get" + MetaClassHelper.capitalize(otherName);
+                if (node != propNode && getterName.equals(otherGetterName)) {
+                    String msg = "The field " + name + " and " + otherName + " on the class " +
+                            cn.getName() + " will result in duplicate JavaBean properties, which is not allowed";
+                    addError(msg, node);
+                }
+            }
+        }
+    }
+
+    private void checkInterfaceFieldModifiers(FieldNode node) {
+        if (!currentClass.isInterface()) return;
+        if ((node.getModifiers() & (ACC_PUBLIC | ACC_STATIC | ACC_FINAL)) == 0 ||
+                (node.getModifiers() & (ACC_PRIVATE | ACC_PROTECTED)) != 0) {
+            addError("The " + getDescription(node) + " is not 'public static final' but is defined in " +
+                    getDescription(currentClass) + ".", node);
+        }
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        if (expression.getOperation().getType() == Types.LEFT_SQUARE_BRACKET &&
+                expression.getRightExpression() instanceof MapEntryExpression) {
+            addError("You tried to use a map entry for an index operation, this is not allowed. " +
+                    "Maybe something should be set in parentheses or a comma is missing?",
+                    expression.getRightExpression());
+        }
+        super.visitBinaryExpression(expression);
+
+        if (Types.isAssignment(expression.getOperation().getType())) {
+            checkFinalFieldAccess(expression.getLeftExpression());
+            checkSuperOrThisOnLHS(expression.getLeftExpression());
+        }
+    }
+
+    private void checkSuperOrThisOnLHS(Expression expression) {
+        if (!(expression instanceof VariableExpression)) return;
+        VariableExpression ve = (VariableExpression) expression;
+        if (ve.isThisExpression()) {
+            addError("cannot have 'this' as LHS of an assignment", expression);
+        } else if (ve.isSuperExpression()) {
+            addError("cannot have 'super' as LHS of an assignment", expression);
+        }
+    }
+
+    private void checkFinalFieldAccess(Expression expression) {
+        if (!(expression instanceof VariableExpression) && !(expression instanceof PropertyExpression)) return;
+        Variable v = null;
+        if (expression instanceof VariableExpression) {
+            VariableExpression ve = (VariableExpression) expression;
+            v = ve.getAccessedVariable();
+        } else {
+            PropertyExpression propExp = ((PropertyExpression) expression);
+            Expression objectExpression = propExp.getObjectExpression();
+            if (objectExpression instanceof VariableExpression) {
+                VariableExpression varExp = (VariableExpression) objectExpression;
+                if (varExp.isThisExpression()) {
+                    v = currentClass.getDeclaredField(propExp.getPropertyAsString());
+                }
+            }
+        }
+        if (v instanceof FieldNode) {
+            FieldNode fn = (FieldNode) v;
+
+            /*
+             *  if it is static final but not accessed inside a static constructor, or,
+             *  if it is an instance final but not accessed inside a instance constructor, it is an error
+             */
+            boolean isFinal = fn.isFinal();
+            boolean isStatic = fn.isStatic();
+            boolean error = isFinal && ((isStatic && !inStaticConstructor) || (!isStatic && !inConstructor));
+
+            if (error) addError("cannot modify" + (isStatic ? " static" : "") + " final field '" + fn.getName() +
+                    "' outside of " + (isStatic ? "static initialization block." : "constructor."), expression);
+        }
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+        inConstructor = true;
+        inStaticConstructor = node.isStaticConstructor();
+        checkGenericsUsage(node, node.getParameters());
+        super.visitConstructor(node);
+    }
+
+    public void visitCatchStatement(CatchStatement cs) {
+        if (!(cs.getExceptionType().isDerivedFrom(ClassHelper.make(Throwable.class)))) {
+            addError("Catch statement parameter type is not a subclass of Throwable.", cs);
+        }
+        super.visitCatchStatement(cs);
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression mce) {
+        super.visitMethodCallExpression(mce);
+        Expression aexp = mce.getArguments();
+        if (aexp instanceof TupleExpression) {
+            TupleExpression arguments = (TupleExpression) aexp;
+            for (Expression e : arguments.getExpressions()) {
+                checkForInvalidDeclaration(e);
+            }
+        } else {
+            checkForInvalidDeclaration(aexp);
+        }
+    }
+
+    @Override
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+        super.visitDeclarationExpression(expression);
+        if (expression.isMultipleAssignmentDeclaration()) return;
+        checkInvalidDeclarationModifier(expression, ACC_ABSTRACT, "abstract");
+        checkInvalidDeclarationModifier(expression, ACC_NATIVE, "native");
+        checkInvalidDeclarationModifier(expression, ACC_PRIVATE, "private");
+        checkInvalidDeclarationModifier(expression, ACC_PROTECTED, "protected");
+        checkInvalidDeclarationModifier(expression, ACC_PUBLIC, "public");
+        checkInvalidDeclarationModifier(expression, ACC_STATIC, "static");
+        checkInvalidDeclarationModifier(expression, ACC_STRICT, "strictfp");
+        checkInvalidDeclarationModifier(expression, ACC_SYNCHRONIZED, "synchronized");
+        checkInvalidDeclarationModifier(expression, ACC_TRANSIENT, "transient");
+        checkInvalidDeclarationModifier(expression, ACC_VOLATILE, "volatile");
+        if (expression.getVariableExpression().getOriginType().equals(VOID_TYPE)) {
+            addError("The variable '" + expression.getVariableExpression().getName() + "' has invalid type void", expression);
+        }
+    }
+
+    private void checkInvalidDeclarationModifier(DeclarationExpression expression, int modifier, String modName) {
+        if ((expression.getVariableExpression().getModifiers() & modifier) != 0) {
+            addError("Modifier '" + modName + "' not allowed here.", expression);
+        }
+    }
+
+    private void checkForInvalidDeclaration(Expression exp) {
+        if (!(exp instanceof DeclarationExpression)) return;
+        addError("Invalid use of declaration inside method call.", exp);
+    }
+
+    public void visitConstantExpression(ConstantExpression expression) {
+        super.visitConstantExpression(expression);
+        checkStringExceedingMaximumLength(expression);
+    }
+
+    public void visitGStringExpression(GStringExpression expression) {
+        super.visitGStringExpression(expression);
+        for (ConstantExpression ce : expression.getStrings()) {
+            checkStringExceedingMaximumLength(ce);
+        }
+    }
+
+    private void checkStringExceedingMaximumLength(ConstantExpression expression) {
+        Object value = expression.getValue();
+        if (value instanceof String) {
+            String s = (String) value;
+            if (s.length() > 65535) {
+                addError("String too long. The given string is " + s.length() + " Unicode code units long, but only a maximum of 65535 is allowed.", expression);
+            }
+        }
+    }
+
+    private void checkGenericsUsage(ASTNode ref, ClassNode[] nodes) {
+        for (ClassNode node : nodes) {
+            checkGenericsUsage(ref, node);
+        }
+    }
+    
+    private void checkGenericsUsage(ASTNode ref, Parameter[] params) {
+        for (Parameter p : params) {
+            checkGenericsUsage(ref, p.getType());
+        }
+    }
+    
+    private void checkGenericsUsage(ASTNode ref, ClassNode node) {
+        if (node.isArray()) {
+            checkGenericsUsage(ref, node.getComponentType());
+        } else if (!node.isRedirectNode() && node.isUsingGenerics()) {
+            addError(   
+                    "A transform used a generics containing ClassNode "+ node + " " +
+                    "for "+getRefDescriptor(ref) + 
+                    "directly. You are not supposed to do this. " +
+                    "Please create a new ClassNode referring to the old ClassNode " +
+                    "and use the new ClassNode instead of the old one. Otherwise " +
+                    "the compiler will create wrong descriptors and a potential " +
+                    "NullPointerException in TypeResolver in the OpenJDK. If this is " +
+                    "not your own doing, please report this bug to the writer of the " +
+                    "transform.",
+                    ref);
+        }
+    }
+
+    private static String getRefDescriptor(ASTNode ref) {
+        if (ref instanceof FieldNode) {
+            FieldNode f = (FieldNode) ref;
+            return "the field "+f.getName()+" ";
+        } else if (ref instanceof PropertyNode) {
+            PropertyNode p = (PropertyNode) ref;
+            return "the property "+p.getName()+" ";
+        } else if (ref instanceof ConstructorNode) {
+            return "the constructor "+ref.getText()+" ";
+        } else if (ref instanceof MethodNode) {
+            return "the method "+ref.getText()+" ";
+        } else if (ref instanceof ClassNode) {
+            return "the super class "+ref+" ";
+        }
+        return "<unknown with class "+ref.getClass()+"> ";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/ClassGenerator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/ClassGenerator.java
new file mode 100644
index 0000000..2ed8661
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassGenerator.java
@@ -0,0 +1,48 @@
+/*
+ *  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.classgen;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.Opcodes;
+
+import java.util.LinkedList;
+
+/**
+ * Abstract base class for generator of Java class versions of Groovy AST classes
+ *
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @author Russel Winder
+ */
+public abstract class ClassGenerator extends ClassCodeVisitorSupport implements Opcodes {
+    // inner classes created while generating bytecode
+    protected LinkedList<ClassNode> innerClasses = new LinkedList<ClassNode>();
+
+    public LinkedList<ClassNode> getInnerClasses() {
+        return innerClasses;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return null;
+    }
+
+    public void visitBytecodeSequence(BytecodeSequence bytecodeSequence) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/ClassGeneratorException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/ClassGeneratorException.java b/src/main/java/org/codehaus/groovy/classgen/ClassGeneratorException.java
new file mode 100644
index 0000000..bce3448
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/ClassGeneratorException.java
@@ -0,0 +1,36 @@
+/*
+ *  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.classgen;
+
+/**
+ * An exception thrown by the class generator
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class ClassGeneratorException extends RuntimeException {
+
+    public ClassGeneratorException(String message) {
+        super(message);
+    }
+
+    public ClassGeneratorException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/DummyClassGenerator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/DummyClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/DummyClassGenerator.java
new file mode 100644
index 0000000..15ade40
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/DummyClassGenerator.java
@@ -0,0 +1,180 @@
+/*
+ *  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.classgen;
+
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Iterator;
+
+/**
+ * To generate a class that has all the fields and methods, except that fields are not initialized
+ * and methods are empty. It's intended for being used as a place holder during code generation
+ * of reference to the "this" class itself.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ */
+public class DummyClassGenerator extends ClassGenerator {
+
+    private final ClassVisitor cv;
+    private MethodVisitor mv;
+    private final GeneratorContext context;
+
+    // current class details
+    private ClassNode classNode;
+    private String internalClassName;
+    private String internalBaseClassName;
+
+
+    public DummyClassGenerator(
+            GeneratorContext context,
+            ClassVisitor classVisitor,
+            ClassLoader classLoader,
+            String sourceFile) {
+        this.context = context;
+        this.cv = classVisitor;
+    }
+
+    // GroovyClassVisitor interface
+    //-------------------------------------------------------------------------
+    public void visitClass(ClassNode classNode) {
+        try {
+            this.classNode = classNode;
+            this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
+
+            //System.out.println("Generating class: " + classNode.getName());
+
+            this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
+
+            cv.visit(
+                    Opcodes.V1_3,
+                    classNode.getModifiers(),
+                    internalClassName,
+                    (String) null,
+                    internalBaseClassName,
+                    BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
+            );
+
+            classNode.visitContents(this);
+
+            for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
+                ClassNode innerClass = (ClassNode) iter.next();
+                ClassNode innerClassType = innerClass;
+                String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassType);
+                String outerClassName = internalClassName; // default for inner classes
+                MethodNode enclosingMethod = innerClass.getEnclosingMethod();
+                if (enclosingMethod != null) {
+                    // local inner classes do not specify the outer class name
+                    outerClassName = null;
+                }
+                cv.visitInnerClass(
+                        innerClassInternalName,
+                        outerClassName,
+                        innerClassType.getName(),
+                        innerClass.getModifiers());
+            }
+            cv.visitEnd();
+        }
+        catch (GroovyRuntimeException e) {
+            e.setModule(classNode.getModule());
+            throw e;
+        }
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+
+        visitParameters(node, node.getParameters());
+
+        String methodType = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, node.getParameters());
+        mv = cv.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
+        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn("not intended for execution");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false);
+        mv.visitInsn(ATHROW);
+        mv.visitMaxs(0, 0);
+    }
+
+    public void visitMethod(MethodNode node) {
+
+        visitParameters(node, node.getParameters());
+
+        String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
+        mv = cv.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
+
+        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn("not intended for execution");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V", false);
+        mv.visitInsn(ATHROW);
+
+        mv.visitMaxs(0, 0);
+    }
+
+    public void visitField(FieldNode fieldNode) {
+
+        cv.visitField(
+                fieldNode.getModifiers(),
+                fieldNode.getName(),
+                BytecodeHelper.getTypeDescription(fieldNode.getType()),
+                null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initializer and instance initializer.
+                null);
+    }
+
+    /**
+     * Creates a getter, setter and field
+     */
+    public void visitProperty(PropertyNode statement) {
+    }
+
+    protected CompileUnit getCompileUnit() {
+        CompileUnit answer = classNode.getCompileUnit();
+        if (answer == null) {
+            answer = context.getCompileUnit();
+        }
+        return answer;
+    }
+
+    protected void visitParameters(ASTNode node, Parameter[] parameters) {
+        for (int i = 0, size = parameters.length; i < size; i++) {
+            visitParameter(node, parameters[i]);
+        }
+    }
+
+    protected void visitParameter(ASTNode node, Parameter parameter) {
+    }
+
+
+    public void visitAnnotations(AnnotatedNode node) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
new file mode 100644
index 0000000..30c6fab
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.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.classgen;
+
+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.ConstructorNode;
+import org.codehaus.groovy.ast.EnumConstantClassNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.TupleConstructorASTTransformation;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Enums have a parent constructor with two arguments from java.lang.Enum.
+ * This visitor adds those two arguments into manually created constructors
+ * and performs the necessary super call.
+ */
+public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
+    private final SourceUnit sourceUnit;
+
+    public EnumCompletionVisitor(CompilationUnit cu, SourceUnit su) {
+        sourceUnit = su;
+    }
+
+    public void visitClass(ClassNode node) {
+        if (!node.isEnum()) return;
+        completeEnum(node);
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return sourceUnit;
+    }
+
+    private void completeEnum(ClassNode enumClass) {
+        boolean isAic = isAnonymousInnerClass(enumClass);
+        if (enumClass.getDeclaredConstructors().isEmpty()) {
+            addImplicitConstructors(enumClass, isAic);
+        }
+
+        for (ConstructorNode ctor : enumClass.getDeclaredConstructors()) {
+            transformConstructor(ctor, isAic);
+        }
+    }
+
+    /**
+     * Add map and no-arg constructor or mirror those of the superclass (i.e. base enum).
+     */
+    private static void addImplicitConstructors(ClassNode enumClass, boolean aic) {
+        if (aic) {
+            ClassNode sn = enumClass.getSuperClass();
+            List<ConstructorNode> sctors = new ArrayList<ConstructorNode>(sn.getDeclaredConstructors());
+            if (sctors.isEmpty()) {
+                addMapConstructors(enumClass, false);
+            } else {
+                for (ConstructorNode constructorNode : sctors) {
+                    ConstructorNode init = new ConstructorNode(Opcodes.ACC_PUBLIC, constructorNode.getParameters(), ClassNode.EMPTY_ARRAY, new BlockStatement());
+                    enumClass.addConstructor(init);
+                }
+            }
+        } else {
+            addMapConstructors(enumClass, false);
+        }
+    }
+
+    /**
+     * If constructor does not define a call to super, then transform constructor
+     * to get String,int parameters at beginning and add call super(String,int).
+     */
+    private void transformConstructor(ConstructorNode ctor, boolean isAic) {
+        boolean chainedThisConstructorCall = false;
+        ConstructorCallExpression cce = null;
+        if (ctor.firstStatementIsSpecialConstructorCall()) {
+            Statement code = ctor.getFirstStatement();
+            cce = (ConstructorCallExpression) ((ExpressionStatement) code).getExpression();
+            if (cce.isSuperCall()) return;
+            // must be call to this(...)
+            chainedThisConstructorCall = true;
+        }
+        // we need to add parameters
+        Parameter[] oldP = ctor.getParameters();
+        Parameter[] newP = new Parameter[oldP.length + 2];
+        String stringParameterName = getUniqueVariableName("__str", ctor.getCode());
+        newP[0] = new Parameter(ClassHelper.STRING_TYPE, stringParameterName);
+        String intParameterName = getUniqueVariableName("__int", ctor.getCode());
+        newP[1] = new Parameter(ClassHelper.int_TYPE, intParameterName);
+        System.arraycopy(oldP, 0, newP, 2, oldP.length);
+        ctor.setParameters(newP);
+        VariableExpression stringVariable = new VariableExpression(newP[0]);
+        VariableExpression intVariable = new VariableExpression(newP[1]);
+        if (chainedThisConstructorCall) {
+            TupleExpression args = (TupleExpression) cce.getArguments();
+            List<Expression> argsExprs = args.getExpressions();
+            argsExprs.add(0, stringVariable);
+            argsExprs.add(1, intVariable);
+        } else {
+            // add a super call
+            List<Expression> args = new ArrayList<Expression>();
+            args.add(stringVariable);
+            args.add(intVariable);
+            if (isAic) {
+                for (Parameter parameter : oldP) {
+                    args.add(new VariableExpression(parameter.getName()));
+                }
+            }
+            cce = new ConstructorCallExpression(ClassNode.SUPER, new ArgumentListExpression(args));
+            BlockStatement code = new BlockStatement();
+            code.addStatement(new ExpressionStatement(cce));
+            Statement oldCode = ctor.getCode();
+            if (oldCode != null) code.addStatement(oldCode);
+            ctor.setCode(code);
+        }
+    }
+
+    public static void addMapConstructors(ClassNode enumClass, boolean hasNoArg) {
+        TupleConstructorASTTransformation.addMapConstructors(enumClass, hasNoArg, "One of the enum constants for enum " + enumClass.getName() +
+                " was initialized with null. Please use a non-null value or define your own constructor.");
+    }
+
+    private String getUniqueVariableName(final String name, Statement code) {
+        if (code == null) return name;
+        final Object[] found = new Object[1];
+        CodeVisitorSupport cv = new CodeVisitorSupport() {
+            public void visitVariableExpression(VariableExpression expression) {
+                if (expression.getName().equals(name)) found[0] = Boolean.TRUE;
+            }
+        };
+        code.visit(cv);
+        if (found[0] != null) return getUniqueVariableName("_" + name, code);
+        return name;
+    }
+
+    private static boolean isAnonymousInnerClass(ClassNode enumClass) {
+        if (!(enumClass instanceof EnumConstantClassNode)) return false;
+        InnerClassNode ic = (InnerClassNode) enumClass;
+        return ic.getVariableScope() == null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
new file mode 100644
index 0000000..743801f
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumVisitor.java
@@ -0,0 +1,444 @@
+/*
+ *  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.classgen;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.EnumConstantClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.SpreadExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+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.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EnumVisitor extends ClassCodeVisitorSupport {
+    // some constants for modifiers
+    private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;
+    private static final int PS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
+    private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS;
+    private static final int PRIVATE_FS = Opcodes.ACC_PRIVATE | FS;
+
+    private final SourceUnit sourceUnit;
+
+
+    public EnumVisitor(CompilationUnit cu, SourceUnit su) {
+        sourceUnit = su;
+    }
+
+    public void visitClass(ClassNode node) {
+        if (!node.isEnum()) return;
+        completeEnum(node);
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return sourceUnit;
+    }
+
+    private void completeEnum(ClassNode enumClass) {
+        boolean isAic = isAnonymousInnerClass(enumClass);
+        // create MIN_VALUE and MAX_VALUE fields
+        FieldNode minValue = null, maxValue = null, values = null;
+
+        if (!isAic) {
+            ClassNode enumRef = enumClass.getPlainNodeReference();
+
+            // create values field
+            values = new FieldNode("$VALUES", PRIVATE_FS | Opcodes.ACC_SYNTHETIC, enumRef.makeArray(), enumClass, null);
+            values.setSynthetic(true);
+
+            addMethods(enumClass, values);
+            checkForAbstractMethods(enumClass);
+
+            // create MIN_VALUE and MAX_VALUE fields
+            minValue = new FieldNode("MIN_VALUE", PUBLIC_FS, enumRef, enumClass, null);
+            maxValue = new FieldNode("MAX_VALUE", PUBLIC_FS, enumRef, enumClass, null);
+        }
+        addInit(enumClass, minValue, maxValue, values, isAic);
+    }
+
+    private static void checkForAbstractMethods(ClassNode enumClass) {
+        List<MethodNode> methods = enumClass.getMethods();
+        for (MethodNode m : methods) {
+            if (m.isAbstract()) {
+                // make the class abstract also see Effective Java p.152
+                enumClass.setModifiers(enumClass.getModifiers() | Opcodes.ACC_ABSTRACT);
+                break;
+            }
+        }
+    }
+
+    private static void addMethods(ClassNode enumClass, FieldNode values) {
+        List<MethodNode> methods = enumClass.getMethods();
+
+        boolean hasNext = false;
+        boolean hasPrevious = false;
+        for (MethodNode m : methods) {
+            if (m.getName().equals("next") && m.getParameters().length == 0) hasNext = true;
+            if (m.getName().equals("previous") && m.getParameters().length == 0) hasPrevious = true;
+            if (hasNext && hasPrevious) break;
+        }
+
+        ClassNode enumRef = enumClass.getPlainNodeReference();
+
+        {
+            // create values() method
+            MethodNode valuesMethod = new MethodNode("values", PUBLIC_FS, enumRef.makeArray(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            valuesMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            MethodCallExpression cloneCall = new MethodCallExpression(new FieldExpression(values), "clone", MethodCallExpression.NO_ARGUMENTS);
+            cloneCall.setMethodTarget(values.getType().getMethod("clone", Parameter.EMPTY_ARRAY));
+            code.addStatement(new ReturnStatement(cloneCall));
+            valuesMethod.setCode(code);
+            enumClass.addMethod(valuesMethod);
+        }
+
+        if (!hasNext) {
+            // create next() method, code:
+            //     Day next() {
+            //        int ordinal = ordinal().next()
+            //        if (ordinal >= values().size()) ordinal = 0
+            //        return values()[ordinal]
+            //     }
+            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+            Token ge = Token.newSymbol(Types.COMPARE_GREATER_THAN_EQUAL, -1, -1);
+            MethodNode nextMethod = new MethodNode("next", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            nextMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            BlockStatement ifStatement = new BlockStatement();
+            ifStatement.addStatement(
+                    new ExpressionStatement(
+                            new BinaryExpression(new VariableExpression("ordinal"), assign, new ConstantExpression(0))
+                    )
+            );
+
+            code.addStatement(
+                    new ExpressionStatement(
+                            new DeclarationExpression(
+                                    new VariableExpression("ordinal"),
+                                    assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    VariableExpression.THIS_EXPRESSION,
+                                                    "ordinal",
+                                                    MethodCallExpression.NO_ARGUMENTS),
+                                            "next",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )
+                    )
+            );
+            code.addStatement(
+                    new IfStatement(
+                            new BooleanExpression(new BinaryExpression(
+                                    new VariableExpression("ordinal"),
+                                    ge,
+                                    new MethodCallExpression(
+                                            new FieldExpression(values),
+                                            "size",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )),
+                            ifStatement,
+                            EmptyStatement.INSTANCE
+                    )
+            );
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
+                    )
+            );
+            nextMethod.setCode(code);
+            enumClass.addMethod(nextMethod);
+        }
+
+        if (!hasPrevious) {
+            // create previous() method, code:
+            //    Day previous() {
+            //        int ordinal = ordinal().previous()
+            //        if (ordinal < 0) ordinal = values().size() - 1
+            //        return values()[ordinal]
+            //    }
+            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+            Token lt = Token.newSymbol(Types.COMPARE_LESS_THAN, -1, -1);
+            MethodNode nextMethod = new MethodNode("previous", Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, enumRef, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null);
+            nextMethod.setSynthetic(true);
+            BlockStatement code = new BlockStatement();
+            BlockStatement ifStatement = new BlockStatement();
+            ifStatement.addStatement(
+                    new ExpressionStatement(
+                            new BinaryExpression(new VariableExpression("ordinal"), assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    new FieldExpression(values),
+                                                    "size",
+                                                    MethodCallExpression.NO_ARGUMENTS
+                                            ),
+                                            "minus",
+                                            new ConstantExpression(1)
+                                    )
+                            )
+                    )
+            );
+
+            code.addStatement(
+                    new ExpressionStatement(
+                            new DeclarationExpression(
+                                    new VariableExpression("ordinal"),
+                                    assign,
+                                    new MethodCallExpression(
+                                            new MethodCallExpression(
+                                                    VariableExpression.THIS_EXPRESSION,
+                                                    "ordinal",
+                                                    MethodCallExpression.NO_ARGUMENTS),
+                                            "previous",
+                                            MethodCallExpression.NO_ARGUMENTS
+                                    )
+                            )
+                    )
+            );
+            code.addStatement(
+                    new IfStatement(
+                            new BooleanExpression(new BinaryExpression(
+                                    new VariableExpression("ordinal"),
+                                    lt,
+                                    new ConstantExpression(0)
+                            )),
+                            ifStatement,
+                            EmptyStatement.INSTANCE
+                    )
+            );
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new FieldExpression(values), "getAt", new VariableExpression("ordinal"))
+                    )
+            );
+            nextMethod.setCode(code);
+            enumClass.addMethod(nextMethod);
+        }
+
+        {
+            // create valueOf
+            Parameter stringParameter = new Parameter(ClassHelper.STRING_TYPE, "name");
+            MethodNode valueOfMethod = new MethodNode("valueOf", PS, enumRef, new Parameter[]{stringParameter}, ClassNode.EMPTY_ARRAY, null);
+            ArgumentListExpression callArguments = new ArgumentListExpression();
+            callArguments.addExpression(new ClassExpression(enumClass));
+            callArguments.addExpression(new VariableExpression("name"));
+
+            BlockStatement code = new BlockStatement();
+            code.addStatement(
+                    new ReturnStatement(
+                            new MethodCallExpression(new ClassExpression(ClassHelper.Enum_Type), "valueOf", callArguments)
+                    )
+            );
+            valueOfMethod.setCode(code);
+            valueOfMethod.setSynthetic(true);
+            enumClass.addMethod(valueOfMethod);
+        }
+    }
+
+    private void addInit(ClassNode enumClass, FieldNode minValue,
+                         FieldNode maxValue, FieldNode values,
+                         boolean isAic) {
+        // constructor helper
+        // This method is used instead of calling the constructor as
+        // calling the constructor may require a table with MetaClass
+        // selecting the constructor for each enum value. So instead we
+        // use this method to have a central point for constructor selection
+        // and only one table. The whole construction is needed because 
+        // Reflection forbids access to the enum constructor.
+        // code:
+        // def $INIT(Object[] para) {
+        //  return this(*para)
+        // }
+        ClassNode enumRef = enumClass.getPlainNodeReference();
+        Parameter[] parameter = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para")};
+        MethodNode initMethod = new MethodNode("$INIT", PUBLIC_FS | Opcodes.ACC_SYNTHETIC, enumRef, parameter, ClassNode.EMPTY_ARRAY, null);
+        initMethod.setSynthetic(true);
+        ConstructorCallExpression cce = new ConstructorCallExpression(
+                ClassNode.THIS,
+                new ArgumentListExpression(
+                        new SpreadExpression(new VariableExpression("para"))
+                )
+        );
+        BlockStatement code = new BlockStatement();
+        code.addStatement(new ReturnStatement(cce));
+        initMethod.setCode(code);
+        enumClass.addMethod(initMethod);
+
+        // static init
+        List<FieldNode> fields = enumClass.getFields();
+        List<Expression> arrayInit = new ArrayList<Expression>();
+        int value = -1;
+        Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);
+        List<Statement> block = new ArrayList<Statement>();
+        FieldNode tempMin = null;
+        FieldNode tempMax = null;
+        for (FieldNode field : fields) {
+            if ((field.getModifiers() & Opcodes.ACC_ENUM) == 0) continue;
+            value++;
+            if (tempMin == null) tempMin = field;
+            tempMax = field;
+
+            ClassNode enumBase = enumClass;
+            ArgumentListExpression args = new ArgumentListExpression();
+            args.addExpression(new ConstantExpression(field.getName()));
+            args.addExpression(new ConstantExpression(value));
+            if (field.getInitialExpression() == null) {
+                if ((enumClass.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
+                    addError(field, "The enum constant " + field.getName() + " must override abstract methods from " + enumBase.getName() + ".");
+                    continue;
+                }
+            } else {
+                ListExpression oldArgs = (ListExpression) field.getInitialExpression();
+                List<MapEntryExpression> savedMapEntries = new ArrayList<MapEntryExpression>();
+                for (Expression exp : oldArgs.getExpressions()) {
+                    if (exp instanceof MapEntryExpression) {
+                        savedMapEntries.add((MapEntryExpression) exp);
+                        continue;
+                    }
+
+                    InnerClassNode inner = null;
+                    if (exp instanceof ClassExpression) {
+                        ClassExpression clazzExp = (ClassExpression) exp;
+                        ClassNode ref = clazzExp.getType();
+                        if (ref instanceof EnumConstantClassNode) {
+                            inner = (InnerClassNode) ref;
+                        }
+                    }
+                    if (inner != null) {
+                        List<MethodNode> baseMethods = enumBase.getMethods();
+                        for (MethodNode methodNode : baseMethods) {
+                            if (!methodNode.isAbstract()) continue;
+                            MethodNode enumConstMethod = inner.getMethod(methodNode.getName(), methodNode.getParameters());
+                            if (enumConstMethod == null || (enumConstMethod.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
+                                addError(field, "Can't have an abstract method in enum constant " + field.getName() + ". Implement method '" + methodNode.getTypeDescriptor() + "'.");
+                            }
+                        }
+                        if (inner.getVariableScope() == null) {
+                            enumBase = inner;
+                            /*
+                             * GROOVY-3985: Remove the final modifier from $INIT method in this case
+                             * so that subclasses of enum generated for enum constants (aic) can provide
+                             * their own $INIT method
+                             */
+                            initMethod.setModifiers(initMethod.getModifiers() & ~Opcodes.ACC_FINAL);
+                            continue;
+                        }
+                    }
+                    args.addExpression(exp);
+                }
+                if (!savedMapEntries.isEmpty()) {
+                    args.getExpressions().add(2, new MapExpression(savedMapEntries));
+                }
+            }
+            field.setInitialValueExpression(null);
+            block.add(
+                    new ExpressionStatement(
+                            new BinaryExpression(
+                                    new FieldExpression(field),
+                                    assign,
+                                    new StaticMethodCallExpression(enumBase, "$INIT", args)
+                            )
+                    )
+            );
+            arrayInit.add(new FieldExpression(field));
+        }
+
+        if (!isAic) {
+            if (tempMin != null) {
+                block.add(
+                        new ExpressionStatement(
+                                new BinaryExpression(
+                                        new FieldExpression(minValue),
+                                        assign,
+                                        new FieldExpression(tempMin)
+                                )
+                        )
+                );
+                block.add(
+                        new ExpressionStatement(
+                                new BinaryExpression(
+                                        new FieldExpression(maxValue),
+                                        assign,
+                                        new FieldExpression(tempMax)
+                                )
+                        )
+                );
+                enumClass.addField(minValue);
+                enumClass.addField(maxValue);
+            }
+
+            block.add(
+                    new ExpressionStatement(
+                            new BinaryExpression(new FieldExpression(values), assign, new ArrayExpression(enumClass, arrayInit))
+                    )
+            );
+            enumClass.addField(values);
+        }
+        enumClass.addStaticInitializerStatements(block, true);
+    }
+
+    private void addError(AnnotatedNode exp, String msg) {
+        sourceUnit.getErrorCollector().addErrorAndContinue(
+                new SyntaxErrorMessage(
+                        new SyntaxException(msg + '\n', exp.getLineNumber(), exp.getColumnNumber(), exp.getLastLineNumber(), exp.getLastColumnNumber()), sourceUnit)
+        );
+    }
+
+    private static boolean isAnonymousInnerClass(ClassNode enumClass) {
+        if (!(enumClass instanceof EnumConstantClassNode)) return false;
+        InnerClassNode ic = (InnerClassNode) enumClass;
+        return ic.getVariableScope() == null;
+    }
+
+}