You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/08/29 19:31:21 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-9871: class model: populate `AnnotationConstantExpression` values

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 2d150af049 GROOVY-9871: class model: populate `AnnotationConstantExpression` values
2d150af049 is described below

commit 2d150af0497e0e33f40b40b140c65cf1539c7956
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Jul 5 11:17:08 2022 -0500

    GROOVY-9871: class model: populate `AnnotationConstantExpression` values
---
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     | 363 +++++++++++----------
 .../ast/{Groovy7826Bug.java => Groovy7826.java}    |  28 +-
 src/test/org/codehaus/groovy/ast/Groovy9871.groovy |  46 +++
 3 files changed, 248 insertions(+), 189 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index d88d108fe5..57a0829b17 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -33,6 +33,7 @@ import org.codehaus.groovy.ast.GenericsType;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.PackageNode;
 import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
 import org.codehaus.groovy.ast.expr.ClassExpression;
 import org.codehaus.groovy.ast.expr.ConstantExpression;
 import org.codehaus.groovy.ast.expr.Expression;
@@ -81,11 +82,7 @@ public class Java8 implements VMPlugin {
     private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
     private static final Permission ACCESS_PERMISSION = new ReflectPermission("suppressAccessChecks");
 
-    public Java8() {
-        super();
-    }
-
-    public static GenericsType configureTypeVariableDefinition(ClassNode base, ClassNode[] cBounds) {
+    public static GenericsType configureTypeVariableDefinition(final ClassNode base, final ClassNode[] cBounds) {
         ClassNode redirect = base.redirect();
         base.setRedirect(null);
         GenericsType gt;
@@ -100,7 +97,7 @@ public class Java8 implements VMPlugin {
         return gt;
     }
 
-    private static ClassNode configureClass(Class<?> c) {
+    private static ClassNode configureClass(final Class<?> c) {
         if (c.isPrimitive()) {
             return ClassHelper.make(c);
         } else {
@@ -108,7 +105,7 @@ public class Java8 implements VMPlugin {
         }
     }
 
-    public static ClassNode configureTypeVariableReference(String name) {
+    public static ClassNode configureTypeVariableReference(final String name) {
         ClassNode cn = ClassHelper.makeWithoutCaching(name);
         cn.setGenericsPlaceHolder(true);
         ClassNode cn2 = ClassHelper.makeWithoutCaching(name);
@@ -119,121 +116,111 @@ public class Java8 implements VMPlugin {
         return cn;
     }
 
-    private static void setRetentionPolicy(RetentionPolicy value, AnnotationNode node) {
+    private static void setRetentionPolicy(final RetentionPolicy value, final AnnotationNode node) {
         switch (value) {
-            case RUNTIME:
-                node.setRuntimeRetention(true);
-                break;
-            case SOURCE:
-                node.setSourceRetention(true);
-                break;
-            case CLASS:
-                node.setClassRetention(true);
-                break;
-            default:
-                throw new GroovyBugError("unsupported Retention " + value);
+          case RUNTIME:
+            node.setRuntimeRetention(true);
+            break;
+          case SOURCE:
+            node.setSourceRetention(true);
+            break;
+          case CLASS:
+            node.setClassRetention(true);
+            break;
+          default:
+            throw new GroovyBugError("unsupported Retention " + value);
         }
     }
 
-    private static void setMethodDefaultValue(MethodNode mn, Method m) {
+    private static void setMethodDefaultValue(final MethodNode mn, final Method m) {
         ConstantExpression cExp = new ConstantExpression(m.getDefaultValue());
         mn.setCode(new ReturnStatement(cExp));
         mn.setAnnotationDefault(true);
     }
 
-    private static Constructor<MethodHandles.Lookup> getLookupConstructor() {
-        return LookupHolder.LOOKUP_Constructor;
-    }
-
     @Override
     public Class<?>[] getPluginDefaultGroovyMethods() {
         return new Class[]{PluginDefaultGroovyMethods.class};
     }
 
+    @Override
+    public Class<?>[] getPluginStaticGroovyMethods() {
+        return EMPTY_CLASS_ARRAY;
+    }
+
     @Override
     public int getVersion() {
         return 8;
     }
 
-    protected int getElementCode(ElementType value) {
+    protected int getElementCode(final ElementType value) {
         switch (value) {
-            case TYPE:
-                return AnnotationNode.TYPE_TARGET;
-            case CONSTRUCTOR:
-                return AnnotationNode.CONSTRUCTOR_TARGET;
-            case METHOD:
-                return AnnotationNode.METHOD_TARGET;
-            case FIELD:
-                return AnnotationNode.FIELD_TARGET;
-            case PARAMETER:
-                return AnnotationNode.PARAMETER_TARGET;
-            case LOCAL_VARIABLE:
-                return AnnotationNode.LOCAL_VARIABLE_TARGET;
-            case ANNOTATION_TYPE:
-                return AnnotationNode.ANNOTATION_TARGET;
-            case PACKAGE:
-                return AnnotationNode.PACKAGE_TARGET;
-            case TYPE_PARAMETER:
-                return AnnotationNode.TYPE_PARAMETER_TARGET;
-            case TYPE_USE:
-                return AnnotationNode.TYPE_USE_TARGET;
-        }
-        if ("MODULE".equals(value.name())) {
+          case TYPE:
+            return AnnotationNode.TYPE_TARGET;
+          case CONSTRUCTOR:
+            return AnnotationNode.CONSTRUCTOR_TARGET;
+          case METHOD:
+            return AnnotationNode.METHOD_TARGET;
+          case FIELD:
+            return AnnotationNode.FIELD_TARGET;
+          case PARAMETER:
+            return AnnotationNode.PARAMETER_TARGET;
+          case LOCAL_VARIABLE:
+            return AnnotationNode.LOCAL_VARIABLE_TARGET;
+          case ANNOTATION_TYPE:
+            return AnnotationNode.ANNOTATION_TARGET;
+          case PACKAGE:
+            return AnnotationNode.PACKAGE_TARGET;
+          case TYPE_PARAMETER:
+            return AnnotationNode.TYPE_PARAMETER_TARGET;
+          case TYPE_USE:
+            return AnnotationNode.TYPE_USE_TARGET;
+          default:
+            // falls through
+        }
+        if ("MODULE".equals(value.name())) { // JDK 9+
             return AnnotationNode.TYPE_TARGET;
         } else {
             throw new GroovyBugError("unsupported Target " + value);
         }
     }
 
-    @Deprecated
-    protected Parameter[] processParameters(CompileUnit compileUnit, Method m) {
-        java.lang.reflect.Parameter[] parameters = m.getParameters();
-        Type[] types = m.getGenericParameterTypes();
-        Parameter[] params = Parameter.EMPTY_ARRAY;
-        if (types.length > 0) {
-            params = new Parameter[types.length];
-            for (int i = 0; i < params.length; i++) {
-                java.lang.reflect.Parameter p = parameters[i];
-                String name = p.isNamePresent() ? p.getName() : "param" + i;
-                params[i] = makeParameter(compileUnit, types[i], m.getParameterTypes()[i], m.getParameterAnnotations()[i], name);
-            }
-        }
-        return params;
-    }
-
-    public void setAdditionalClassInformation(ClassNode cn) {
+    @Override
+    public void setAdditionalClassInformation(final ClassNode cn) {
         setGenericsTypes(cn);
     }
 
-    private void setGenericsTypes(ClassNode cn) {
+    private void setGenericsTypes(final ClassNode cn) {
         TypeVariable[] tvs = cn.getTypeClass().getTypeParameters();
         GenericsType[] gts = configureTypeVariable(tvs);
         cn.setGenericsTypes(gts);
     }
 
-    private GenericsType[] configureTypeVariable(TypeVariable[] tvs) {
-        if (tvs.length == 0) return null;
-        GenericsType[] gts = new GenericsType[tvs.length];
-        for (int i = 0; i < tvs.length; i++) {
+    private GenericsType[] configureTypeVariable(final TypeVariable[] tvs) {
+        final int n = tvs.length;
+        if (n == 0) return null;
+        GenericsType[] gts = new GenericsType[n];
+        for (int i = 0; i < n; i += 1) {
             gts[i] = configureTypeVariableDefinition(tvs[i]);
         }
         return gts;
     }
 
-    private GenericsType configureTypeVariableDefinition(TypeVariable tv) {
+    private GenericsType configureTypeVariableDefinition(final TypeVariable tv) {
         return configureTypeVariableDefinition(configureTypeVariableReference(tv.getName()), configureTypes(tv.getBounds()));
     }
 
-    private ClassNode[] configureTypes(Type[] types) {
-        if (types.length == 0) return null;
-        ClassNode[] nodes = new ClassNode[types.length];
-        for (int i = 0; i < types.length; i++) {
+    private ClassNode[] configureTypes(final Type[] types) {
+        final int n = types.length;
+        if (n == 0) return null;
+        ClassNode[] nodes = new ClassNode[n];
+        for (int i = 0; i < n; i += 1) {
             nodes[i] = configureType(types[i]);
         }
         return nodes;
     }
 
-    private ClassNode configureType(Type type) {
+    private ClassNode configureType(final Type type) {
         if (type instanceof WildcardType) {
             return configureWildcardType((WildcardType) type);
         } else if (type instanceof ParameterizedType) {
@@ -244,14 +231,14 @@ public class Java8 implements VMPlugin {
             return configureTypeVariableReference(((TypeVariable) type).getName());
         } else if (type instanceof Class) {
             return configureClass((Class<?>) type);
-        } else if (type==null) {
+        } else if (type == null) {
             throw new GroovyBugError("Type is null. Most probably you let a transform reuse existing ClassNodes with generics information, that is now used in a wrong context.");
         } else {
             throw new GroovyBugError("unknown type: " + type + " := " + type.getClass());
         }
     }
 
-    private ClassNode configureGenericArray(GenericArrayType genericArrayType) {
+    private ClassNode configureGenericArray(final GenericArrayType genericArrayType) {
         Type component = genericArrayType.getGenericComponentType();
         ClassNode node = configureType(component);
         return node.makeArray();
@@ -276,17 +263,18 @@ public class Java8 implements VMPlugin {
         return wt;
     }
 
-    private ClassNode configureParameterizedType(ParameterizedType parameterizedType) {
+    private ClassNode configureParameterizedType(final ParameterizedType parameterizedType) {
         ClassNode base = configureType(parameterizedType.getRawType());
         GenericsType[] gts = configureTypeArguments(parameterizedType.getActualTypeArguments());
         base.setGenericsTypes(gts);
         return base;
     }
 
-    private GenericsType[] configureTypeArguments(Type[] ta) {
-        if (ta.length == 0) return null;
-        GenericsType[] gts = new GenericsType[ta.length];
-        for (int i = 0; i < ta.length; i++) {
+    private GenericsType[] configureTypeArguments(final Type[] ta) {
+        final int n = ta.length;
+        if (n == 0) return null;
+        GenericsType[] gts = new GenericsType[n];
+        for (int i = 0; i < n; i += 1) {
             ClassNode t = configureType(ta[i]);
             if (ta[i] instanceof WildcardType) {
                 GenericsType[] gen = t.getGenericsTypes();
@@ -298,51 +286,10 @@ public class Java8 implements VMPlugin {
         return gts;
     }
 
-    public Class<?>[] getPluginStaticGroovyMethods() {
-        return EMPTY_CLASS_ARRAY;
-    }
-
-    private void setAnnotationMetaData(Annotation[] annotations, AnnotatedNode an) {
-        for (Annotation annotation : annotations) {
-            AnnotationNode node = new AnnotationNode(ClassHelper.make(annotation.annotationType()));
-            configureAnnotation(node, annotation);
-            an.addAnnotation(node);
-        }
-    }
-
-    @Deprecated
-    private void configureAnnotationFromDefinition(AnnotationNode definition, AnnotationNode root) {
-        VMPlugin plugin = VMPluginFactory.getPlugin();
-        plugin.configureAnnotationNodeFromDefinition(definition, root);
-    }
+    //
 
-    public void configureAnnotationNodeFromDefinition(AnnotationNode definition, AnnotationNode root) {
-        ClassNode type = definition.getClassNode();
-        final String typeName = type.getName();
-        if ("java.lang.annotation.Retention".equals(typeName)) {
-            Expression exp = definition.getMember("value");
-            if (!(exp instanceof PropertyExpression)) return;
-            PropertyExpression pe = (PropertyExpression) exp;
-            String name = pe.getPropertyAsString();
-            RetentionPolicy policy = RetentionPolicy.valueOf(name);
-            setRetentionPolicy(policy, root);
-        } else if ("java.lang.annotation.Target".equals(typeName)) {
-            Expression exp = definition.getMember("value");
-            if (!(exp instanceof ListExpression)) return;
-            ListExpression le = (ListExpression) exp;
-            int bitmap = 0;
-            for (Expression e : le.getExpressions()) {
-                if (!(e instanceof PropertyExpression)) return;
-                PropertyExpression element = (PropertyExpression) e;
-                String name = element.getPropertyAsString();
-                ElementType value = ElementType.valueOf(name);
-                bitmap |= getElementCode(value);
-            }
-            root.setAllowedTargets(bitmap);
-        }
-    }
-
-    public void configureAnnotation(AnnotationNode node) {
+    @Override
+    public void configureAnnotation(final AnnotationNode node) {
         ClassNode type = node.getClassNode();
         VMPlugin plugin = VMPluginFactory.getPlugin();
         List<AnnotationNode> annotations = type.getAnnotations();
@@ -354,7 +301,7 @@ public class Java8 implements VMPlugin {
         }
     }
 
-    private void configureAnnotation(AnnotationNode node, Annotation annotation) {
+    private void configureAnnotation(final AnnotationNode node, final Annotation annotation) {
         Class<?> type = annotation.annotationType();
         if (type == Retention.class) {
             Retention r = (Retention) annotation;
@@ -382,38 +329,82 @@ public class Java8 implements VMPlugin {
             for (Method declaredMethod : declaredMethods) {
                 try {
                     Object value = declaredMethod.invoke(annotation);
-                    Expression valueExpression = annotationValueToExpression(value);
-                    if (valueExpression == null)
-                        continue;
-                    node.setMember(declaredMethod.getName(), valueExpression);
+                    Expression valueExpression = toAnnotationValueExpression(value);
+                    if (valueExpression != null) node.setMember(declaredMethod.getName(), valueExpression);
+
                 } catch (IllegalAccessException | InvocationTargetException ignore) {
                 }
             }
         }
     }
 
-    private Expression annotationValueToExpression (Object value) {
+    private void setAnnotationMetaData(final Annotation[] annotations, final AnnotatedNode target) {
+        for (Annotation annotation : annotations) {
+            target.addAnnotation(toAnnotationNode(annotation));
+        }
+    }
+
+    private AnnotationNode toAnnotationNode(final Annotation annotation) {
+        ClassNode type = ClassHelper.make(annotation.annotationType());
+        AnnotationNode node = new AnnotationNode(type);
+        configureAnnotation(node, annotation);
+        return node;
+    }
+
+    private Expression toAnnotationValueExpression(final Object value) {
         if (value == null || value instanceof String || value instanceof Number || value instanceof Character || value instanceof Boolean)
             return new ConstantExpression(value);
 
         if (value instanceof Class)
             return new ClassExpression(ClassHelper.makeWithoutCaching((Class<?>)value));
 
+        if (value instanceof Annotation)
+            return new AnnotationConstantExpression(toAnnotationNode((Annotation)value));
+
         if (value instanceof Enum)
             return new PropertyExpression(new ClassExpression(ClassHelper.makeWithoutCaching(value.getClass())), value.toString());
 
         if (value.getClass().isArray()) {
-            ListExpression elementExprs = new ListExpression();
-            int len = Array.getLength(value);
-            for (int i = 0; i != len; ++i)
-                elementExprs.addExpression(annotationValueToExpression(Array.get(value, i)));
-            return elementExprs;
+            ListExpression list = new ListExpression();
+            for (int i = 0, n = Array.getLength(value); i < n; i += 1)
+                list.addExpression(toAnnotationValueExpression(Array.get(value, i)));
+            return list;
         }
 
         return null;
     }
 
-    public void configureClassNode(CompileUnit compileUnit, ClassNode classNode) {
+    //
+
+    @Override
+    public void configureAnnotationNodeFromDefinition(final AnnotationNode definition, final AnnotationNode root) {
+        ClassNode type = definition.getClassNode();
+        final String typeName = type.getName();
+        if ("java.lang.annotation.Retention".equals(typeName)) {
+            Expression exp = definition.getMember("value");
+            if (!(exp instanceof PropertyExpression)) return;
+            PropertyExpression pe = (PropertyExpression) exp;
+            String name = pe.getPropertyAsString();
+            RetentionPolicy policy = RetentionPolicy.valueOf(name);
+            setRetentionPolicy(policy, root);
+        } else if ("java.lang.annotation.Target".equals(typeName)) {
+            Expression exp = definition.getMember("value");
+            if (!(exp instanceof ListExpression)) return;
+            ListExpression le = (ListExpression) exp;
+            int bitmap = 0;
+            for (Expression e : le.getExpressions()) {
+                if (!(e instanceof PropertyExpression)) return;
+                PropertyExpression element = (PropertyExpression) e;
+                String name = element.getPropertyAsString();
+                ElementType value = ElementType.valueOf(name);
+                bitmap |= getElementCode(value);
+            }
+            root.setAllowedTargets(bitmap);
+        }
+    }
+
+    @Override
+    public void configureClassNode(final CompileUnit compileUnit, final ClassNode classNode) {
         try {
             Class<?> clazz = classNode.getTypeClass();
             Field[] fields = clazz.getDeclaredFields();
@@ -435,8 +426,8 @@ public class Java8 implements VMPlugin {
                 mn.setGenericsTypes(configureTypeVariable(m.getTypeParameters()));
                 classNode.addMethod(mn);
             }
-            Constructor[] constructors = clazz.getDeclaredConstructors();
-            for (Constructor ctor : constructors) {
+            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
+            for (Constructor<?> ctor : constructors) {
                 Parameter[] params = makeParameters(compileUnit, ctor.getGenericParameterTypes(), ctor.getParameterTypes(), getConstructorParameterAnnotations(ctor), ctor);
                 ClassNode[] exceptions = makeClassNodes(compileUnit, ctor.getGenericExceptionTypes(), ctor.getExceptionTypes());
                 ConstructorNode cn = classNode.addConstructor(ctor.getModifiers(), params, exceptions, null);
@@ -453,9 +444,9 @@ public class Java8 implements VMPlugin {
                 setAnnotationMetaData(clazz.getPackage().getAnnotations(), packageNode);
             }
         } catch (NoClassDefFoundError e) {
-            throw new NoClassDefFoundError("Unable to load class "+classNode.toString(false)+" due to missing dependency "+e.getMessage());
+            throw new NoClassDefFoundError("Unable to load class " + classNode.toString(false) + " due to missing dependency " + e.getMessage());
         } catch (MalformedParameterizedTypeException e) {
-            throw new RuntimeException("Unable to configure class node for class "+classNode.toString(false)+" due to malformed parameterized types", e);
+            throw new RuntimeException("Unable to configure class node for class " + classNode.toString(false) + " due to malformed parameterized types", e);
         }
     }
 
@@ -474,7 +465,7 @@ public class Java8 implements VMPlugin {
      * @param constructor the Constructor for which to return parameter annotations
      * @return array of arrays containing the annotations on the parameters of the given Constructor
      */
-    private Annotation[][] getConstructorParameterAnnotations(Constructor<?> constructor) {
+    private Annotation[][] getConstructorParameterAnnotations(final Constructor<?> constructor) {
         /*
          * TODO: Remove after JDK9 is the minimum JDK supported
          *
@@ -495,7 +486,7 @@ public class Java8 implements VMPlugin {
                 );
             }
             Annotation[][] adjusted = new Annotation[parameterCount][];
-            for (int i = 0; i < diff; i++) {
+            for (int i = 0; i < diff; i += 1) {
                 adjusted[i] = EMPTY_ANNOTATION_ARRAY;
             }
             System.arraycopy(annotations, 0, adjusted, diff, annotations.length);
@@ -504,20 +495,20 @@ public class Java8 implements VMPlugin {
         return annotations;
     }
 
-    private void makeInterfaceTypes(CompileUnit cu, ClassNode classNode, Class<?> clazz) {
+    private void makeInterfaceTypes(final CompileUnit cu, final ClassNode classNode, final Class<?> clazz) {
         Type[] interfaceTypes = clazz.getGenericInterfaces();
-        if (interfaceTypes.length == 0) {
+        final int n = interfaceTypes.length;
+        if (n == 0) {
             classNode.setInterfaces(ClassNode.EMPTY_ARRAY);
         } else {
-            ClassNode[] ret = new ClassNode[interfaceTypes.length];
-            for (int i = 0; i < interfaceTypes.length; i++) {
+            ClassNode[] ret = new ClassNode[n];
+            for (int i = 0; i < n; i += 1) {
                 Type type = interfaceTypes[i];
                 while (!(type instanceof Class)) {
                     ParameterizedType pt = (ParameterizedType) type;
                     Type t2 = pt.getRawType();
-                    if (t2==type) {
-                        throw new GroovyBugError("Cannot transform generic signature of "+clazz+
-                                " with generic interface "+interfaceTypes[i]+" to a class.");
+                    if (t2 == type) {
+                        throw new GroovyBugError("Cannot transform generic signature of " + clazz + " with generic interface " + interfaceTypes[i] + " to a class.");
                     }
                     type = t2;
                 }
@@ -527,15 +518,16 @@ public class Java8 implements VMPlugin {
         }
     }
 
-    private ClassNode[] makeClassNodes(CompileUnit cu, Type[] types, Class<?>[] cls) {
-        ClassNode[] nodes = new ClassNode[types.length];
-        for (int i = 0; i < nodes.length; i++) {
+    private ClassNode[] makeClassNodes(final CompileUnit cu, final Type[] types, final Class<?>[] cls) {
+        final int n = types.length;
+        ClassNode[] nodes = new ClassNode[n];
+        for (int i = 0; i < n; i += 1) {
             nodes[i] = makeClassNode(cu, types[i], cls[i]);
         }
         return nodes;
     }
 
-    private ClassNode makeClassNode(CompileUnit cu, Type t, Class<?> c) {
+    private ClassNode makeClassNode(final CompileUnit cu, Type t, final Class<?> c) {
         ClassNode back = null;
         if (cu != null) back = cu.getClass(c.getName());
         if (back == null) back = ClassHelper.make(c);
@@ -548,14 +540,30 @@ public class Java8 implements VMPlugin {
     }
 
     @Deprecated
-    protected Parameter makeParameter(CompileUnit cu, Type type, Class<?> cl, Annotation[] annotations, String name) {
+    protected Parameter[] processParameters(final CompileUnit compileUnit, final Method m) {
+        java.lang.reflect.Parameter[] parameters = m.getParameters();
+        Type[] types = m.getGenericParameterTypes();
+        Parameter[] params = Parameter.EMPTY_ARRAY;
+        if (types.length > 0) {
+            params = new Parameter[types.length];
+            for (int i = 0; i < params.length; i++) {
+                java.lang.reflect.Parameter p = parameters[i];
+                String name = p.isNamePresent() ? p.getName() : "param" + i;
+                params[i] = makeParameter(compileUnit, types[i], m.getParameterTypes()[i], m.getParameterAnnotations()[i], name);
+            }
+        }
+        return params;
+    }
+
+    @Deprecated
+    protected Parameter makeParameter(final CompileUnit cu, final Type type, final Class<?> cl, final Annotation[] annotations, final String name) {
         ClassNode cn = makeClassNode(cu, type, cl);
         Parameter parameter = new Parameter(cn, name);
         setAnnotationMetaData(annotations, parameter);
         return parameter;
     }
 
-    private Parameter[] makeParameters(CompileUnit cu, Type[] types, Class[] cls, Annotation[][] parameterAnnotations, Member member) {
+    private Parameter[] makeParameters(final CompileUnit cu, final Type[] types, final Class<?>[] cls, final Annotation[][] parameterAnnotations, final Member member) {
         Parameter[] params = Parameter.EMPTY_ARRAY;
         int n = types.length;
         if (n > 0) {
@@ -570,14 +578,14 @@ public class Java8 implements VMPlugin {
         return params;
     }
 
-    protected void fillParameterNames(String[] names, Member member) {
+    protected void fillParameterNames(final String[] names, final Member member) {
         try {
             java.lang.reflect.Parameter[] parameters = ((java.lang.reflect.Executable) member).getParameters();
             for (int i = 0, n = names.length; i < n; i += 1) {
                 names[i] = parameters[i].getName();
             }
         } catch (RuntimeException e) {
-            throw new GroovyBugError(e); // or Java5.fillParameterNames(names, member);
+            throw new GroovyBugError(e);
         }
     }
 
@@ -591,7 +599,7 @@ public class Java8 implements VMPlugin {
      * @return the check result
      */
     @Override
-    public boolean checkCanSetAccessible(AccessibleObject accessibleObject, Class<?> callerClass) {
+    public boolean checkCanSetAccessible(final AccessibleObject accessibleObject, final Class<?> callerClass) {
         SecurityManager sm = System.getSecurityManager();
         try {
             if (sm != null) {
@@ -602,7 +610,7 @@ public class Java8 implements VMPlugin {
         }
 
         if (accessibleObject instanceof Constructor) {
-            Constructor c = (Constructor) accessibleObject;
+            Constructor<?> c = (Constructor<?>) accessibleObject;
             if (c.getDeclaringClass() == Class.class) {
                 return false; // Cannot make a java.lang.Class constructor accessible
             }
@@ -612,12 +620,12 @@ public class Java8 implements VMPlugin {
     }
 
     @Override
-    public boolean checkAccessible(Class<?> callerClass, Class<?> declaringClass, int memberModifiers, boolean allowIllegalAccess) {
+    public boolean checkAccessible(final Class<?> callerClass, final Class<?> declaringClass, final int memberModifiers, final boolean allowIllegalAccess) {
         return true;
     }
 
     @Override
-    public boolean trySetAccessible(AccessibleObject ao) {
+    public boolean trySetAccessible(final AccessibleObject ao) {
         try {
             ao.setAccessible(true);
             return true;
@@ -629,12 +637,12 @@ public class Java8 implements VMPlugin {
     }
 
     @Override
-    public MetaMethod transformMetaMethod(MetaClass metaClass, MetaMethod metaMethod, Class<?> caller) {
+    public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod, final Class<?> caller) {
         return metaMethod;
     }
 
     @Override
-    public MetaMethod transformMetaMethod(MetaClass metaClass, MetaMethod metaMethod) {
+    public MetaMethod transformMetaMethod(final MetaClass metaClass, final MetaMethod metaMethod) {
         return transformMetaMethod(metaClass, metaMethod, null);
     }
 
@@ -644,7 +652,7 @@ public class Java8 implements VMPlugin {
     }
 
     @Override
-    public Object getInvokeSpecialHandle(Method method, Object receiver) {
+    public Object getInvokeSpecialHandle(final Method method, final Object receiver) {
         final Class<?> receiverType = receiver.getClass();
         try {
             return of(receiverType).unreflectSpecial(method, receiverType).bindTo(receiver);
@@ -653,7 +661,7 @@ public class Java8 implements VMPlugin {
         }
     }
 
-    private Object getInvokeSpecialHandleFallback(Method method, Object receiver) {
+    private Object getInvokeSpecialHandleFallback(final Method method, final Object receiver) {
         if (getLookupConstructor() == null) {
             throw new GroovyBugError("getInvokeSpecialHandle requires at least JDK 7 for private access to Lookup");
         }
@@ -676,9 +684,8 @@ public class Java8 implements VMPlugin {
     }
 
     @Override
-    public Object invokeHandle(Object handle, Object[] args) throws Throwable {
-        MethodHandle mh = (MethodHandle) handle;
-        return mh.invokeWithArguments(args);
+    public Object invokeHandle(final Object handle, final Object[] args) throws Throwable {
+        return ((MethodHandle) handle).invokeWithArguments(args);
     }
 
     public static MethodHandles.Lookup of(final Class<?> declaringClass) {
@@ -691,6 +698,10 @@ public class Java8 implements VMPlugin {
         }
     }
 
+    private static Constructor<MethodHandles.Lookup> getLookupConstructor() {
+        return LookupHolder.LOOKUP_Constructor;
+    }
+
     private static class LookupHolder {
         private static final Constructor<MethodHandles.Lookup> LOOKUP_Constructor;
 
@@ -698,21 +709,21 @@ public class Java8 implements VMPlugin {
             Constructor<MethodHandles.Lookup> lookup;
             try {
                 lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
-            } catch (final NoSuchMethodException ex) {
-                throw new IllegalStateException("Incompatible JVM", ex);
+            } catch (final NoSuchMethodException e) {
+                throw new IllegalStateException("Incompatible JVM", e);
             }
             try {
                 if (!lookup.isAccessible()) {
-                    final Constructor tmp = lookup;
+                    final Constructor<MethodHandles.Lookup> finalReference = lookup;
                     AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
-                        ReflectionUtils.trySetAccessible(tmp);
+                        ReflectionUtils.trySetAccessible(finalReference);
                         return null;
                     });
                 }
             } catch (SecurityException ignore) {
                 lookup = null;
-            } catch (RuntimeException re) {
-                throw re;
+            } catch (RuntimeException e) {
+                throw e;
             }
             LOOKUP_Constructor = lookup;
         }
diff --git a/src/test/org/codehaus/groovy/ast/Groovy7826Bug.java b/src/test/org/codehaus/groovy/ast/Groovy7826.java
similarity index 61%
rename from src/test/org/codehaus/groovy/ast/Groovy7826Bug.java
rename to src/test/org/codehaus/groovy/ast/Groovy7826.java
index ed6b4ab596..fb8745c52a 100644
--- a/src/test/org/codehaus/groovy/ast/Groovy7826Bug.java
+++ b/src/test/org/codehaus/groovy/ast/Groovy7826.java
@@ -19,23 +19,25 @@
 package org.codehaus.groovy.ast;
 
 import groovy.lang.GroovyShell;
-import groovy.test.GroovyTestCase;
 import org.codehaus.groovy.control.CompilerConfiguration;
+import org.junit.Test;
 
-public class Groovy7826Bug extends GroovyTestCase {
-  public void testComplexTypeArguments() throws Exception {
-    String script = "def f(org.codehaus.groovy.ast.Groovy7826Bug.C1 c1) { }";
+public final class Groovy7826 {
 
-    CompilerConfiguration config = new CompilerConfiguration();
-    config.getOptimizationOptions().put("asmResolving", false);
+    @Test
+    public void testComplexTypeArguments() throws Exception {
+        String script = "def f(" + getClass().getName() + ".C1 c1) { }";
 
-    GroovyShell shell = new GroovyShell(config);
-    shell.evaluate(script, "bug7826.groovy");
-  }
+        CompilerConfiguration config = new CompilerConfiguration();
+        config.getOptimizationOptions().put("asmResolving", false);
 
-  public static class C1<T2 extends C2<T2, T1>, T1 extends C1<T2, T1>> {
-  }
+        GroovyShell shell = new GroovyShell(config);
+        shell.evaluate(script, "bug7826.groovy");
+    }
 
-  public static class C2<T2 extends C2<T2, T1>, T1 extends C1<T2, T1>> {
-  }
+    public static class C1<T2 extends C2<T2, T1>, T1 extends C1<T2, T1>> {
+    }
+
+    public static class C2<T2 extends C2<T2, T1>, T1 extends C1<T2, T1>> {
+    }
 }
diff --git a/src/test/org/codehaus/groovy/ast/Groovy9871.groovy b/src/test/org/codehaus/groovy/ast/Groovy9871.groovy
new file mode 100644
index 0000000000..1db74bca01
--- /dev/null
+++ b/src/test/org/codehaus/groovy/ast/Groovy9871.groovy
@@ -0,0 +1,46 @@
+/*
+ *  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.ast
+
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.ListExpression
+import org.junit.Test
+
+final class Groovy9871 {
+
+    @Test
+    void testAnnotationConstantExpression() {
+        ClassNode cn = new ClassNode(org.codehaus.groovy.runtime.ResourceGroovyMethods)
+        // method with @NamedParam annotations that should be wrapped in @NamedParams container
+        MethodNode mn = cn.getMethod('traverse', new Parameter(ClassHelper.make(File), 'file'), new Parameter(ClassHelper.MAP_TYPE, 'options'))
+
+        List<AnnotationNode> annotations = mn.parameters[1].annotations
+
+        assert annotations.size() == 1
+        assert annotations[0].classNode.name == 'groovy.transform.NamedParams'
+        assert annotations[0].members.value instanceof ListExpression
+
+        List<Expression> expressions = annotations[0].members.value.expressions
+
+        assert expressions.size() > 1 // 12 currently
+        assert expressions[0] instanceof AnnotationConstantExpression
+        assert expressions[0].type.name == 'groovy.transform.NamedParam'
+    }
+}