ART: Fixes for constructor parameter annotations
Synthesize empty parameter annotations for implicit parameters on
constructors. Reflective methods for recovering parameter annotations
expect them to be present though they may not be present in the DEX file.
Bug: b/68033708
Test: art/test/run-test --host 715
Change-Id: I0827c7e71ff7c7e044fc9dd6c5aac639a0e1a4c6
diff --git a/test/715-clinit-implicit-parameter-annotations/src/Main.java b/test/715-clinit-implicit-parameter-annotations/src/Main.java
new file mode 100644
index 0000000..351e3a9
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/src/Main.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Parameter;
+
+public class Main {
+ // A simple parameter annotation
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationA {}
+
+ // A parameter annotation with additional state
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface AnnotationB {
+ String value() default "default-value";
+ }
+
+ // An inner class whose constructors with have an implicit
+ // argument for the enclosing instance.
+ public class Inner {
+ private final int number;
+ private final String text;
+ boolean flag;
+
+ Inner(@AnnotationA int number, String text) {
+ this.number = number;
+ this.text = text;
+ this.flag = false;
+ }
+
+ Inner(@AnnotationA int number, String text, @AnnotationB("x") boolean flag) {
+ this.number = number;
+ this.text = text;
+ this.flag = flag;
+ }
+ }
+
+ // An inner class whose constructors with have no implicit
+ // arguments for the enclosing instance.
+ public static class StaticInner {
+ private final int number;
+ private final String text;
+ boolean flag;
+
+ StaticInner(@AnnotationA int number, String text) {
+ this.number = number;
+ this.text = text;
+ this.flag = false;
+ }
+
+ StaticInner(@AnnotationB("foo") int number, String text, @AnnotationA boolean flag) {
+ this.number = number;
+ this.text = text;
+ this.flag = flag;
+ }
+ }
+
+ public enum ImportantNumber {
+ ONE(1.0),
+ TWO(2.0),
+ MANY(3.0, true);
+
+ private double doubleValue;
+ private boolean isLarge;
+
+ ImportantNumber(@AnnotationA double doubleValue) {
+ this.doubleValue = doubleValue;
+ this.isLarge = false;
+ }
+
+ ImportantNumber(@AnnotationB("x") double doubleValue, @AnnotationB("y") boolean isLarge) {
+ this.doubleValue = doubleValue;
+ this.isLarge = isLarge;
+ }
+ }
+
+ public enum BinaryNumber {
+ ZERO,
+ ONE;
+ }
+
+ private abstract static class AnonymousBase {
+ public AnonymousBase(@AnnotationA String s) {}
+ }
+
+ private static String annotationToNormalizedString(Annotation annotation) {
+ // String.replace() to accomodate different representation across VMs.
+ return annotation.toString().replace("\"", "");
+ }
+
+ private static void DumpConstructorParameterAnnotations(Class<?> cls) throws Throwable {
+ System.out.println(cls.getName());
+ for (Constructor c : cls.getDeclaredConstructors()) {
+ System.out.println(" " + c);
+ Annotation[][] annotations = c.getParameterAnnotations();
+ Parameter[] parameters = c.getParameters();
+ for (int i = 0; i < annotations.length; ++i) {
+ // Exercise java.lang.reflect.Executable.getParameterAnnotationsNative()
+ // which retrieves all annotations for the parameters.
+ System.out.print(" Parameter [" + i + "]:");
+ for (Annotation annotation : parameters[i].getAnnotations()) {
+ System.out.println(" Indexed : " + annotationToNormalizedString(annotation));
+ }
+ for (Annotation annotation : annotations[i]) {
+ System.out.println(" Array : " + annotationToNormalizedString(annotation));
+ }
+
+ // Exercise Parameter.getAnnotationNative() with
+ // retrieves a single parameter annotation according to type.
+ Object[] opaqueClasses = new Object[] {AnnotationA.class, AnnotationB.class};
+ for (Object opaqueClass : opaqueClasses) {
+ @SuppressWarnings("unchecked")
+ Class<? extends Annotation> annotationClass =
+ (Class<? extends Annotation>) opaqueClass;
+ Annotation annotation = parameters[i].getDeclaredAnnotation(annotationClass);
+ String hasAnnotation = (annotation != null ? "Yes" : "No");
+ System.out.println(" " + annotationClass.getName() + " " + hasAnnotation);
+
+ Annotation[] parameterAnnotations = parameters[i].getDeclaredAnnotationsByType(annotationClass);
+ for (Annotation parameterAnnotation : parameterAnnotations) {
+ System.out.println(" " + annotationToNormalizedString(parameterAnnotation));
+ }
+ }
+ }
+ }
+ }
+
+ private Class<?> getLocalClassWithEnclosingInstanceCapture() {
+ class LocalClass {
+ private final int integerValue;
+
+ LocalClass(@AnnotationA int integerValue) {
+ this.integerValue = integerValue;
+ }
+ }
+ return LocalClass.class;
+ }
+
+ private Class<?> getLocalClassWithEnclosingInstanceAndLocalCapture() {
+ final long CAPTURED_VALUE = System.currentTimeMillis();
+ class LocalClassWithCapture {
+ private final String value;
+ private final long capturedValue;
+
+ LocalClassWithCapture(@AnnotationA String p1) {
+ this.value = p1;
+ this.capturedValue = CAPTURED_VALUE;
+ }
+ }
+ return LocalClassWithCapture.class;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ // A local class declared in a static context (0 implicit parameters).
+ class LocalClassStaticContext {
+ private final int value;
+
+ LocalClassStaticContext(@AnnotationA int p0) {
+ this.value = p0;
+ }
+ }
+
+ final long CAPTURED_VALUE = System.currentTimeMillis();
+ // A local class declared in a static context with a capture (1 implicit parameters).
+ class LocalClassStaticContextWithCapture {
+ private final long capturedValue;
+ private final String argumentValue;
+
+ LocalClassStaticContextWithCapture(@AnnotationA String p1) {
+ this.capturedValue = CAPTURED_VALUE;
+ this.argumentValue = p1;
+ }
+ }
+
+ // Another local class declared in a static context with a capture (1 implicit parameters).
+ class LocalClassStaticContextWithCaptureAlternateOrdering {
+ private final String argumentValue;
+ private final long capturedValue;
+
+ LocalClassStaticContextWithCaptureAlternateOrdering(@AnnotationA String p1) {
+ this.argumentValue = p1;
+ this.capturedValue = CAPTURED_VALUE;
+ }
+ }
+
+ DumpConstructorParameterAnnotations(Main.class);
+ DumpConstructorParameterAnnotations(LocalClassStaticContext.class);
+ DumpConstructorParameterAnnotations(LocalClassStaticContextWithCapture.class);
+ DumpConstructorParameterAnnotations(LocalClassStaticContextWithCaptureAlternateOrdering.class);
+ Main m = new Main();
+ DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceCapture());
+ DumpConstructorParameterAnnotations(m.getLocalClassWithEnclosingInstanceAndLocalCapture());
+ DumpConstructorParameterAnnotations(Inner.class);
+ DumpConstructorParameterAnnotations(StaticInner.class);
+ DumpConstructorParameterAnnotations(ImportantNumber.class);
+ DumpConstructorParameterAnnotations(BinaryNumber.class);
+ DumpConstructorParameterAnnotations(new AnonymousBase("") {}.getClass());
+ }
+}