Merge "Synthetic constructor parameter annotation tests" into pi-dev
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
index cb49aef..3c22aa8 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementParameterTest.java
@@ -23,6 +23,7 @@
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Parameter;
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
@@ -154,7 +155,7 @@
     }
 
     // Tests for isAnnotationPresent and getDeclaredAnnotation.
-    public void testMethodDeclaredAnnotation() throws Exception {
+    public void testMethodDeclaredAnnotation_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0DeclaredAnnotation(
                 AnnotatedMethodClass.getMethodWithoutAnnotations(),
@@ -231,7 +232,7 @@
     }
 
     // Tests for isAnnotationPresent and getDeclaredAnnotation.
-    public void testConstructorDeclaredAnnotation() throws Exception {
+    public void testConstructorDeclaredAnnotation_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0DeclaredAnnotation(
                 AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
@@ -279,7 +280,7 @@
         assertGetDeclaredAnnotation(parameter, annotationType, expectedAnnotationString);
     }
 
-    public void testMethodGetDeclaredAnnotationsByType() throws Exception {
+    public void testMethodGetDeclaredAnnotationsByType_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0GetDeclaredAnnotationsByType(
                 AnnotatedMethodClass.getMethodWithoutAnnotations(),
@@ -315,7 +316,7 @@
                 container, EXPECT_EMPTY);
     }
 
-    public void testConstructorGetDeclaredAnnotationsByType() throws Exception {
+    public void testConstructorGetDeclaredAnnotationsByType_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0GetDeclaredAnnotationsByType(
                 AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
@@ -359,7 +360,7 @@
                 parameter, annotationType, expectedAnnotationStrings);
     }
 
-    public void testMethodGetAnnotationsByType() throws Exception {
+    public void testMethodGetAnnotationsByType_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0GetAnnotationsByType(
                 AnnotatedMethodClass.getMethodWithoutAnnotations(),
@@ -395,7 +396,7 @@
                 container, EXPECT_EMPTY);
     }
 
-    public void testConstructorGetAnnotationsByType() throws Exception {
+    public void testConstructorGetAnnotationsByType_repeated() throws Exception {
         Class<? extends Annotation> repeated = Repeated.class;
         checkParameter0GetAnnotationsByType(
                 AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
@@ -444,35 +445,104 @@
      * the enclosing object, is inserted by the compiler.
      */
     class InnerClass {
-        InnerClass(@Repeated(1) String p1) {}
+        InnerClass(@AnnotationA String p1) {}
     }
 
-    /** Special case testing for a compiler-generated constructor parameter. */
-    public void testImplicitConstructorParameters_singleAnnotation() throws Exception {
+    /** Special case testing for a compiler-generated constructor parameter. JLS 8.8.1, JLS 13.1. */
+    public void testImplicitConstructorParameters_innerClass() throws Exception {
         Constructor<InnerClass> constructor =
                 InnerClass.class.getDeclaredConstructor(
                         AnnotatedElementParameterTest.class, String.class);
         Parameter[] parameters = constructor.getParameters();
 
+        // The parameter annotation code behaves as if there are two parameters.
+
         // The compiler-generated constructor should have no annotations.
         Parameter parameter0 = parameters[0];
         AnnotatedElementTestSupport.assertGetAnnotationsByType(
-                parameter0, Repeated.class, new String[0]);
+                parameter0, AnnotationA.class, new String[0]);
         AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
-                parameter0, Repeated.class, new String[0]);
+                parameter0, AnnotationA.class, new String[0]);
         AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
-                parameter0, Repeated.class, null);
-        AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter0, Repeated.class, false);
+                parameter0, AnnotationA.class, null);
+        AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter0, AnnotationA.class, false);
 
         // The annotation should remain on the correct parameter.
         Parameter parameter1 = parameters[1];
         AnnotatedElementTestSupport.assertGetAnnotationsByType(
-                parameter1, Repeated.class, new String[] {"@Repeated(1)"});
+                parameter1, AnnotationA.class, new String[] {"@AnnotationA"});
         AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
-                parameter1, Repeated.class, new String[] {"@Repeated(1)"});
+                parameter1, AnnotationA.class, new String[] {"@AnnotationA"});
         AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
