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/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 145eb67..9276994 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -295,6 +295,11 @@
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 013856f..5d9b729 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -602,6 +602,8 @@
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 3431bb7..6f3354b 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1121,6 +1121,21 @@
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 @@
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 d7ebf84..4bb0d75 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -55,6 +55,8 @@
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 a1d0ff7..6000317 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -186,6 +186,11 @@
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 a5e70af..b129c66 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -70,7 +70,6 @@
if (method->GetDeclaringClass()->IsProxyClass()) {
return nullptr;
}
- StackHandleScope<1> hs(soa.Self());
return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForMethod(method));
}
@@ -80,9 +79,76 @@
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
if (method->IsProxyMethod()) {
return nullptr;
- } else {
- return soa.AddLocalReference<jobjectArray>(annotations::GetParameterAnnotations(method));
}
+
+ 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 {
+ // 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 0b3015b..1ab9109 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -58,6 +58,40 @@
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 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) {