Add support for Executable.getParameters() metadata
java.lang.reflect.Executable native code to create Parameter
objects and supporting code for obtaining the system annotations
that hold the parameter metadata.
Bug: 30391692
Test: test-art-host and CtsLibcoreTestCases
Change-Id: I23d7e36014716967ce189fba5955cc5e064fe8d0
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index c6c87fd..e0d5337 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -1027,6 +1027,66 @@
annotation_class);
}
+bool GetParametersMetadataForMethod(ArtMethod* method,
+ MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+ MutableHandle<mirror::IntArray>* access_flags) {
+ const DexFile::AnnotationSetItem::AnnotationSetItem* annotation_set =
+ FindAnnotationSetForMethod(method);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+
+ const DexFile* dex_file = method->GetDexFile();
+ const DexFile::AnnotationItem* annotation_item =
+ SearchAnnotationSet(*dex_file,
+ annotation_set,
+ "Ldalvik/annotation/MethodParameters;",
+ DexFile::kDexVisibilitySystem);
+ if (annotation_item == nullptr) {
+ return false;
+ }
+
+ StackHandleScope<5> hs(Thread::Current());
+
+ // Extract the parameters' names String[].
+ mirror::Class* string_class = mirror::String::GetJavaLangString();
+ Handle<mirror::Class> string_array_class(hs.NewHandle(
+ Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class)));
+ if (UNLIKELY(string_array_class.Get() == nullptr)) {
+ return false;
+ }
+
+ Handle<mirror::Class> klass = hs.NewHandle(method->GetDeclaringClass());
+ Handle<mirror::Object> names_obj =
+ hs.NewHandle(GetAnnotationValue(klass,
+ annotation_item,
+ "names",
+ string_array_class,
+ DexFile::kDexAnnotationArray));
+ if (names_obj.Get() == nullptr) {
+ return false;
+ }
+
+ // Extract the parameters' access flags int[].
+ Handle<mirror::Class> int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass()));
+ if (UNLIKELY(int_array_class.Get() == nullptr)) {
+ return false;
+ }
+ Handle<mirror::Object> access_flags_obj =
+ hs.NewHandle(GetAnnotationValue(klass,
+ annotation_item,
+ "accessFlags",
+ int_array_class,
+ DexFile::kDexAnnotationArray));
+ if (access_flags_obj.Get() == nullptr) {
+ return false;
+ }
+
+ names->Assign(names_obj.Get()->AsObjectArray<mirror::String>());
+ access_flags->Assign(access_flags_obj.Get()->AsIntArray());
+ return true;
+}
+
mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method) {
const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
if (annotation_set == nullptr) {
diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h
index 7b4e856..c66c5bd 100644
--- a/runtime/dex_file_annotations.h
+++ b/runtime/dex_file_annotations.h
@@ -30,6 +30,7 @@
class ArtField;
class ArtMethod;
class ClassLinker;
+template<class T> class MutableHandle;
namespace annotations {
@@ -58,6 +59,10 @@
uint32_t parameter_idx,
Handle<mirror::Class> annotation_class)
REQUIRES_SHARED(Locks::mutator_lock_);
+bool GetParametersMetadataForMethod(ArtMethod* method,
+ MutableHandle<mirror::ObjectArray<mirror::String>>* names,
+ MutableHandle<mirror::IntArray>* access_flags)
+ REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class,
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 8fcf6ac..f345c09 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -18,8 +18,10 @@
#include "art_method-inl.h"
#include "dex_file_annotations.h"
+#include "handle.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
+#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "reflection.h"
@@ -43,8 +45,8 @@
}
static jobject Executable_getAnnotationNative(JNIEnv* env,
- jobject javaMethod,
- jclass annotationType) {
+ jobject javaMethod,
+ jclass annotationType) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
@@ -77,9 +79,107 @@
}
}
+static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
+ ScopedFastNativeObjectAccess soa(env);
+ Thread* self = soa.Self();
+ StackHandleScope<8> hs(self);
+
+ Handle<mirror::Method> executable = hs.NewHandle(soa.Decode<mirror::Method*>(javaMethod));
+ ArtMethod* art_method = executable.Get()->GetArtMethod();
+ if (art_method->GetDeclaringClass()->IsProxyClass()) {
+ return nullptr;
+ }
+
+ // Find the MethodParameters system annotation.
+ MutableHandle<mirror::ObjectArray<mirror::String>> names =
+ hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr);
+ MutableHandle<mirror::IntArray> access_flags = hs.NewHandle<mirror::IntArray>(nullptr);
+ if (!annotations::GetParametersMetadataForMethod(art_method, &names, &access_flags)) {
+ return nullptr;
+ }
+
+ // Validate the MethodParameters system annotation data.
+ if (UNLIKELY(names.Get() == nullptr || access_flags.Get() == nullptr)) {
+ ThrowIllegalArgumentException(
+ StringPrintf("Missing parameter metadata for names or access flags for %s",
+ PrettyMethod(art_method).c_str()).c_str());
+ return nullptr;
+ }
+
+ // Check array sizes match each other
+ int32_t names_count = names.Get()->GetLength();
+ int32_t access_flags_count = access_flags.Get()->GetLength();
+ if (names_count != access_flags_count) {
+ ThrowIllegalArgumentException(
+ StringPrintf(
+ "Inconsistent parameter metadata for %s. names length: %d, access flags length: %d",
+ PrettyMethod(art_method).c_str(),
+ names_count,
+ access_flags_count).c_str());
+ return nullptr;
+ }
+
+ // Instantiate a Parameter[] to hold the result.
+ Handle<mirror::Class> parameter_array_class =
+ hs.NewHandle(
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Parameter__array));
+ Handle<mirror::ObjectArray<mirror::Object>> parameter_array =
+ hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(self,
+ parameter_array_class.Get(),
+ names_count));
+ if (UNLIKELY(parameter_array.Get() == nullptr)) {
+ self->AssertPendingException();
+ return nullptr;
+ }
+
+ Handle<mirror::Class> parameter_class =
+ hs.NewHandle(soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_reflect_Parameter));
+ ArtMethod* parameter_init =
+ soa.DecodeMethod(WellKnownClasses::java_lang_reflect_Parameter_init);
+
+ // Mutable handles used in the loop below to ensure cleanup without scaling the number of
+ // handles by the number of parameters.
+ MutableHandle<mirror::String> name = hs.NewHandle<mirror::String>(nullptr);
+ MutableHandle<mirror::Object> parameter = hs.NewHandle<mirror::Object>(nullptr);
+
+ // Populate the Parameter[] to return.
+ for (int32_t parameter_index = 0; parameter_index < names_count; parameter_index++) {
+ name.Assign(names.Get()->Get(parameter_index));
+ int32_t modifiers = access_flags.Get()->Get(parameter_index);
+
+ // Allocate / initialize the Parameter to add to parameter_array.
+ parameter.Assign(parameter_class->AllocObject(self));
+ if (UNLIKELY(parameter.Get() == nullptr)) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ uint32_t args[5] = { PointerToLowMemUInt32(parameter.Get()),
+ PointerToLowMemUInt32(name.Get()),
+ static_cast<uint32_t>(modifiers),
+ PointerToLowMemUInt32(executable.Get()),
+ static_cast<uint32_t>(parameter_index)
+ };
+ JValue result;
+ static const char* method_signature = "VLILI"; // return + parameter types
+ parameter_init->Invoke(self, args, sizeof(args), &result, method_signature);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ return nullptr;
+ }
+
+ // Store the Parameter in the Parameter[].
+ parameter_array.Get()->Set(parameter_index, parameter.Get());
+ if (UNLIKELY(self->IsExceptionPending())) {
+ return nullptr;
+ }
+ }
+ return soa.AddLocalReference<jobjectArray>(parameter_array.Get());
+}
+
static jboolean Executable_isAnnotationPresentNative(JNIEnv* env,
- jobject javaMethod,
- jclass annotationType) {
+ jobject javaMethod,
+ jclass annotationType) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
if (method->GetDeclaringClass()->IsProxyClass()) {
@@ -96,6 +196,7 @@
NATIVE_METHOD(Executable, getDeclaredAnnotationsNative, "!()[Ljava/lang/annotation/Annotation;"),
NATIVE_METHOD(Executable, getParameterAnnotationsNative,
"!()[[Ljava/lang/annotation/Annotation;"),
+ NATIVE_METHOD(Executable, getParameters0, "!()[Ljava/lang/reflect/Parameter;"),
NATIVE_METHOD(Executable, getSignatureAnnotation, "!()[Ljava/lang/String;"),
NATIVE_METHOD(Executable, isAnnotationPresentNative, "!(Ljava/lang/Class;)Z"),
};
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 6b569f4..e5216fb 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -53,6 +53,8 @@
jclass WellKnownClasses::java_lang_reflect_Executable;
jclass WellKnownClasses::java_lang_reflect_Field;
jclass WellKnownClasses::java_lang_reflect_Method;
+jclass WellKnownClasses::java_lang_reflect_Parameter;
+jclass WellKnownClasses::java_lang_reflect_Parameter__array;
jclass WellKnownClasses::java_lang_reflect_Proxy;
jclass WellKnownClasses::java_lang_RuntimeException;
jclass WellKnownClasses::java_lang_StackOverflowError;
@@ -87,6 +89,7 @@
jmethodID WellKnownClasses::java_lang_Long_valueOf;
jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
+jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
jmethodID WellKnownClasses::java_lang_Short_valueOf;
@@ -280,6 +283,8 @@
java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable");
java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field");
java_lang_reflect_Method = CacheClass(env, "java/lang/reflect/Method");
+ java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter");
+ java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;");
java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
@@ -312,6 +317,7 @@
ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue"));
java_lang_ref_ReferenceQueue_add = CacheMethod(env, java_lang_ref_ReferenceQueue.get(), true, "add", "(Ljava/lang/ref/Reference;)V");
+ java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
java_lang_reflect_Proxy_invoke = CacheMethod(env, java_lang_reflect_Proxy, true, "invoke", "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c9110e6..ddfc5b8 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -67,6 +67,8 @@
static jclass java_lang_reflect_Executable;
static jclass java_lang_reflect_Field;
static jclass java_lang_reflect_Method;
+ static jclass java_lang_reflect_Parameter;
+ static jclass java_lang_reflect_Parameter__array;
static jclass java_lang_reflect_Proxy;
static jclass java_lang_RuntimeException;
static jclass java_lang_StackOverflowError;
@@ -101,6 +103,7 @@
static jmethodID java_lang_Long_valueOf;
static jmethodID java_lang_ref_FinalizerReference_add;
static jmethodID java_lang_ref_ReferenceQueue_add;
+ static jmethodID java_lang_reflect_Parameter_init;
static jmethodID java_lang_reflect_Proxy_invoke;
static jmethodID java_lang_Runtime_nativeLoad;
static jmethodID java_lang_Short_valueOf;