-                parameter1, Repeated.class, "@Repeated(1)");
+                parameter1, AnnotationA.class, "@AnnotationA");
         AnnotatedElementTestSupport.assertIsAnnotationPresent(
-                parameter1, Repeated.class, true);
+                parameter1, AnnotationA.class, true);
+    }
+
+    static abstract class AnonymousBaseClass {
+        public AnonymousBaseClass(@AnnotationA String p1) {}
+    }
+
+    /** Special case testing for a compiler-generated constructor parameter. JLS 13.1 */
+    public void testImplicitConstructorParameters_anonymousClass() throws Exception {
+        /*
+         * As an anonymous class the constructor will actually have two parameters: the first,
+         * referencing the enclosing object, is inserted by the compiler.
+         */
+        AnonymousBaseClass anonymousClassInstance = new AnonymousBaseClass("p1") {};
+
+        Constructor<? extends AnonymousBaseClass> constructor =
+                anonymousClassInstance.getClass().getDeclaredConstructor(
+                        AnnotatedElementParameterTest.class, String.class);
+        Parameter[] parameters = constructor.getParameters();
+        assertEquals(2, parameters.length);
+
+        // The parameter annotation code behaves as if there are two parameters.
+
+        // This is the synthetic parameter.
+        Parameter parameter0 = parameters[0];
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                parameter0, AnnotationA.class, new String[0]);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                parameter0, AnnotationA.class, new String[0]);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
+                parameter0, AnnotationA.class, null);
+        AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter0, AnnotationA.class, false);
+
+        // There's no annotation since we cannot annotate the parameter on the anonymous class.
+        Parameter parameter1 = parameters[1];
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                parameter1, AnnotationA.class, new String[0]);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                parameter1, AnnotationA.class, new String[0]);
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
+                parameter1, AnnotationA.class, null);
+        AnnotatedElementTestSupport.assertIsAnnotationPresent(parameter1, AnnotationA.class, false);
+    }
+
+    /**
+     * A static inner / nested member class will not have synthetic parameters and should behave
+     * like a top-level class.
+     */
+    static class StaticInnerClass {
+        StaticInnerClass(@AnnotationA String p1) {}
+    }
+
+    /** Special case testing for a compiler-generated constructor parameter. */
+    public void testImplicitConstructorParameters_staticInnerClass() throws Exception {
+        Constructor<StaticInnerClass> constructor =
+                StaticInnerClass.class.getDeclaredConstructor(String.class);
+        Parameter[] parameters = constructor.getParameters();
+        assertEquals(1, parameters.length);
+
+        Parameter parameter0 = parameters[0];
+        AnnotatedElementTestSupport.assertGetAnnotationsByType(
+                parameter0, AnnotationA.class, new String[] {"@AnnotationA"});
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotationsByType(
+                parameter0, AnnotationA.class, new String[] {"@AnnotationA"});
+        AnnotatedElementTestSupport.assertGetDeclaredAnnotation(
+                parameter0, AnnotationA.class, "@AnnotationA");
+        AnnotatedElementTestSupport.assertIsAnnotationPresent(
+                parameter0, AnnotationA.class, true);
     }
 }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
index b17fbff..80be3f5 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/AnnotatedElementTestSupport.java
@@ -24,6 +24,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Parameter;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -291,8 +292,8 @@
     }
 
     private static String createAnnotationTestString(Annotation annotation) {
-        return "@" + annotation.annotationType().getSimpleName() + createArgumentsTestString(
-                annotation);
+        return "@" + annotation.annotationType().getSimpleName()
+                + createArgumentsTestString(annotation);
     }
 
     private static String createArgumentsTestString(Annotation annotation) {
@@ -309,6 +310,6 @@
             String repeatedValuesString = joiner.toString();
             return "(" +  repeatedValuesString + ")";
         }
-        throw new AssertionError("Unknown annotation: " + annotation);
+        return "";
     }
 }
diff --git a/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
index 38d9899..96a83c2 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/annotations/ExecutableParameterTest.java
@@ -22,7 +22,9 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 
+import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationA;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationB;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationC;
 import libcore.java.lang.reflect.annotations.AnnotatedElementTestSupport.AnnotationD;
