groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cchamp...@apache.org
Subject [25/57] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`
Date Thu, 14 Dec 2017 21:56:50 GMT
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/Verifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
new file mode 100644
index 0000000..59a8d60
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -0,0 +1,1565 @@
+/*
+ *  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.GroovyClassLoader;
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import groovy.transform.Generated;
+import org.apache.groovy.ast.tools.ClassNodeUtils;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.AnnotationNode;
+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.FieldNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.GroovyClassVisitor;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+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.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+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.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
+import org.codehaus.groovy.ast.tools.PropertyNodeUtils;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.MopWriter;
+import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.ClassNodeSkip;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.reflection.ClassInfo;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.codehaus.groovy.transform.trait.Traits;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static java.lang.reflect.Modifier.isAbstract;
+import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPrivate;
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+import static org.apache.groovy.ast.tools.MethodNodeUtils.methodDescriptorWithoutReturnType;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.correctToGenericsSpec;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.createGenericsSpec;
+
+/**
+ * Verifies the AST node and adds any default AST code before bytecode generation occurs.
+ *
+ * Checks include:
+ * <ul>
+ *     <li>Methods with duplicate signatures</li>
+ *     <li>Duplicate interfaces</li>
+ *     <li>Reassigned final variables/parameters</li>
+ *     <li>Uninitialized variables</li>
+ *     <li>Bad code in object initializers or constructors</li>
+ *     <li>Mismatches in modifiers or return types between implementations and interfaces/abstract classes</li>
+ * </ul>
+ *
+ * Added code includes:
+ * <ul>
+ *     <li>Methods needed to implement GroovyObject</li>
+ *     <li>Property accessor methods</li>
+ *     <li>Covariant methods</li>
+ *     <li>Additional methods/constructors as needed for default parameters</li>
+ * </ul>
+ */
+public class Verifier implements GroovyClassVisitor, Opcodes {
+
+    public static final String STATIC_METACLASS_BOOL = "__$stMC";
+    public static final String SWAP_INIT = "__$swapInit";
+    public static final String INITIAL_EXPRESSION = "INITIAL_EXPRESSION";
+    public static final String DEFAULT_PARAMETER_GENERATED = "DEFAULT_PARAMETER_GENERATED";
+
+    // NOTE: timeStamp constants shouldn't belong to Verifier but kept here
+    // for binary compatibility
+    public static final String __TIMESTAMP = "__timeStamp";
+    public static final String __TIMESTAMP__ = "__timeStamp__239_neverHappen";
+    private static final Parameter[] INVOKE_METHOD_PARAMS = new Parameter[]{
+            new Parameter(ClassHelper.STRING_TYPE, "method"),
+            new Parameter(ClassHelper.OBJECT_TYPE, "arguments")
+    };
+    private static final Parameter[] SET_PROPERTY_PARAMS = new Parameter[]{
+            new Parameter(ClassHelper.STRING_TYPE, "property"),
+            new Parameter(ClassHelper.OBJECT_TYPE, "value")
+    };
+    private static final Parameter[] GET_PROPERTY_PARAMS = new Parameter[]{
+            new Parameter(ClassHelper.STRING_TYPE, "property")
+    };
+    private static final Parameter[] SET_METACLASS_PARAMS = new Parameter[]{
+            new Parameter(ClassHelper.METACLASS_TYPE, "mc")
+    };
+
+    private static final Class GENERATED_ANNOTATION = Generated.class;
+
+    private ClassNode classNode;
+    private MethodNode methodNode;
+
+    public ClassNode getClassNode() {
+        return classNode;
+    }
+
+    protected void setClassNode(ClassNode classNode) {
+        this.classNode = classNode;
+    }
+
+    public MethodNode getMethodNode() {
+        return methodNode;
+    }
+
+    private static FieldNode setMetaClassFieldIfNotExists(ClassNode node, FieldNode metaClassField) {
+        if (metaClassField != null) return metaClassField;
+        final String classInternalName = BytecodeHelper.getClassInternalName(node);
+        metaClassField =
+                node.addField("metaClass", ACC_PRIVATE | ACC_TRANSIENT | ACC_SYNTHETIC, ClassHelper.METACLASS_TYPE,
+                        new BytecodeExpression(ClassHelper.METACLASS_TYPE) {
+                            public void visit(MethodVisitor mv) {
+                                mv.visitVarInsn(ALOAD, 0);
+                                mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                            }
+                        });
+        metaClassField.setSynthetic(true);
+        return metaClassField;
+    }
+
+    private static FieldNode getMetaClassField(ClassNode node) {
+        FieldNode ret = node.getDeclaredField("metaClass");
+        if (ret != null) {
+            ClassNode mcFieldType = ret.getType();
+            if (!mcFieldType.equals(ClassHelper.METACLASS_TYPE)) {
+                throw new RuntimeParserException("The class " + node.getName() +
+                        " cannot declare field 'metaClass' of type " + mcFieldType.getName() + " as it needs to be of " +
+                        "the type " + ClassHelper.METACLASS_TYPE.getName() + " for internal groovy purposes", ret);
+            }
+            return ret;
+        }
+        ClassNode current = node;
+        while (current != ClassHelper.OBJECT_TYPE) {
+            current = current.getSuperClass();
+            if (current == null) break;
+            ret = current.getDeclaredField("metaClass");
+            if (ret == null) continue;
+            if (isPrivate(ret.getModifiers())) continue;
+            return ret;
+        }
+        return null;
+    }
+
+    /**
+     * walk the class
+     *
+     * @param node the node to visit
+     */
+    public void visitClass(final ClassNode node) {
+        this.classNode = node;
+
+        if (Traits.isTrait(node) // maybe possible to have this true in joint compilation mode
+                || classNode.isInterface()) {
+            //interfaces have no constructors, but this code expects one,
+            //so create a dummy and don't add it to the class node
+            ConstructorNode dummy = new ConstructorNode(0, null);
+            addInitialization(node, dummy);
+            node.visitContents(this);
+            if (classNode.getNodeMetaData(ClassNodeSkip.class) == null) {
+                classNode.setNodeMetaData(ClassNodeSkip.class, true);
+            }
+            return;
+        }
+
+        ClassNode[] classNodes = classNode.getInterfaces();
+        List<String> interfaces = new ArrayList<String>();
+        for (ClassNode classNode : classNodes) {
+            interfaces.add(classNode.getName());
+        }
+        Set<String> interfaceSet = new HashSet<String>(interfaces);
+        if (interfaceSet.size() != interfaces.size()) {
+            throw new RuntimeParserException("Duplicate interfaces in implements list: " + interfaces, classNode);
+        }
+
+        addDefaultParameterMethods(node);
+        addDefaultParameterConstructors(node);
+
+        final String classInternalName = BytecodeHelper.getClassInternalName(node);
+
+        addStaticMetaClassField(node, classInternalName);
+
+        boolean knownSpecialCase =
+                node.isDerivedFrom(ClassHelper.GSTRING_TYPE)
+                        || node.isDerivedFrom(ClassHelper.GROOVY_OBJECT_SUPPORT_TYPE);
+
+        addFastPathHelperFieldsAndHelperMethod(node, classInternalName, knownSpecialCase);
+        if (!knownSpecialCase) addGroovyObjectInterfaceAndMethods(node, classInternalName);
+
+        addDefaultConstructor(node);
+
+        addInitialization(node);
+        checkReturnInObjectInitializer(node.getObjectInitializerStatements());
+        node.getObjectInitializerStatements().clear();
+        node.visitContents(this);
+        checkForDuplicateMethods(node);
+        addCovariantMethods(node);
+
+        checkFinalVariables(node);
+    }
+
+    private void checkFinalVariables(ClassNode node) {
+        FinalVariableAnalyzer analyzer = new FinalVariableAnalyzer(null, getFinalVariablesCallback());
+        analyzer.visitClass(node);
+
+    }
+
+    protected FinalVariableAnalyzer.VariableNotFinalCallback getFinalVariablesCallback() {
+        return new FinalVariableAnalyzer.VariableNotFinalCallback() {
+            @Override
+            public void variableNotFinal(Variable var, Expression bexp) {
+                if (var instanceof VariableExpression) {
+                    var = ((VariableExpression) var).getAccessedVariable();
+                }
+                if (var instanceof VariableExpression && isFinal(var.getModifiers())) {
+                    throw new RuntimeParserException("The variable [" + var.getName() + "] is declared final but is reassigned", bexp);
+                }
+                if (var instanceof Parameter && isFinal(var.getModifiers())) {
+                    throw new RuntimeParserException("The parameter [" + var.getName() + "] is declared final but is reassigned", bexp);
+                }
+            }
+
+            @Override
+            public void variableNotAlwaysInitialized(final VariableExpression var) {
+                throw new RuntimeParserException("The variable [" + var.getName() + "] may be uninitialized", var);
+            }
+        };
+    }
+
+    private static void checkForDuplicateMethods(ClassNode cn) {
+        Set<String> descriptors = new HashSet<String>();
+        for (MethodNode mn : cn.getMethods()) {
+            if (mn.isSynthetic()) continue;
+            String mySig = methodDescriptorWithoutReturnType(mn);
+            if (descriptors.contains(mySig)) {
+                if (mn.isScriptBody() || mySig.equals(scriptBodySignatureWithoutReturnType(cn))) {
+                    throw new RuntimeParserException("The method " + mn.getText() +
+                            " is a duplicate of the one declared for this script's body code", mn);
+                } else {
+                    throw new RuntimeParserException("The method " + mn.getText() +
+                            " duplicates another method of the same signature", mn);
+                }
+            }
+            descriptors.add(mySig);
+        }
+    }
+
+    private static String scriptBodySignatureWithoutReturnType(ClassNode cn) {
+        for (MethodNode mn : cn.getMethods()) {
+            if (mn.isScriptBody()) return methodDescriptorWithoutReturnType(mn);
+        }
+        return null;
+    }
+
+    private static FieldNode checkFieldDoesNotExist(ClassNode node, String fieldName) {
+        FieldNode ret = node.getDeclaredField(fieldName);
+        if (ret != null) {
+            if (isPublic(ret.getModifiers()) &&
+                    ret.getType().redirect() == ClassHelper.boolean_TYPE) {
+                return ret;
+            }
+            throw new RuntimeParserException("The class " + node.getName() +
+                    " cannot declare field '" + fieldName + "' as this" +
+                    " field is needed for internal groovy purposes", ret);
+        }
+        return null;
+    }
+
+    private static void addFastPathHelperFieldsAndHelperMethod(ClassNode node, final String classInternalName, boolean knownSpecialCase) {
+        if (node.getNodeMetaData(ClassNodeSkip.class) != null) return;
+        FieldNode stMCB = checkFieldDoesNotExist(node, STATIC_METACLASS_BOOL);
+        if (stMCB == null) {
+            stMCB = node.addField(
+                    STATIC_METACLASS_BOOL,
+                    ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC | ACC_TRANSIENT,
+                    ClassHelper.boolean_TYPE, null);
+            stMCB.setSynthetic(true);
+        }
+    }
+
+    protected void addDefaultConstructor(ClassNode node) {
+        if (!node.getDeclaredConstructors().isEmpty()) return;
+
+        BlockStatement empty = new BlockStatement();
+        empty.setSourcePosition(node);
+        ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, empty);
+        constructor.setSourcePosition(node);
+        constructor.setHasNoRealSourcePosition(true);
+        node.addConstructor(constructor);
+    }
+
+    private void addStaticMetaClassField(final ClassNode node, final String classInternalName) {
+        String _staticClassInfoFieldName = "$staticClassInfo";
+        while (node.getDeclaredField(_staticClassInfoFieldName) != null)
+            _staticClassInfoFieldName = _staticClassInfoFieldName + "$";
+        final String staticMetaClassFieldName = _staticClassInfoFieldName;
+
+        FieldNode staticMetaClassField = node.addField(staticMetaClassFieldName, ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC, ClassHelper.make(ClassInfo.class, false), null);
+        staticMetaClassField.setSynthetic(true);
+
+        node.addSyntheticMethod(
+                "$getStaticMetaClass",
+                ACC_PROTECTED,
+                ClassHelper.make(MetaClass.class),
+                Parameter.EMPTY_ARRAY,
+                ClassNode.EMPTY_ARRAY,
+                new BytecodeSequence(new BytecodeInstruction() {
+                    public void visit(MethodVisitor mv) {
+                        mv.visitVarInsn(ALOAD, 0);
+                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+                        if (BytecodeHelper.isClassLiteralPossible(node) || BytecodeHelper.isSameCompilationUnit(classNode, node)) {
+                            BytecodeHelper.visitClassLiteral(mv, node);
+                        } else {
+                            mv.visitMethodInsn(INVOKESTATIC, classInternalName, "$get$$class$" + classInternalName.replaceAll("\\/", "\\$"), "()Ljava/lang/Class;", false);
+                        }
+                        Label l1 = new Label();
+                        mv.visitJumpInsn(IF_ACMPEQ, l1);
+
+                        mv.visitVarInsn(ALOAD, 0);
+                        mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/ScriptBytecodeAdapter", "initMetaClass", "(Ljava/lang/Object;)Lgroovy/lang/MetaClass;", false);
+                        mv.visitInsn(ARETURN);
+
+                        mv.visitLabel(l1);
+
+                        mv.visitFieldInsn(GETSTATIC, classInternalName, staticMetaClassFieldName, "Lorg/codehaus/groovy/reflection/ClassInfo;");
+                        mv.visitVarInsn(ASTORE, 1);
+                        mv.visitVarInsn(ALOAD, 1);
+                        Label l0 = new Label();
+                        mv.visitJumpInsn(IFNONNULL, l0);
+
+                        mv.visitVarInsn(ALOAD, 0);
+                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+                        mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/reflection/ClassInfo", "getClassInfo", "(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;", false);
+                        mv.visitInsn(DUP);
+                        mv.visitVarInsn(ASTORE, 1);
+                        mv.visitFieldInsn(PUTSTATIC, classInternalName, staticMetaClassFieldName, "Lorg/codehaus/groovy/reflection/ClassInfo;");
+
+                        mv.visitLabel(l0);
+
+                        mv.visitVarInsn(ALOAD, 1);
+                        mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/reflection/ClassInfo", "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                        mv.visitInsn(ARETURN);
+
+                    }
+                })
+        );
+    }
+
+    protected void addGroovyObjectInterfaceAndMethods(ClassNode node, final String classInternalName) {
+        if (!node.isDerivedFromGroovyObject()) node.addInterface(ClassHelper.make(GroovyObject.class));
+        FieldNode metaClassField = getMetaClassField(node);
+
+        boolean shouldAnnotate = classNode.getModule().getContext() != null;
+        AnnotationNode generatedAnnotation = shouldAnnotate ? new AnnotationNode(ClassHelper.make(GENERATED_ANNOTATION)) : null;
+
+        if (!node.hasMethod("getMetaClass", Parameter.EMPTY_ARRAY)) {
+            metaClassField = setMetaClassFieldIfNotExists(node, metaClassField);
+            MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()),
+                    "getMetaClass",
+                    ACC_PUBLIC,
+                    ClassHelper.METACLASS_TYPE,
+                    Parameter.EMPTY_ARRAY,
+                    ClassNode.EMPTY_ARRAY,
+                    new BytecodeSequence(new BytecodeInstruction() {
+                        public void visit(MethodVisitor mv) {
+                            Label nullLabel = new Label();
+                            /**
+                             *  the code is:
+                             *  if (this.metaClass==null) {
+                             *      this.metaClass = this.$getStaticMetaClass
+                             *      return this.metaClass
+                             *  } else {
+                             *      return this.metaClass
+                             *  }
+                             *  with the optimization that the result of the
+                             *  first this.metaClass is duped on the operand
+                             *  stack and reused for the return in the else part
+                             */
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+                            mv.visitInsn(DUP);
+                            mv.visitJumpInsn(IFNULL, nullLabel);
+                            mv.visitInsn(ARETURN);
+
+                            mv.visitLabel(nullLabel);
+                            mv.visitInsn(POP);
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitInsn(DUP);
+                            mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "$getStaticMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                            mv.visitFieldInsn(PUTFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitFieldInsn(GETFIELD, classInternalName, "metaClass", "Lgroovy/lang/MetaClass;");
+                            mv.visitInsn(ARETURN);
+                        }
+                    })
+            );
+            if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation);
+        }
+
+        Parameter[] parameters = new Parameter[]{new Parameter(ClassHelper.METACLASS_TYPE, "mc")};
+        if (!node.hasMethod("setMetaClass", parameters)) {
+            metaClassField = setMetaClassFieldIfNotExists(node, metaClassField);
+            Statement setMetaClassCode;
+            if (isFinal(metaClassField.getModifiers())) {
+                ConstantExpression text = new ConstantExpression("cannot set read-only meta class");
+                ConstructorCallExpression cce = new ConstructorCallExpression(ClassHelper.make(IllegalArgumentException.class), text);
+                setMetaClassCode = new ExpressionStatement(cce);
+            } else {
+                List list = new ArrayList();
+                list.add(new BytecodeInstruction() {
+                    public void visit(MethodVisitor mv) {
+                        /**
+                         * the code is (meta class is stored in 1):
+                         * this.metaClass = <1>
+                         */
+                        mv.visitVarInsn(ALOAD, 0);
+                        mv.visitVarInsn(ALOAD, 1);
+                        mv.visitFieldInsn(PUTFIELD, classInternalName,
+                                "metaClass", "Lgroovy/lang/MetaClass;");
+                        mv.visitInsn(RETURN);
+                    }
+                });
+                setMetaClassCode = new BytecodeSequence(list);
+            }
+
+            MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()),
+                    "setMetaClass",
+                    ACC_PUBLIC, ClassHelper.VOID_TYPE,
+                    SET_METACLASS_PARAMS, ClassNode.EMPTY_ARRAY,
+                    setMetaClassCode
+            );
+            if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation);
+        }
+
+        if (!node.hasMethod("invokeMethod", INVOKE_METHOD_PARAMS)) {
+            VariableExpression vMethods = new VariableExpression("method");
+            VariableExpression vArguments = new VariableExpression("arguments");
+            VariableScope blockScope = new VariableScope();
+            blockScope.putReferencedLocalVariable(vMethods);
+            blockScope.putReferencedLocalVariable(vArguments);
+
+            MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()),
+                    "invokeMethod",
+                    ACC_PUBLIC,
+                    ClassHelper.OBJECT_TYPE, INVOKE_METHOD_PARAMS,
+                    ClassNode.EMPTY_ARRAY,
+                    new BytecodeSequence(new BytecodeInstruction() {
+                        public void visit(MethodVisitor mv) {
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitVarInsn(ALOAD, 1);
+                            mv.visitVarInsn(ALOAD, 2);
+                            mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", true);
+                            mv.visitInsn(ARETURN);
+                        }
+                    })
+            );
+            if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation);
+        }
+
+        if (!node.hasMethod("getProperty", GET_PROPERTY_PARAMS)) {
+            MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()),
+                    "getProperty",
+                    ACC_PUBLIC,
+                    ClassHelper.OBJECT_TYPE,
+                    GET_PROPERTY_PARAMS,
+                    ClassNode.EMPTY_ARRAY,
+                    new BytecodeSequence(new BytecodeInstruction() {
+                        public void visit(MethodVisitor mv) {
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitVarInsn(ALOAD, 1);
+                            mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "getProperty", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", true);
+                            mv.visitInsn(ARETURN);
+                        }
+                    })
+            );
+            if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation);
+        }
+
+        if (!node.hasMethod("setProperty", SET_PROPERTY_PARAMS)) {
+            MethodNode methodNode = addMethod(node, !isAbstract(node.getModifiers()),
+                    "setProperty",
+                    ACC_PUBLIC,
+                    ClassHelper.VOID_TYPE,
+                    SET_PROPERTY_PARAMS,
+                    ClassNode.EMPTY_ARRAY,
+                    new BytecodeSequence(new BytecodeInstruction() {
+                        public void visit(MethodVisitor mv) {
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitMethodInsn(INVOKEVIRTUAL, classInternalName, "getMetaClass", "()Lgroovy/lang/MetaClass;", false);
+                            mv.visitVarInsn(ALOAD, 0);
+                            mv.visitVarInsn(ALOAD, 1);
+                            mv.visitVarInsn(ALOAD, 2);
+                            mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "setProperty", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V", true);
+                            mv.visitInsn(RETURN);
+                        }
+                    })
+            );
+            if (shouldAnnotate) methodNode.addAnnotation(generatedAnnotation);
+        }
+    }
+
+    /**
+     * Helper method to add a new method to a ClassNode.  Depending on the shouldBeSynthetic flag the
+     * call will either be made to ClassNode.addSyntheticMethod() or ClassNode.addMethod(). If a non-synthetic method
+     * is to be added the ACC_SYNTHETIC modifier is removed if it has been accidentally supplied.
+     */
+    protected MethodNode addMethod(ClassNode node, boolean shouldBeSynthetic, String name, int modifiers, ClassNode returnType, Parameter[] parameters,
+                                   ClassNode[] exceptions, Statement code) {
+        if (shouldBeSynthetic) {
+            return node.addSyntheticMethod(name, modifiers, returnType, parameters, exceptions, code);
+        } else {
+            return node.addMethod(name, modifiers & ~ACC_SYNTHETIC, returnType, parameters, exceptions, code);
+        }
+    }
+
+    @Deprecated
+    protected void addTimeStamp(ClassNode node) {
+    }
+
+    private static void checkReturnInObjectInitializer(List<Statement> init) {
+        CodeVisitorSupport cvs = new CodeVisitorSupport() {
+            @Override
+            public void visitClosureExpression(ClosureExpression expression) {
+                // return is OK in closures in object initializers
+            }
+
+            public void visitReturnStatement(ReturnStatement statement) {
+                throw new RuntimeParserException("'return' is not allowed in object initializer", statement);
+            }
+        };
+        for (Statement stm : init) {
+            stm.visit(cvs);
+        }
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+        CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
+            boolean firstMethodCall = true;
+            String type = null;
+
+            public void visitMethodCallExpression(MethodCallExpression call) {
+                if (!firstMethodCall) return;
+                firstMethodCall = false;
+                String name = call.getMethodAsString();
+                // the name might be null if the method name is a GString for example
+                if (name == null) return;
+                if (!name.equals("super") && !name.equals("this")) return;
+                type = name;
+                call.getArguments().visit(this);
+                type = null;
+            }
+
+            public void visitConstructorCallExpression(ConstructorCallExpression call) {
+                if (!call.isSpecialCall()) return;
+                type = call.getText();
+                call.getArguments().visit(this);
+                type = null;
+            }
+
+            public void visitVariableExpression(VariableExpression expression) {
+                if (type == null) return;
+                String name = expression.getName();
+                if (!name.equals("this") && !name.equals("super")) return;
+                throw new RuntimeParserException("cannot reference " + name + " inside of " + type + "(....) before supertype constructor has been called", expression);
+            }
+        };
+        Statement s = node.getCode();
+        if (s == null) {
+            return;
+        } else {
+            s.visit(new VerifierCodeVisitor(this));
+        }
+        s.visit(checkSuper);
+    }
+
+    public void visitMethod(MethodNode node) {
+        //GROOVY-3712 - if it's an MOP method, it's an error as they aren't supposed to exist before ACG is invoked
+        if (MopWriter.isMopMethod(node.getName())) {
+            throw new RuntimeParserException("Found unexpected MOP methods in the class node for " + classNode.getName() +
+                    "(" + node.getName() + ")", classNode);
+        }
+        this.methodNode = node;
+        adjustTypesIfStaticMainMethod(node);
+        addReturnIfNeeded(node);
+        Statement statement;
+        statement = node.getCode();
+        if (statement != null) statement.visit(new VerifierCodeVisitor(this));
+    }
+
+    private static void adjustTypesIfStaticMainMethod(MethodNode node) {
+        if (node.getName().equals("main") && node.isStatic()) {
+            Parameter[] params = node.getParameters();
+            if (params.length == 1) {
+                Parameter param = params[0];
+                if (param.getType() == null || param.getType() == ClassHelper.OBJECT_TYPE) {
+                    param.setType(ClassHelper.STRING_TYPE.makeArray());
+                    ClassNode returnType = node.getReturnType();
+                    if (returnType == ClassHelper.OBJECT_TYPE) {
+                        node.setReturnType(ClassHelper.VOID_TYPE);
+                    }
+                }
+            }
+        }
+    }
+
+    protected void addReturnIfNeeded(MethodNode node) {
+        ReturnAdder adder = new ReturnAdder();
+        adder.visitMethod(node);
+    }
+
+    public void visitField(FieldNode node) {
+    }
+
+    private boolean methodNeedsReplacement(MethodNode m) {
+        // no method found, we need to replace
+        if (m == null) return true;
+        // method is in current class, nothing to be done
+        if (m.getDeclaringClass() == this.getClassNode()) return false;
+        // do not overwrite final
+        if (isFinal(m.getModifiers())) return false;
+        return true;
+    }
+
+    public void visitProperty(PropertyNode node) {
+        String name = node.getName();
+        FieldNode field = node.getField();
+
+        String getterName = "get" + capitalize(name);
+        String setterName = "set" + capitalize(name);
+
+        int accessorModifiers = PropertyNodeUtils.adjustPropertyModifiersForMethod(node);
+
+        Statement getterBlock = node.getGetterBlock();
+        if (getterBlock == null) {
+            MethodNode getter = classNode.getGetterMethod(getterName, !node.isStatic());
+            if (getter == null && ClassHelper.boolean_TYPE == node.getType()) {
+                String secondGetterName = "is" + capitalize(name);
+                getter = classNode.getGetterMethod(secondGetterName);
+            }
+            if (!node.isPrivate() && methodNeedsReplacement(getter)) {
+                getterBlock = createGetterBlock(node, field);
+            }
+        }
+        Statement setterBlock = node.getSetterBlock();
+        if (setterBlock == null) {
+            // 2nd arg false below: though not usual, allow setter with non-void return type
+            MethodNode setter = classNode.getSetterMethod(setterName, false);
+            if (!node.isPrivate() && !isFinal(accessorModifiers) && methodNeedsReplacement(setter)) {
+                setterBlock = createSetterBlock(node, field);
+            }
+        }
+
+        int getterModifiers = accessorModifiers;
+        // don't make static accessors final
+        if (node.isStatic()) {
+            getterModifiers = ~Modifier.FINAL & getterModifiers;
+        }
+        if (getterBlock != null) {
+            MethodNode getter =
+                    new MethodNode(getterName, getterModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
+            getter.setSynthetic(true);
+            addPropertyMethod(getter);
+            visitMethod(getter);
+
+            if (ClassHelper.boolean_TYPE == node.getType() || ClassHelper.Boolean_TYPE == node.getType()) {
+                String secondGetterName = "is" + capitalize(name);
+                MethodNode secondGetter =
+                        new MethodNode(secondGetterName, getterModifiers, node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
+                secondGetter.setSynthetic(true);
+                addPropertyMethod(secondGetter);
+                visitMethod(secondGetter);
+            }
+        }
+        if (setterBlock != null) {
+            Parameter[] setterParameterTypes = {new Parameter(node.getType(), "value")};
+            MethodNode setter =
+                    new MethodNode(setterName, accessorModifiers, ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock);
+            setter.setSynthetic(true);
+            addPropertyMethod(setter);
+            visitMethod(setter);
+        }
+    }
+
+    protected void addPropertyMethod(MethodNode method) {
+        classNode.addMethod(method);
+        // GROOVY-4415 / GROOVY-4645: check that there's no abstract method which corresponds to this one
+        List<MethodNode> abstractMethods = classNode.getAbstractMethods();
+        if (abstractMethods == null) return;
+        String methodName = method.getName();
+        Parameter[] parameters = method.getParameters();
+        ClassNode methodReturnType = method.getReturnType();
+        for (MethodNode node : abstractMethods) {
+            if (!node.getDeclaringClass().equals(classNode)) continue;
+            if (node.getName().equals(methodName) && node.getParameters().length == parameters.length) {
+                if (parameters.length == 1) {
+                    // setter
+                    ClassNode abstractMethodParameterType = node.getParameters()[0].getType();
+                    ClassNode methodParameterType = parameters[0].getType();
+                    if (!methodParameterType.isDerivedFrom(abstractMethodParameterType) && !methodParameterType.implementsInterface(abstractMethodParameterType)) {
+                        continue;
+                    }
+                }
+                ClassNode nodeReturnType = node.getReturnType();
+                if (!methodReturnType.isDerivedFrom(nodeReturnType) && !methodReturnType.implementsInterface(nodeReturnType)) {
+                    continue;
+                }
+                // matching method, remove abstract status and use the same body
+                node.setModifiers(node.getModifiers() ^ ACC_ABSTRACT);
+                node.setCode(method.getCode());
+            }
+        }
+    }
+
+    public interface DefaultArgsAction {
+        void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
+    }
+
+    /**
+     * Creates a new helper method for each combination of default parameter expressions
+     */
+    protected void addDefaultParameterMethods(final ClassNode node) {
+        List methods = new ArrayList(node.getMethods());
+        addDefaultParameters(methods, new DefaultArgsAction() {
+            public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
+                final BlockStatement code = new BlockStatement();
+
+                MethodNode newMethod = new MethodNode(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code);
+
+                // GROOVY-5681 and GROOVY-5632
+                for (Expression argument : arguments.getExpressions()) {
+                    if (argument instanceof CastExpression) {
+                        argument = ((CastExpression) argument).getExpression();
+                    }
+                    if (argument instanceof ConstructorCallExpression) {
+                        ClassNode type = argument.getType();
+                        if (type instanceof InnerClassNode && ((InnerClassNode) type).isAnonymous()) {
+                            type.setEnclosingMethod(newMethod);
+                        }
+                    }
+
+                    // check whether closure shared variables refer to params with default values (GROOVY-5632)
+                    if (argument instanceof ClosureExpression) {
+                        final List<Parameter> newMethodNodeParameters = Arrays.asList(newParams);
+
+                        CodeVisitorSupport visitor = new CodeVisitorSupport() {
+                            @Override
+                            public void visitVariableExpression(VariableExpression expression) {
+                                Variable v = expression.getAccessedVariable();
+                                if (!(v instanceof Parameter)) return;
+
+                                Parameter param = (Parameter) v;
+                                if (param.hasInitialExpression() && code.getVariableScope().getDeclaredVariable(param.getName()) == null && !newMethodNodeParameters.contains(param)) {
+
+                                    VariableExpression localVariable = new VariableExpression(param.getName(), ClassHelper.makeReference());
+                                    DeclarationExpression declarationExpression = new DeclarationExpression(localVariable, Token.newSymbol(Types.EQUAL, -1, -1), new ConstructorCallExpression(ClassHelper.makeReference(), param.getInitialExpression()));
+
+                                    code.addStatement(new ExpressionStatement(declarationExpression));
+                                    code.getVariableScope().putDeclaredVariable(localVariable);
+                                }
+                            }
+                        };
+
+                        visitor.visitClosureExpression((ClosureExpression) argument);
+                    }
+                }
+
+                MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
+                expression.setMethodTarget(method);
+                expression.setImplicitThis(true);
+
+                if (method.isVoidMethod()) {
+                    code.addStatement(new ExpressionStatement(expression));
+                } else {
+                    code.addStatement(new ReturnStatement(expression));
+                }
+
+                List<AnnotationNode> annotations = method.getAnnotations();
+                if (annotations != null) {
+                    newMethod.addAnnotations(annotations);
+                }
+                MethodNode oldMethod = node.getDeclaredMethod(method.getName(), newParams);
+                if (oldMethod != null) {
+                    throw new RuntimeParserException(
+                            "The method with default parameters \"" + method.getTypeDescriptor() +
+                                    "\" defines a method \"" + newMethod.getTypeDescriptor() +
+                                    "\" that is already defined.",
+                            method);
+                }
+                addPropertyMethod(newMethod);
+                newMethod.setGenericsTypes(method.getGenericsTypes());
+                newMethod.putNodeMetaData(DEFAULT_PARAMETER_GENERATED, true);
+            }
+        });
+    }
+
+    protected void addDefaultParameterConstructors(final ClassNode node) {
+        List methods = new ArrayList(node.getDeclaredConstructors());
+        addDefaultParameters(methods, new DefaultArgsAction() {
+            public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
+                ConstructorNode ctor = (ConstructorNode) method;
+                ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments);
+                Statement code = new ExpressionStatement(expression);
+                addConstructor(newParams, ctor, code, node);
+            }
+        });
+    }
+
+    protected void addConstructor(Parameter[] newParams, ConstructorNode ctor, Statement code, ClassNode node) {
+        node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
+    }
+
+    /**
+     * Creates a new helper method for each combination of default parameter expressions
+     */
+    protected void addDefaultParameters(List methods, DefaultArgsAction action) {
+        for (Object next : methods) {
+            MethodNode method = (MethodNode) next;
+            if (method.hasDefaultValue()) {
+                addDefaultParameters(action, method);
+            }
+        }
+    }
+
+    protected void addDefaultParameters(DefaultArgsAction action, MethodNode method) {
+        Parameter[] parameters = method.getParameters();
+        int counter = 0;
+        List paramValues = new ArrayList();
+        int size = parameters.length;
+        for (int i = size - 1; i >= 0; i--) {
+            Parameter parameter = parameters[i];
+            if (parameter != null && parameter.hasInitialExpression()) {
+                paramValues.add(i);
+                paramValues.add(
+                        new CastExpression(
+                                parameter.getType(),
+                                parameter.getInitialExpression()
+                        )
+                );
+                counter++;
+            }
+        }
+
+        for (int j = 1; j <= counter; j++) {
+            Parameter[] newParams = new Parameter[parameters.length - j];
+            ArgumentListExpression arguments = new ArgumentListExpression();
+            int index = 0;
+            int k = 1;
+            for (Parameter parameter : parameters) {
+                if (parameter == null) {
+                    throw new GroovyBugError("Parameter should not be null for method " + methodNode.getName());
+                } else {
+                    if (k > counter - j && parameter.hasInitialExpression()) {
+                        arguments.addExpression(
+                                new CastExpression(
+                                        parameter.getType(),
+                                        parameter.getInitialExpression()
+                                )
+                        );
+                        k++;
+                    } else if (parameter.hasInitialExpression()) {
+                        newParams[index++] = parameter;
+                        arguments.addExpression(
+                                new CastExpression(
+                                        parameter.getType(),
+                                        new VariableExpression(parameter.getName())
+                                )
+                        );
+                        k++;
+                    } else {
+                        newParams[index++] = parameter;
+                        arguments.addExpression(
+                                new CastExpression(
+                                        parameter.getType(),
+                                        new VariableExpression(parameter.getName())
+                                )
+                        );
+                    }
+                }
+            }
+            action.call(arguments, newParams, method);
+        }
+
+        for (Parameter parameter : parameters) {
+            // remove default expression and store it as node metadata
+            parameter.putNodeMetaData(Verifier.INITIAL_EXPRESSION, parameter.getInitialExpression());
+            parameter.setInitialExpression(null);
+        }
+    }
+
+    protected void addClosureCode(InnerClassNode node) {
+        // add a new invoke
+    }
+
+    protected void addInitialization(final ClassNode node) {
+        boolean addSwapInit = moveOptimizedConstantsInitialization(node);
+
+        for (ConstructorNode cn : node.getDeclaredConstructors()) {
+            addInitialization(node, cn);
+        }
+
+        if (addSwapInit) {
+            BytecodeSequence seq = new BytecodeSequence(
+                    new BytecodeInstruction() {
+                        @Override
+                        public void visit(MethodVisitor mv) {
+                            mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(node), SWAP_INIT, "()V", false);
+                        }
+                    });
+
+            List<Statement> swapCall = new ArrayList<Statement>(1);
+            swapCall.add(seq);
+            node.addStaticInitializerStatements(swapCall, true);
+        }
+    }
+
+    protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
+        Statement firstStatement = constructorNode.getFirstStatement();
+        // if some transformation decided to generate constructor then it probably knows who it does
+        if (firstStatement instanceof BytecodeSequence)
+            return;
+
+        ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
+
+        // in case of this(...) let the other constructor do the init
+        if (first != null && (first.isThisCall())) return;
+
+        List<Statement> statements = new ArrayList<Statement>();
+        List<Statement> staticStatements = new ArrayList<Statement>();
+        final boolean isEnum = node.isEnum();
+        List<Statement> initStmtsAfterEnumValuesInit = new ArrayList<Statement>();
+        Set<String> explicitStaticPropsInEnum = new HashSet<String>();
+        if (isEnum) {
+            for (PropertyNode propNode : node.getProperties()) {
+                if (!propNode.isSynthetic() && propNode.getField().isStatic()) {
+                    explicitStaticPropsInEnum.add(propNode.getField().getName());
+                }
+            }
+            for (FieldNode fieldNode : node.getFields()) {
+                if (!fieldNode.isSynthetic() && fieldNode.isStatic() && fieldNode.getType() != node) {
+                    explicitStaticPropsInEnum.add(fieldNode.getName());
+                }
+            }
+        }
+
+        if (!Traits.isTrait(node)) {
+            for (FieldNode fn : node.getFields()) {
+                addFieldInitialization(statements, staticStatements, fn, isEnum,
+                        initStmtsAfterEnumValuesInit, explicitStaticPropsInEnum);
+            }
+        }
+
+        statements.addAll(node.getObjectInitializerStatements());
+
+        Statement code = constructorNode.getCode();
+        BlockStatement block = new BlockStatement();
+        List<Statement> otherStatements = block.getStatements();
+        if (code instanceof BlockStatement) {
+            block = (BlockStatement) code;
+            otherStatements = block.getStatements();
+        } else if (code != null) {
+            otherStatements.add(code);
+        }
+        if (!otherStatements.isEmpty()) {
+            if (first != null) {
+                // it is super(..) since this(..) is already covered
+                otherStatements.remove(0);
+                statements.add(0, firstStatement);
+            }
+            Statement stmtThis$0 = getImplicitThis$0StmtIfInnerClass(otherStatements);
+            if (stmtThis$0 != null) {
+                // since there can be field init statements that depend on method/property dispatching
+                // that uses this$0, it needs to bubble up before the super call itself (GROOVY-4471)
+                statements.add(0, stmtThis$0);
+            }
+            statements.addAll(otherStatements);
+        }
+        BlockStatement newBlock = new BlockStatement(statements, block.getVariableScope());
+        newBlock.setSourcePosition(block);
+        constructorNode.setCode(newBlock);
+
+
+        if (!staticStatements.isEmpty()) {
+            if (isEnum) {
+                /*
+                 * GROOVY-3161: initialize statements for explicitly declared static fields
+                 * inside an enum should come after enum values are initialized
+                 */
+                staticStatements.removeAll(initStmtsAfterEnumValuesInit);
+                node.addStaticInitializerStatements(staticStatements, true);
+                if (!initStmtsAfterEnumValuesInit.isEmpty()) {
+                    node.positionStmtsAfterEnumInitStmts(initStmtsAfterEnumValuesInit);
+                }
+            } else {
+                node.addStaticInitializerStatements(staticStatements, true);
+            }
+        }
+    }
+
+    /*
+    *  when InnerClassVisitor adds this.this$0 = $p$n, it adds it as a BlockStatement having that
+    *  ExpressionStatement
+    */
+    private Statement getImplicitThis$0StmtIfInnerClass(List<Statement> otherStatements) {
+        if (!(classNode instanceof InnerClassNode)) return null;
+        for (Statement stmt : otherStatements) {
+            if (stmt instanceof BlockStatement) {
+                List<Statement> stmts = ((BlockStatement) stmt).getStatements();
+                for (Statement bstmt : stmts) {
+                    if (bstmt instanceof ExpressionStatement) {
+                        if (extractImplicitThis$0StmtIfInnerClassFromExpression(stmts, bstmt)) return bstmt;
+                    }
+                }
+            } else if (stmt instanceof ExpressionStatement) {
+                if (extractImplicitThis$0StmtIfInnerClassFromExpression(otherStatements, stmt)) return stmt;
+            }
+        }
+        return null;
+    }
+
+    private static boolean extractImplicitThis$0StmtIfInnerClassFromExpression(final List<Statement> stmts, final Statement bstmt) {
+        Expression expr = ((ExpressionStatement) bstmt).getExpression();
+        if (expr instanceof BinaryExpression) {
+            Expression lExpr = ((BinaryExpression) expr).getLeftExpression();
+            if (lExpr instanceof FieldExpression) {
+                if ("this$0".equals(((FieldExpression) lExpr).getFieldName())) {
+                    stmts.remove(bstmt); // remove from here and let the caller reposition it
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
+        if (code == null || !(code instanceof ExpressionStatement)) return null;
+
+        Expression expression = ((ExpressionStatement) code).getExpression();
+        if (!(expression instanceof ConstructorCallExpression)) return null;
+        ConstructorCallExpression cce = (ConstructorCallExpression) expression;
+        if (cce.isSpecialCall()) return cce;
+        return null;
+    }
+
+    protected void addFieldInitialization(List list, List staticList, FieldNode fieldNode,
+                                          boolean isEnumClassNode, List initStmtsAfterEnumValuesInit, Set explicitStaticPropsInEnum) {
+        Expression expression = fieldNode.getInitialExpression();
+        if (expression != null) {
+            final FieldExpression fe = new FieldExpression(fieldNode);
+            if (fieldNode.getType().equals(ClassHelper.REFERENCE_TYPE) && ((fieldNode.getModifiers() & Opcodes.ACC_SYNTHETIC) != 0)) {
+                fe.setUseReferenceDirectly(true);
+            }
+            ExpressionStatement statement =
+                    new ExpressionStatement(
+                            new BinaryExpression(
+                                    fe,
+                                    Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
+                                    expression));
+            if (fieldNode.isStatic()) {
+                // GROOVY-3311: pre-defined constants added by groovy compiler for numbers/characters should be
+                // initialized first so that code dependent on it does not see their values as empty
+                Expression initialValueExpression = fieldNode.getInitialValueExpression();
+                if (initialValueExpression instanceof ConstantExpression) {
+                    ConstantExpression cexp = (ConstantExpression) initialValueExpression;
+                    cexp = transformToPrimitiveConstantIfPossible(cexp);
+                    if (fieldNode.isFinal() && ClassHelper.isStaticConstantInitializerType(cexp.getType()) && cexp.getType().equals(fieldNode.getType())) {
+                        return; // GROOVY-5150: primitive type constants will be initialized directly
+                    }
+                    staticList.add(0, statement);
+                } else {
+                    staticList.add(statement);
+                }
+                fieldNode.setInitialValueExpression(null); // to avoid double initialization in case of several constructors
+                /*
+                 * If it is a statement for an explicitly declared static field inside an enum, store its
+                 * reference. For enums, they need to be handled differently as such init statements should
+                 * come after the enum values have been initialized inside <clinit> block. GROOVY-3161.
+                 */
+                if (isEnumClassNode && explicitStaticPropsInEnum.contains(fieldNode.getName())) {
+                    initStmtsAfterEnumValuesInit.add(statement);
+                }
+            } else {
+                list.add(statement);
+            }
+        }
+    }
+
+    /**
+     * Capitalizes the start of the given bean property name
+     */
+    public static String capitalize(String name) {
+        return MetaClassHelper.capitalize(name);
+    }
+
+    protected Statement createGetterBlock(PropertyNode propertyNode, final FieldNode field) {
+        return new BytecodeSequence(new BytecodeInstruction() {
+            public void visit(MethodVisitor mv) {
+                if (field.isStatic()) {
+                    mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                } else {
+                    mv.visitVarInsn(ALOAD, 0);
+                    mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                }
+                BytecodeHelper.doReturn(mv, field.getType());
+            }
+        });
+    }
+
+    protected Statement createSetterBlock(PropertyNode propertyNode, final FieldNode field) {
+        return new BytecodeSequence(new BytecodeInstruction() {
+            public void visit(MethodVisitor mv) {
+                if (field.isStatic()) {
+                    BytecodeHelper.load(mv, field.getType(), 0);
+                    mv.visitFieldInsn(PUTSTATIC, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                } else {
+                    mv.visitVarInsn(ALOAD, 0);
+                    BytecodeHelper.load(mv, field.getType(), 1);
+                    mv.visitFieldInsn(PUTFIELD, BytecodeHelper.getClassInternalName(classNode), field.getName(), BytecodeHelper.getTypeDescription(field.getType()));
+                }
+                mv.visitInsn(RETURN);
+            }
+        });
+    }
+
+    public void visitGenericType(GenericsType genericsType) {
+
+    }
+
+    public static Long getTimestampFromFieldName(String fieldName) {
+        if (fieldName.startsWith(__TIMESTAMP__)) {
+            try {
+                return Long.decode(fieldName.substring(__TIMESTAMP__.length()));
+            } catch (NumberFormatException e) {
+                return Long.MAX_VALUE;
+            }
+        }
+        return null;
+    }
+
+    public static long getTimestamp(Class clazz) {
+        if (clazz.getClassLoader() instanceof GroovyClassLoader.InnerLoader) {
+            GroovyClassLoader.InnerLoader innerLoader = (GroovyClassLoader.InnerLoader) clazz.getClassLoader();
+            return innerLoader.getTimeStamp();
+        }
+
+        final Field[] fields = clazz.getFields();
+        for (int i = 0; i != fields.length; ++i) {
+            if (isStatic(fields[i].getModifiers())) {
+                Long timestamp = getTimestampFromFieldName(fields[i].getName());
+                if (timestamp != null) {
+                    return timestamp;
+                }
+            }
+        }
+        return Long.MAX_VALUE;
+    }
+
+    protected void addCovariantMethods(ClassNode classNode) {
+        Map methodsToAdd = new HashMap();
+        Map genericsSpec = new HashMap();
+
+        // unimplemented abstract methods from interfaces
+        Map<String, MethodNode> abstractMethods = ClassNodeUtils.getDeclaredMethodsFromInterfaces(classNode);
+        Map<String, MethodNode> allInterfaceMethods = new HashMap<String, MethodNode>(abstractMethods);
+        ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(classNode, allInterfaceMethods);
+
+        List<MethodNode> declaredMethods = new ArrayList<MethodNode>(classNode.getMethods());
+        // remove all static, private and package private methods
+        for (Iterator methodsIterator = declaredMethods.iterator(); methodsIterator.hasNext(); ) {
+            MethodNode m = (MethodNode) methodsIterator.next();
+            abstractMethods.remove(m.getTypeDescriptor());
+            if (m.isStatic() || !(m.isPublic() || m.isProtected())) {
+                methodsIterator.remove();
+            }
+            MethodNode intfMethod = allInterfaceMethods.get(m.getTypeDescriptor());
+            if (intfMethod != null && ((m.getModifiers() & ACC_SYNTHETIC) == 0)
+                    && !m.isPublic() && !m.isStaticConstructor()) {
+                throw new RuntimeParserException("The method " + m.getName() +
+                        " should be public as it implements the corresponding method from interface " +
+                        intfMethod.getDeclaringClass(), m);
+
+            }
+        }
+
+        addCovariantMethods(classNode, declaredMethods, abstractMethods, methodsToAdd, genericsSpec);
+
+        Map<String, MethodNode> declaredMethodsMap = new HashMap<String, MethodNode>();
+        if (!methodsToAdd.isEmpty()) {
+            for (MethodNode mn : declaredMethods) {
+                declaredMethodsMap.put(mn.getTypeDescriptor(), mn);
+            }
+        }
+
+        for (Object o : methodsToAdd.entrySet()) {
+            Map.Entry entry = (Map.Entry) o;
+            MethodNode method = (MethodNode) entry.getValue();
+            // we skip bridge methods implemented in current class already
+            MethodNode mn = declaredMethodsMap.get(entry.getKey());
+            if (mn != null && mn.getDeclaringClass().equals(classNode)) continue;
+            addPropertyMethod(method);
+        }
+    }
+
+    private void addCovariantMethods(ClassNode classNode, List declaredMethods, Map abstractMethods, Map methodsToAdd, Map oldGenericsSpec) {
+        ClassNode sn = classNode.getUnresolvedSuperClass(false);
+
+        if (sn != null) {
+            Map genericsSpec = createGenericsSpec(sn, oldGenericsSpec);
+            List<MethodNode> classMethods = sn.getMethods();
+            // original class causing bridge methods for methods in super class
+            for (Object declaredMethod : declaredMethods) {
+                MethodNode method = (MethodNode) declaredMethod;
+                if (method.isStatic()) continue;
+                storeMissingCovariantMethods(classMethods, method, methodsToAdd, genericsSpec, false);
+            }
+            // super class causing bridge methods for abstract methods in original class
+            if (!abstractMethods.isEmpty()) {
+                for (Object classMethod : classMethods) {
+                    MethodNode method = (MethodNode) classMethod;
+                    if (method.isStatic()) continue;
+                    storeMissingCovariantMethods(abstractMethods.values(), method, methodsToAdd, Collections.EMPTY_MAP, true);
+                }
+            }
+
+            addCovariantMethods(sn.redirect(), declaredMethods, abstractMethods, methodsToAdd, genericsSpec);
+        }
+
+        ClassNode[] interfaces = classNode.getInterfaces();
+        for (ClassNode anInterface : interfaces) {
+            List interfacesMethods = anInterface.getMethods();
+            Map genericsSpec = createGenericsSpec(anInterface, oldGenericsSpec);
+            for (Object declaredMethod : declaredMethods) {
+                MethodNode method = (MethodNode) declaredMethod;
+                if (method.isStatic()) continue;
+                storeMissingCovariantMethods(interfacesMethods, method, methodsToAdd, genericsSpec, false);
+            }
+            addCovariantMethods(anInterface, declaredMethods, abstractMethods, methodsToAdd, genericsSpec);
+        }
+
+    }
+
+    private MethodNode getCovariantImplementation(final MethodNode oldMethod, final MethodNode overridingMethod, Map genericsSpec, boolean ignoreError) {
+        // method name
+        if (!oldMethod.getName().equals(overridingMethod.getName())) return null;
+        if ((overridingMethod.getModifiers() & ACC_BRIDGE) != 0) return null;
+        if (oldMethod.isPrivate()) return null;
+
+        // parameters
+        boolean normalEqualParameters = equalParametersNormal(overridingMethod, oldMethod);
+        boolean genericEqualParameters = equalParametersWithGenerics(overridingMethod, oldMethod, genericsSpec);
+        if (!normalEqualParameters && !genericEqualParameters) return null;
+
+        //correct to method level generics for the overriding method
+        genericsSpec = GenericsUtils.addMethodGenerics(overridingMethod, genericsSpec);
+
+        // return type
+        ClassNode mr = overridingMethod.getReturnType();
+        ClassNode omr = oldMethod.getReturnType();
+        boolean equalReturnType = mr.equals(omr);
+
+        ClassNode testmr = correctToGenericsSpec(genericsSpec, omr);
+        if (!isAssignable(mr, testmr)) {
+            if (ignoreError) return null;
+            throw new RuntimeParserException(
+                    "The return type of " +
+                            overridingMethod.getTypeDescriptor() +
+                            " in " + overridingMethod.getDeclaringClass().getName() +
+                            " is incompatible with " + testmr.getName() +
+                            " in " + oldMethod.getDeclaringClass().getName(),
+                    overridingMethod);
+        }
+
+        if (equalReturnType && normalEqualParameters) return null;
+
+        if ((oldMethod.getModifiers() & ACC_FINAL) != 0) {
+            throw new RuntimeParserException(
+                    "Cannot override final method " +
+                            oldMethod.getTypeDescriptor() +
+                            " in " + oldMethod.getDeclaringClass().getName(),
+                    overridingMethod);
+        }
+        if (oldMethod.isStatic() != overridingMethod.isStatic()) {
+            throw new RuntimeParserException(
+                    "Cannot override method " +
+                            oldMethod.getTypeDescriptor() +
+                            " in " + oldMethod.getDeclaringClass().getName() +
+                            " with disparate static modifier",
+                    overridingMethod);
+        }
+        if (!equalReturnType) {
+            boolean oldM = ClassHelper.isPrimitiveType(oldMethod.getReturnType());
+            boolean newM = ClassHelper.isPrimitiveType(overridingMethod.getReturnType());
+            if (oldM || newM) {
+                String message = "";
+                if (oldM && newM) {
+                    message = " with old and new method having different primitive return types";
+                } else if (newM) {
+                    message = " with new method having a primitive return type and old method not";
+                } else /* oldM */ {
+                    message = " with old method having a primitive return type and new method not";
+                }
+                throw new RuntimeParserException(
+                        "Cannot override method " +
+                                oldMethod.getTypeDescriptor() +
+                                " in " + oldMethod.getDeclaringClass().getName() +
+                                message,
+                        overridingMethod);
+            }
+        }
+
+        // if we reach this point we have at least one parameter or return type, that
+        // is different in its specified form. That means we have to create a bridge method!
+        MethodNode newMethod = new MethodNode(
+                oldMethod.getName(),
+                overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
+                cleanType(oldMethod.getReturnType()),
+                cleanParameters(oldMethod.getParameters()),
+                oldMethod.getExceptions(),
+                null
+        );
+        List instructions = new ArrayList(1);
+        instructions.add(
+                new BytecodeInstruction() {
+                    public void visit(MethodVisitor mv) {
+                        mv.visitVarInsn(ALOAD, 0);
+                        Parameter[] para = oldMethod.getParameters();
+                        Parameter[] goal = overridingMethod.getParameters();
+                        int doubleSlotOffset = 0;
+                        for (int i = 0; i < para.length; i++) {
+                            ClassNode type = para[i].getType();
+                            BytecodeHelper.load(mv, type, i + 1 + doubleSlotOffset);
+                            if (type.redirect() == ClassHelper.double_TYPE ||
+                                    type.redirect() == ClassHelper.long_TYPE) {
+                                doubleSlotOffset++;
+                            }
+                            if (!type.equals(goal[i].getType())) {
+                                BytecodeHelper.doCast(mv, goal[i].getType());
+                            }
+                        }
+                        mv.visitMethodInsn(INVOKEVIRTUAL, BytecodeHelper.getClassInternalName(classNode), overridingMethod.getName(), BytecodeHelper.getMethodDescriptor(overridingMethod.getReturnType(), overridingMethod.getParameters()), false);
+
+                        BytecodeHelper.doReturn(mv, oldMethod.getReturnType());
+                    }
+                }
+
+        );
+        newMethod.setCode(new BytecodeSequence(instructions));
+        return newMethod;
+    }
+
+    private boolean isAssignable(ClassNode node, ClassNode testNode) {
+        if (node.isArray() && testNode.isArray()) {
+            return isArrayAssignable(node.getComponentType(), testNode.getComponentType());
+        }
+        if (testNode.isInterface()) {
+            if (node.equals(testNode) || node.implementsInterface(testNode)) return true;
+        }
+        return node.isDerivedFrom(testNode);
+    }
+
+    private boolean isArrayAssignable(ClassNode node, ClassNode testNode) {
+        if (node.isArray() && testNode.isArray()) {
+            return isArrayAssignable(node.getComponentType(), testNode.getComponentType());
+        }
+        return isAssignable(node, testNode);
+    }
+
+    private static Parameter[] cleanParameters(Parameter[] parameters) {
+        Parameter[] params = new Parameter[parameters.length];
+        for (int i = 0; i < params.length; i++) {
+            params[i] = new Parameter(cleanType(parameters[i].getType()), parameters[i].getName());
+        }
+        return params;
+    }
+
+    private static ClassNode cleanType(ClassNode type) {
+        // todo: should this be directly handled by getPlainNodeReference?
+        if (type.isArray()) return cleanType(type.getComponentType()).makeArray();
+        return type.getPlainNodeReference();
+    }
+
+    private void storeMissingCovariantMethods(Collection methods, MethodNode method, Map methodsToAdd, Map genericsSpec, boolean ignoreError) {
+        for (Object next : methods) {
+            MethodNode toOverride = (MethodNode) next;
+            MethodNode bridgeMethod = getCovariantImplementation(toOverride, method, genericsSpec, ignoreError);
+            if (bridgeMethod == null) continue;
+            methodsToAdd.put(bridgeMethod.getTypeDescriptor(), bridgeMethod);
+            return;
+        }
+    }
+
+    private static boolean equalParametersNormal(MethodNode m1, MethodNode m2) {
+        Parameter[] p1 = m1.getParameters();
+        Parameter[] p2 = m2.getParameters();
+        if (p1.length != p2.length) return false;
+        for (int i = 0; i < p2.length; i++) {
+            ClassNode type = p2[i].getType();
+            ClassNode parameterType = p1[i].getType();
+            if (!parameterType.equals(type)) return false;
+        }
+        return true;
+    }
+
+    private static boolean equalParametersWithGenerics(MethodNode m1, MethodNode m2, Map genericsSpec) {
+        Parameter[] p1 = m1.getParameters();
+        Parameter[] p2 = m2.getParameters();
+        if (p1.length != p2.length) return false;
+        for (int i = 0; i < p2.length; i++) {
+            ClassNode type = p2[i].getType();
+            ClassNode genericsType = correctToGenericsSpec(genericsSpec, type);
+            ClassNode parameterType = p1[i].getType();
+            if (!parameterType.equals(genericsType)) return false;
+        }
+        return true;
+    }
+
+    private static boolean moveOptimizedConstantsInitialization(final ClassNode node) {
+        if (node.isInterface() && !Traits.isTrait(node)) return false;
+
+        final int mods = Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_PUBLIC;
+        String name = SWAP_INIT;
+        BlockStatement methodCode = new BlockStatement();
+
+        methodCode.addStatement(new SwapInitStatement());
+        boolean swapInitRequired = false;
+        for (FieldNode fn : node.getFields()) {
+            if (!fn.isStatic() || !fn.isSynthetic() || !fn.getName().startsWith("$const$")) continue;
+            if (fn.getInitialExpression() == null) continue;
+            final FieldExpression fe = new FieldExpression(fn);
+            if (fn.getType().equals(ClassHelper.REFERENCE_TYPE)) fe.setUseReferenceDirectly(true);
+            ConstantExpression init = (ConstantExpression) fn.getInitialExpression();
+            init = new ConstantExpression(init.getValue(), true);
+            ExpressionStatement statement =
+                    new ExpressionStatement(
+                            new BinaryExpression(
+                                    fe,
+                                    Token.newSymbol(Types.EQUAL, fn.getLineNumber(), fn.getColumnNumber()),
+                                    init));
+            fn.setInitialValueExpression(null);
+            methodCode.addStatement(statement);
+            swapInitRequired = true;
+        }
+
+        if (swapInitRequired) {
+            node.addSyntheticMethod(
+                    name, mods, ClassHelper.VOID_TYPE,
+                    Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, methodCode);
+        }
+
+        return swapInitRequired;
+    }
+
+    /**
+     * When constant expressions are created, the value is always wrapped to a non primitive type.
+     * Some constant expressions are optimized to return primitive types, but not all primitives are
+     * handled. This method guarantees to return a similar constant expression but with a primitive type
+     * instead of a boxed type.
+     * <p/>
+     * Additionally, single char strings are converted to 'char' types.
+     *
+     * @param constantExpression a constant expression
+     * @return the same instance of constant expression if the type is already primitive, or a primitive
+     * constant if possible.
+     */
+    public static ConstantExpression transformToPrimitiveConstantIfPossible(ConstantExpression constantExpression) {
+        Object value = constantExpression.getValue();
+        if (value == null) return constantExpression;
+        ConstantExpression result;
+        ClassNode type = constantExpression.getType();
+        if (ClassHelper.isPrimitiveType(type)) return constantExpression;
+        if (value instanceof String && ((String) value).length() == 1) {
+            result = new ConstantExpression(((String) value).charAt(0));
+            result.setType(ClassHelper.char_TYPE);
+        } else {
+            type = ClassHelper.getUnwrapper(type);
+            result = new ConstantExpression(value, true);
+            result.setType(type);
+        }
+        return result;
+    }
+
+    private static class SwapInitStatement extends BytecodeSequence {
+
+        private WriterController controller;
+
+        public SwapInitStatement() {
+            super(new SwapInitInstruction());
+            ((SwapInitInstruction) getInstructions().get(0)).statement = this;
+        }
+
+        @Override
+        public void visit(final GroovyCodeVisitor visitor) {
+            if (visitor instanceof AsmClassGenerator) {
+                AsmClassGenerator generator = (AsmClassGenerator) visitor;
+                controller = generator.getController();
+            }
+            super.visit(visitor);
+        }
+
+        private static class SwapInitInstruction extends BytecodeInstruction {
+            SwapInitStatement statement;
+
+            @Override
+            public void visit(final MethodVisitor mv) {
+                statement.controller.getCallSiteWriter().makeCallSiteArrayInitializer();
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java b/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
new file mode 100644
index 0000000..424aa7e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
@@ -0,0 +1,102 @@
+/*
+ *  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.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+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.VariableExpression;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Performs various checks on code inside methods and constructors
+ * including checking for valid field, variables names etc. that
+ * would otherwise lead to invalid code.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class VerifierCodeVisitor extends CodeVisitorSupport implements Opcodes {
+
+    private final Verifier verifier;
+
+    VerifierCodeVisitor(Verifier verifier) {
+        this.verifier = verifier;
+    }
+
+    public void visitForLoop(ForStatement expression) {
+        assertValidIdentifier(expression.getVariable().getName(), "for loop variable name", expression);
+        super.visitForLoop(expression);
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+        if (!expression.getField().isSynthetic()) {
+            assertValidIdentifier(expression.getFieldName(), "field name", expression);
+        }
+        super.visitFieldExpression(expression);
+    }
+
+    public void visitVariableExpression(VariableExpression expression) {
+        assertValidIdentifier(expression.getName(), "variable name", expression);
+        super.visitVariableExpression(expression);
+    }
+
+    public void visitListExpression(ListExpression expression) {
+        for (Expression element : expression.getExpressions()) {
+            if (element instanceof MapEntryExpression) {
+                throw new RuntimeParserException("No map entry allowed at this place", element);
+            }
+        }
+        super.visitListExpression(expression);
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression call) {
+        ClassNode callType = call.getType();
+        if (callType.isEnum() && !callType.equals(verifier.getClassNode())) {
+            throw new RuntimeParserException("Enum constructor calls are only allowed inside the enum class", call);
+        }
+    }
+
+    public static void assertValidIdentifier(String name, String message, ASTNode node) {
+        int size = name.length();
+        if (size <= 0) {
+            throw new RuntimeParserException("Invalid " + message + ". Identifier must not be empty", node);
+        }
+        char firstCh = name.charAt(0);
+        if (size == 1 && firstCh == '$') {
+            throw new RuntimeParserException("Invalid " + message + ". Must include a letter but only found: " + name, node);
+        }
+        if (!Character.isJavaIdentifierStart(firstCh)) {
+            throw new RuntimeParserException("Invalid " + message + ". Must start with a letter but was: " + name, node);
+        }
+
+        for (int i = 1; i < size; i++) {
+            char ch = name.charAt(i);
+            if (!Character.isJavaIdentifierPart(ch)) {
+                throw new RuntimeParserException("Invalid " + message + ". Invalid character at position: " + (i + 1) + " of value:  " + ch + " in name: " + name, node);
+            }
+        }
+    }
+}


Mime
View raw message