summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <treehugger-gerrit@google.com> 2018-03-05 19:02:39 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2018-03-05 19:02:39 +0000
commitf46f46cf5bd32788d5252b7107628a66594a5e98 (patch)
treee63323329298a7cc67799324a6edc2378cfb6822
parent4927ba0f5011b7394e52dca2c3cec52f265f8529 (diff)
parent58143d2c47734c46c1fa4855cb603c24f2d15454 (diff)
Merge "ART: Fixes for constructor parameter annotations"
-rw-r--r--runtime/art_method-inl.h5
-rw-r--r--runtime/art_method.h2
-rw-r--r--runtime/dex/dex_file_annotations.cc19
-rw-r--r--runtime/dex/dex_file_annotations.h2
-rw-r--r--runtime/mirror/class.h5
-rw-r--r--runtime/native/java_lang_reflect_Executable.cc70
-rw-r--r--runtime/native/java_lang_reflect_Parameter.cc41
-rw-r--r--test/715-clinit-implicit-parameter-annotations/build24
-rw-r--r--test/715-clinit-implicit-parameter-annotations/expected.txt113
-rw-r--r--test/715-clinit-implicit-parameter-annotations/info.txt5
-rw-r--r--test/715-clinit-implicit-parameter-annotations/src/Main.java215
-rwxr-xr-xtest/etc/default-build2
12 files changed, 497 insertions, 6 deletions
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 145eb67aa9..92769942c0 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -295,6 +295,11 @@ inline const DexFile::ClassDef& ArtMethod::GetClassDef() {
return GetDexFile()->GetClassDef(GetClassDefIndex());
}
+inline size_t ArtMethod::GetNumberOfParameters() {
+ constexpr size_t return_type_count = 1u;
+ return strlen(GetShorty()) - return_type_count;
+}
+
inline const char* ArtMethod::GetReturnTypeDescriptor() {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 013856f3fe..5d9b729847 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -602,6 +602,8 @@ class ArtMethod FINAL {
const DexFile::ClassDef& GetClassDef() REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE size_t GetNumberOfParameters() REQUIRES_SHARED(Locks::mutator_lock_);
+
const char* GetReturnTypeDescriptor() REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE Primitive::Type GetReturnTypePrimitive() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 3431bb7efb..6f3354b724 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1121,6 +1121,21 @@ mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method)
return ProcessAnnotationSetRefList(ClassData(method), set_ref_list, size);
}
+uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method) {
+ const DexFile* dex_file = method->GetDexFile();
+ const DexFile::ParameterAnnotationsItem* parameter_annotations =
+ FindAnnotationsItemForMethod(method);
+ if (parameter_annotations == nullptr) {
+ return 0u;
+ }
+ const DexFile::AnnotationSetRefList* set_ref_list =
+ dex_file->GetParameterAnnotationSetRefList(parameter_annotations);
+ if (set_ref_list == nullptr) {
+ return 0u;
+ }
+ return set_ref_list->size_;
+}
+
mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
uint32_t parameter_idx,
Handle<mirror::Class> annotation_class) {
@@ -1141,7 +1156,9 @@ mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
const DexFile::AnnotationSetRefItem* annotation_set_ref = &set_ref_list->list_[parameter_idx];
const DexFile::AnnotationSetItem* annotation_set =
dex_file->GetSetRefItemItem(annotation_set_ref);
-
+ if (annotation_set == nullptr) {
+ return nullptr;
+ }
return GetAnnotationObjectFromAnnotationSet(ClassData(method),
annotation_set,
DexFile::kDexVisibilityRuntime,
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index d7ebf84b1c..4bb0d75a57 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -55,6 +55,8 @@ mirror::ObjectArray<mirror::Class>* GetExceptionTypesForMethod(ArtMethod* method
REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::Object>* GetParameterAnnotations(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
+uint32_t GetNumberOfAnnotatedMethodParameters(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
mirror::Object* GetAnnotationForMethodParameter(ArtMethod* method,
uint32_t parameter_idx,
Handle<mirror::Class> annotation_class)
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index a1d0ff7374..6000317c85 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -186,6 +186,11 @@ class MANAGED Class FINAL : public Object {
void SetAccessFlags(uint32_t new_access_flags) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if the class is an enum.
+ ALWAYS_INLINE bool IsEnum() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccEnum) != 0;
+ }
+
// Returns true if the class is an interface.
ALWAYS_INLINE bool IsInterface() REQUIRES_SHARED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccInterface) != 0;
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index a5e70affa5..b129c66759 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -70,7 +70,6 @@ static jobjectArray Executable_getSignatureAnnotation(JNIEnv* env, jobject javaM
if (method->GetDeclaringClass()->IsProxyClass()) {
return nullptr;
}
- StackHandleScope<1> hs(soa.Self());
return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForMethod(method));
}
@@ -80,9 +79,76 @@ static jobjectArray Executable_getParameterAnnotationsNative(JNIEnv* env, jobjec
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
if (method->IsProxyMethod()) {
return nullptr;
+ }
+
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::ObjectArray<mirror::Object>> annotations =
+ hs.NewHandle(annotations::GetParameterAnnotations(method));
+ if (annotations.IsNull()) {
+ return nullptr;
+ }
+
+ // If the method is not a constructor, or has parameter annotations
+ // for each parameter, then we can return those annotations
+ // unmodified. Otherwise, we need to look at whether the
+ // constructor has implicit parameters as these may need padding
+ // with empty parameter annotations.
+ if (!method->IsConstructor() ||
+ annotations->GetLength() == static_cast<int>(method->GetNumberOfParameters())) {
+ return soa.AddLocalReference<jobjectArray>(annotations.Get());
+ }
+
+ // If declaring class is a local or an enum, do not pad parameter
+ // annotations, as the implicit constructor parameters are an implementation
+ // detail rather than required by JLS.
+ Handle<mirror::Class> declaring_class = hs.NewHandle(method->GetDeclaringClass());
+ if (annotations::GetEnclosingMethod(declaring_class) != nullptr ||
+ declaring_class->IsEnum()) {
+ return soa.AddLocalReference<jobjectArray>(annotations.Get());
+ }
+
+ // Prepare to resize the annotations so there is 1:1 correspondence
+ // with the constructor parameters.
+ Handle<mirror::ObjectArray<mirror::Object>> resized_annotations = hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(),
+ annotations->GetClass(),
+ static_cast<int>(method->GetNumberOfParameters())));
+ if (resized_annotations.IsNull()) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return nullptr;
+ }
+
+ static constexpr bool kTransactionActive = false;
+ const int32_t offset = resized_annotations->GetLength() - annotations->GetLength();
+ if (offset > 0) {
+ // Workaround for dexers (d8/dx) that do not insert annotations
+ // for implicit parameters (b/68033708).
+ ObjPtr<mirror::Class> annotation_array_class =
+ soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ Handle<mirror::ObjectArray<mirror::Object>> empty_annotations = hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0));
+ if (empty_annotations.IsNull()) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return nullptr;
+ }
+ for (int i = 0; i < offset; ++i) {
+ resized_annotations->SetWithoutChecks<kTransactionActive>(i, empty_annotations.Get());
+ }
+ for (int i = 0; i < annotations->GetLength(); ++i) {
+ ObjPtr<mirror::Object> annotation = annotations->GetWithoutChecks(i);
+ resized_annotations->SetWithoutChecks<kTransactionActive>(i + offset, annotation);
+ }
} else {
- return soa.AddLocalReference<jobjectArray>(annotations::GetParameterAnnotations(method));
+ // Workaround for Jack (defunct) erroneously inserting annotations
+ // for local classes (b/68033708).
+ DCHECK_LT(offset, 0);
+ for (int i = 0; i < resized_annotations->GetLength(); ++i) {
+ ObjPtr<mirror::Object> annotation = annotations->GetWithoutChecks(i - offset);
+ resized_annotations->SetWithoutChecks<kTransactionActive>(i, annotation);
+ }
}
+ return soa.AddLocalReference<jobjectArray>(resized_annotations.Get());
}
static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index 0b3015bda8..1ab91098d7 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -58,6 +58,40 @@ static jobject Parameter_getAnnotationNative(JNIEnv* env,
return nullptr;
}
+ uint32_t annotated_parameter_count = annotations::GetNumberOfAnnotatedMethodParameters(method);
+ if (annotated_parameter_count == 0u) {
+ return nullptr;
+ }
+
+ // For constructors with implicit arguments, we may need to adjust
+ // annotation positions based on whether the implicit parameters are
+ // expected to known and not just a compiler implementation detail.
+ if (method->IsConstructor()) {
+ StackHandleScope<1> hs(soa.Self());
+ // If declaring class is a local or an enum, do not pad parameter
+ // annotations, as the implicit constructor parameters are an
+ // implementation detail rather than required by JLS.
+ Handle<mirror::Class> declaring_class = hs.NewHandle(method->GetDeclaringClass());
+ if (annotations::GetEnclosingMethod(declaring_class) == nullptr && !declaring_class->IsEnum()) {
+ // Adjust the parameter index if the number of annotations does
+ // not match the number of parameters.
+ if (annotated_parameter_count <= parameter_count) {
+ // Workaround for dexer not inserting annotation state for implicit parameters (b/68033708).
+ uint32_t skip_count = parameter_count - annotated_parameter_count;
+ DCHECK_GE(2u, skip_count);
+ if (parameterIndex < static_cast<jint>(skip_count)) {
+ return nullptr;
+ }
+ parameterIndex -= skip_count;
+ } else {
+ // Workaround for Jack erroneously inserting implicit parameter for local classes
+ // (b/68033708).
+ DCHECK_EQ(1u, annotated_parameter_count - parameter_count);
+ parameterIndex += static_cast<jint>(annotated_parameter_count - parameter_count);
+ }
+ }
+ }
+
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(soa.Decode<mirror::Class>(annotationType)));
return soa.AddLocalReference<jobject>(
@@ -65,9 +99,10 @@ static jobject Parameter_getAnnotationNative(JNIEnv* env,
}
static JNINativeMethod gMethods[] = {
- FAST_NATIVE_METHOD(Parameter,
- getAnnotationNative,
- "(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ FAST_NATIVE_METHOD(
+ Parameter,
+ getAnnotationNative,
+ "(Ljava/lang/reflect/Executable;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;"),
};
void register_java_lang_reflect_Parameter(JNIEnv* env) {
diff --git a/test/715-clinit-implicit-parameter-annotations/build b/test/715-clinit-implicit-parameter-annotations/build
new file mode 100644
index 0000000000..4753c8c7dc
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/build
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Make us exit on a failure
+set -e
+
+# Always use D8 as DX does not support propagating parameter name and
+# access_flag information.
+export USE_D8=true
+
+./default-build "$@" --experimental parameter-annotations
diff --git a/test/715-clinit-implicit-parameter-annotations/expected.txt b/test/715-clinit-implicit-parameter-annotations/expected.txt
new file mode 100644
index 0000000000..357eb6253e
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/expected.txt
@@ -0,0 +1,113 @@
+Main
+ public Main()
+Main$1LocalClassStaticContext
+ Main$1LocalClassStaticContext(int)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$1LocalClassStaticContextWithCapture
+ Main$1LocalClassStaticContextWithCapture(java.lang.String,long)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$1LocalClassStaticContextWithCaptureAlternateOrdering
+ Main$1LocalClassStaticContextWithCaptureAlternateOrdering(java.lang.String,long)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$1LocalClass
+ Main$1LocalClass(Main,int)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$1LocalClassWithCapture
+ Main$1LocalClassWithCapture(Main,java.lang.String,long)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$Inner
+ Main$Inner(Main,int,java.lang.String)
+ Parameter [0]: Main$AnnotationA No
+ Main$AnnotationB No
+ Parameter [1]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+ Parameter [2]: Main$AnnotationA No
+ Main$AnnotationB No
+ Main$Inner(Main,int,java.lang.String,boolean)
+ Parameter [0]: Main$AnnotationA No
+ Main$AnnotationB No
+ Parameter [1]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+ Parameter [2]: Main$AnnotationA No
+ Main$AnnotationB No
+ Parameter [3]: Indexed : @Main$AnnotationB(value=x)
+ Array : @Main$AnnotationB(value=x)
+ Main$AnnotationA No
+ Main$AnnotationB Yes
+ @Main$AnnotationB(value=x)
+Main$StaticInner
+ Main$StaticInner(int,java.lang.String)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+ Parameter [1]: Main$AnnotationA No
+ Main$AnnotationB No
+ Main$StaticInner(int,java.lang.String,boolean)
+ Parameter [0]: Indexed : @Main$AnnotationB(value=foo)
+ Array : @Main$AnnotationB(value=foo)
+ Main$AnnotationA No
+ Main$AnnotationB Yes
+ @Main$AnnotationB(value=foo)
+ Parameter [1]: Main$AnnotationA No
+ Main$AnnotationB No
+ Parameter [2]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+Main$ImportantNumber
+ private Main$ImportantNumber(java.lang.String,int,double)
+ Parameter [0]: Indexed : @Main$AnnotationA()
+ Array : @Main$AnnotationA()
+ Main$AnnotationA Yes
+ @Main$AnnotationA()
+ Main$AnnotationB No
+ private Main$ImportantNumber(java.lang.String,int,double,boolean)
+ Parameter [0]: Indexed : @Main$AnnotationB(value=x)
+ Array : @Main$AnnotationB(value=x)
+ Main$AnnotationA No
+ Main$AnnotationB Yes
+ @Main$AnnotationB(value=x)
+ Parameter [1]: Indexed : @Main$AnnotationB(value=y)
+ Array : @Main$AnnotationB(value=y)
+ Main$AnnotationA No
+ Main$AnnotationB Yes
+ @Main$AnnotationB(value=y)
+Main$BinaryNumber
+ private Main$BinaryNumber(java.lang.String,int)
+ Parameter [0]: Main$AnnotationA No
+ Main$AnnotationB No
+ Parameter [1]: Main$AnnotationA No
+ Main$AnnotationB No
+Main$1
+ Main$1(java.lang.String)
+ Parameter [0]: Main$AnnotationA No
+ Main$AnnotationB No
diff --git a/test/715-clinit-implicit-parameter-annotations/info.txt b/test/715-clinit-implicit-parameter-annotations/info.txt
new file mode 100644
index 0000000000..31afd62f27
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/info.txt
@@ -0,0 +1,5 @@
+Tests ART synthesizes parameter annotations for implicit parameters on
+constructors. Inner class and enum constructors may have implicit
+parameters. If the constructor has parameter annotations, the implicit
+parameters may not have annotations in the DEX file, but code that
+looks at these annotations will expect them to.
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 0000000000..351e3a94b3
--- /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());
+ }
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 4ed2af6ac6..3e6577cfda 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -137,12 +137,14 @@ declare -A JAVAC_EXPERIMENTAL_ARGS
JAVAC_EXPERIMENTAL_ARGS["default-methods"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["lambdas"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["method-handles"]="-source 1.8 -target 1.8"
+JAVAC_EXPERIMENTAL_ARGS["parameter-annotations"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["var-handles"]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS[${DEFAULT_EXPERIMENT}]="-source 1.8 -target 1.8"
JAVAC_EXPERIMENTAL_ARGS["agents"]="-source 1.8 -target 1.8"
declare -A DX_EXPERIMENTAL_ARGS
DX_EXPERIMENTAL_ARGS["method-handles"]="--min-sdk-version=26"
+DX_EXPERIMENTAL_ARGS["parameter-annotations"]="--min-sdk-version=25"
DX_EXPERIMENTAL_ARGS["var-handles"]="--min-sdk-version=26"
while true; do