@@ -36,7 +38,7 @@
 
 /**
  * Tests for {@link Executable#getParameterAnnotations()} via the {@link Constructor} and
- * {@link Method} classes. See {@link AnnotatedElementParameterTest} for testing of the
+ * {@link Method} classes. See {@link  for testing of the
  * {@link java.lang.reflect.AnnotatedElement} methods.
  */
 public class ExecutableParameterTest extends TestCase {
@@ -50,46 +52,15 @@
     public void testMethodGetParameterAnnotations() throws Exception {
         Method methodWithoutAnnotatedParameters = MethodClass.class.getMethod(
                 "methodWithoutAnnotatedParameters", String.class, String.class);
-        Annotation[][] noParameterAnnotations =
-                methodWithoutAnnotatedParameters.getParameterAnnotations();
-        assertEquals(2, noParameterAnnotations.length);
+        Annotation[][] noParameterAnnotations = getParameterAnnotations(
+                methodWithoutAnnotatedParameters, 2);
         assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
         assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
 
         Method methodWithAnnotatedParameters = MethodClass.class.getMethod(
                 "methodWithAnnotatedParameters", String.class, String.class);
-        Annotation[][] parameterAnnotations =
-                methodWithAnnotatedParameters.getParameterAnnotations();
-        assertEquals(2, parameterAnnotations.length);
-        assertEquals(set(AnnotationB.class, AnnotationD.class),
-                annotationsToTypes(parameterAnnotations[0]));
-        assertEquals(set(AnnotationC.class, AnnotationD.class),
-                annotationsToTypes(parameterAnnotations[1]));
-    }
-
-    private static class ConstructorClass {
-        // No annotations.
-        public ConstructorClass(Integer parameter1, Integer parameter2) {}
-
-        // Annotations.
-        public ConstructorClass(@AnnotationB @AnnotationD String parameter1,
-                @AnnotationC @AnnotationD String parameter2) {}
-    }
-
-    public void testConstructorGetParameterAnnotations() throws Exception {
-        Constructor constructorWithoutAnnotatedParameters =
-                ConstructorClass.class.getDeclaredConstructor(Integer.class, Integer.class);
-        Annotation[][] noParameterAnnotations =
-                constructorWithoutAnnotatedParameters.getParameterAnnotations();
-        assertEquals(2, noParameterAnnotations.length);
-        assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
-        assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
-
-        Constructor constructorWithAnnotatedParameters =
-                ConstructorClass.class.getDeclaredConstructor(String.class, String.class);
-        Annotation[][] parameterAnnotations =
-                constructorWithAnnotatedParameters.getParameterAnnotations();
-        assertEquals(2, parameterAnnotations.length);
+        Annotation[][] parameterAnnotations = getParameterAnnotations(
+                methodWithAnnotatedParameters, 2);
         assertEquals(set(AnnotationB.class, AnnotationD.class),
                 annotationsToTypes(parameterAnnotations[0]));
         assertEquals(set(AnnotationC.class, AnnotationD.class),
@@ -149,29 +120,56 @@
 
 
     public void testMethodGetParameterAnnotations_repeated() throws Exception {
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodWithoutAnnotations(), EXPECT_EMPTY);
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodMultipleAnnotationOddity(),
                 "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodMultipleAnnotationExplicitSingle(),
                 "@Container({@Repeated(1)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodMultipleAnnotation(),
                 "@Container({@Repeated(1), @Repeated(2)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodSingleAnnotation(),
                 "@Repeated(1)");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodClass.getMethodStaticSingleAnnotation(),
                 "@Repeated(1)");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedMethodAbstractClass.getMethodAbstractSingleAnnotation(),
                 "@Repeated(1)");
 
     }
 
+    private static class ConstructorClass {
+        // No annotations.
+        public ConstructorClass(Integer parameter1, Integer parameter2) {}
+
+        // Annotations.
+        public ConstructorClass(@AnnotationB @AnnotationD String parameter1,
+                @AnnotationC @AnnotationD String parameter2) {}
+    }
+
+    public void testConstructorGetParameterAnnotations() throws Exception {
+        Constructor constructorWithoutAnnotatedParameters =
+                ConstructorClass.class.getDeclaredConstructor(Integer.class, Integer.class);
+        Annotation[][] noParameterAnnotations = getParameterAnnotations(
+                constructorWithoutAnnotatedParameters, 2);
+        assertEquals(set(), annotationsToTypes(noParameterAnnotations[0]));
+        assertEquals(set(), annotationsToTypes(noParameterAnnotations[1]));
+
+        Constructor constructorWithAnnotatedParameters =
+                ConstructorClass.class.getDeclaredConstructor(String.class, String.class);
+        Annotation[][] parameterAnnotations = getParameterAnnotations(
+                constructorWithAnnotatedParameters, 2);
+        assertEquals(set(AnnotationB.class, AnnotationD.class),
+                annotationsToTypes(parameterAnnotations[0]));
+        assertEquals(set(AnnotationC.class, AnnotationD.class),
+                annotationsToTypes(parameterAnnotations[1]));
+    }
+
     private static class AnnotatedConstructorClass {
         public AnnotatedConstructorClass(Boolean p0) {}
 
@@ -207,30 +205,95 @@
     }
 
     public void testConstructorGetParameterAnnotations_repeated() throws Exception {
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedConstructorClass.getConstructorWithoutAnnotations(),
                 EXPECT_EMPTY);
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedConstructorClass.getConstructorMultipleAnnotationOddity(),
                 "@Repeated(1)", "@Container({@Repeated(2), @Repeated(3)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedConstructorClass.getConstructorMultipleAnnotationExplicitSingle(),
                 "@Container({@Repeated(1)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedConstructorClass.getConstructorMultipleAnnotation(),
                 "@Container({@Repeated(1), @Repeated(2)})");
-        assertParameter0Annotations(
+        assertOnlyParameterAnnotations(
                 AnnotatedConstructorClass.getConstructorSingleAnnotation(),
                 "@Repeated(1)");
     }
 
-    private static void assertParameter0Annotations(
+    /**
+     * As an inner class the constructor will actually have two parameters: the first, referencing
+     * the enclosing object, is inserted by the compiler.
+     */
+    class InnerClass {
+        InnerClass(@AnnotationA String p1) {}
+    }
+
+    /** Special case testing for a compiler-generated constructor parameter. JLS 8.8.1, JLS 13.1. */
+    public void testImplicitConstructorParameters_innerClass() throws Exception {
+        Constructor<InnerClass> constructor =
+                InnerClass.class.getDeclaredConstructor(
+                        ExecutableParameterTest.class, String.class);
+
+        // The parameter annotation code behaves as if there are two parameters.
+        Annotation[][] annotations = getParameterAnnotations(constructor, 2);
+        assertAnnotationsMatch(annotations[0], new String[0]);
+        assertAnnotationsMatch(annotations[1], new String[] { "@AnnotationA" });
+    }
+
+    static abstract class AnonymousBaseClass {
+        public AnonymousBaseClass(@AnnotationA String p1) {}
+    }
+
+    /** Special case testing for a compiler-generated constructor parameter. JLS 13.1 */
+    public void testImplicitConstructorParameters_anonymousClass() throws Exception {
+        /*
+         * As an anonymous class the constructor will actually have two parameters: the first,
+         * referencing the enclosing object, is inserted by the compiler.
+         */
+        AnonymousBaseClass anonymousClassInstance = new AnonymousBaseClass("p1") {};
+
+        Constructor<? extends AnonymousBaseClass> constructor =
+                anonymousClassInstance.getClass().getDeclaredConstructor(
+                        ExecutableParameterTest.class, String.class);
+        // The parameter annotation code behaves as if there are two parameters.
+        Annotation[][] annotations = getParameterAnnotations(constructor, 2);
+        assertAnnotationsMatch(annotations[0], new String[0]);
+        assertAnnotationsMatch(annotations[1], new String[0]);
+    }
+
+    /**
+     * A static inner / nested member class will not have synthetic parameters and should behave
+     * like a top-level class.
+     */
+    static class StaticInnerClass {
+        StaticInnerClass(@AnnotationA String p1) {}
+    }
+
+    /** Special case testing for a compiler-generated constructor parameter. */
+    public void testImplicitConstructorParameters_staticInnerClass() throws Exception {
+        Constructor<StaticInnerClass> constructor =
+                StaticInnerClass.class.getDeclaredConstructor(String.class);
+        Parameter[] parameters = constructor.getParameters();
+        assertEquals(1, parameters.length);
+
+        Annotation[][] annotations = getParameterAnnotations(constructor, 1);
+        assertAnnotationsMatch(annotations[0], new String[] { "@AnnotationA" });
+    }
+
+    private static void assertOnlyParameterAnnotations(
             Executable executable, String... expectedAnnotationStrings) throws Exception {
-        Annotation[][] allAnnotations = executable.getParameterAnnotations();
-        final int expectedParameterCount = 1;
-        assertEquals(expectedParameterCount, allAnnotations.length);
+        Annotation[][] allAnnotations = getParameterAnnotations(executable, 1);
 
         Annotation[] p0Annotations = allAnnotations[0];
         assertAnnotationsMatch(p0Annotations, expectedAnnotationStrings);
     }
+
+    private static Annotation[][] getParameterAnnotations(
+            Executable executable, int expectedParameterAnnotationsSize) {
+        Annotation[][] allAnnotations = executable.getParameterAnnotations();
+        assertEquals(expectedParameterAnnotationsSize, allAnnotations.length);
+        return allAnnotations;
+    }
 }