Merge changes Id7509ef1,Id33b8c54,I2f9e5c62
* changes:
ART: Add GetClassFields support
ART: Add Field methods
ART: Add IsInterface and IsArrayClass support
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 0f9fbb2..6105fec 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -21,6 +21,7 @@
"object_tagging.cc",
"OpenjdkJvmTi.cc",
"ti_class.cc",
+ "ti_field.cc",
"ti_heap.cc",
"ti_method.cc",
"ti_stack.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 5f97b60..b8f1500 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -47,6 +47,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_class.h"
+#include "ti_field.h"
#include "ti_heap.h"
#include "ti_method.h"
#include "ti_redefine.h"
@@ -559,7 +560,7 @@
jclass klass,
jint* field_count_ptr,
jfieldID** fields_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::GetClassFields(env, klass, field_count_ptr, fields_ptr);
}
static jvmtiError GetImplementedInterfaces(jvmtiEnv* env,
@@ -585,13 +586,13 @@
}
static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::IsInterface(env, klass, is_interface_ptr);
}
static jvmtiError IsArrayClass(jvmtiEnv* env,
jclass klass,
jboolean* is_array_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ClassUtil::IsArrayClass(env, klass, is_array_class_ptr);
}
static jvmtiError IsModifiableClass(jvmtiEnv* env,
@@ -640,28 +641,28 @@
char** name_ptr,
char** signature_ptr,
char** generic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldName(env, klass, field, name_ptr, signature_ptr, generic_ptr);
}
static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
jclass klass,
jfieldID field,
jclass* declaring_class_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldDeclaringClass(env, klass, field, declaring_class_ptr);
}
static jvmtiError GetFieldModifiers(jvmtiEnv* env,
jclass klass,
jfieldID field,
jint* modifiers_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::GetFieldModifiers(env, klass, field, modifiers_ptr);
}
static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
jclass klass,
jfieldID field,
jboolean* is_synthetic_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr);
}
static jvmtiError GetMethodName(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index de2076a..eea317a 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -32,11 +32,54 @@
#include "ti_class.h"
#include "art_jvmti.h"
+#include "jni_internal.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
namespace openjdkjvmti {
+jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
+ jclass jklass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (field_count_ptr == nullptr || fields_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::StackHandleScope<1> hs(soa.Self());
+ art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields();
+ art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields();
+ size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
+
+ unsigned char* out_ptr;
+ jvmtiError allocError = env->Allocate(array_size * sizeof(jfieldID), &out_ptr);
+ if (allocError != ERR(NONE)) {
+ return allocError;
+ }
+ jfieldID* field_array = reinterpret_cast<jfieldID*>(out_ptr);
+
+ size_t array_idx = 0;
+ for (art::ArtField& field : sfields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+ for (art::ArtField& field : ifields) {
+ field_array[array_idx] = art::jni::EncodeArtField(&field);
+ ++array_idx;
+ }
+
+ *field_count_ptr = static_cast<jint>(array_size);
+ *fields_ptr = field_array;
+
+ return ERR(NONE);
+}
+
jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
jclass jklass,
char** signature_ptr,
@@ -70,4 +113,38 @@
return ERR(NONE);
}
+template <typename T>
+static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+
+ if (is_t_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ *is_t_ptr = test(klass) ? JNI_TRUE : JNI_FALSE;
+ return ERR(NONE);
+}
+
+jvmtiError ClassUtil::IsInterface(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jboolean* is_interface_ptr) {
+ auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return klass->IsInterface();
+ };
+ return ClassIsT(jklass, test, is_interface_ptr);
+}
+
+jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass jklass,
+ jboolean* is_array_class_ptr) {
+ auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return klass->IsArrayClass();
+ };
+ return ClassIsT(jklass, test, is_array_class_ptr);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h
index caa77d4..34edb16 100644
--- a/runtime/openjdkjvmti/ti_class.h
+++ b/runtime/openjdkjvmti/ti_class.h
@@ -39,10 +39,18 @@
class ClassUtil {
public:
+ static jvmtiError GetClassFields(jvmtiEnv* env,
+ jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr);
+
static jvmtiError GetClassSignature(jvmtiEnv* env,
jclass klass,
char** signature_ptr,
char** generic_ptr);
+
+ static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
+ static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc
new file mode 100644
index 0000000..a762830
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -0,0 +1,171 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_field.h"
+
+#include "art_jvmti.h"
+#include "art_field-inl.h"
+#include "base/enums.h"
+#include "jni_internal.h"
+#include "modifiers.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+// Note: For all these functions, we could do a check that the field actually belongs to the given
+// class. But the spec seems to assume a certain encoding of the field ID, and so doesn't
+// specify any errors.
+
+jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+
+ JvmtiUniquePtr name_copy;
+ if (name_ptr != nullptr) {
+ const char* field_name = art_field->GetName();
+ if (field_name == nullptr) {
+ field_name = "<error>";
+ }
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, field_name, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ name_copy = MakeJvmtiUniquePtr(env, tmp);
+ *name_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ JvmtiUniquePtr signature_copy;
+ if (signature_ptr != nullptr) {
+ const char* sig = art_field->GetTypeDescriptor();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, sig, &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ signature_copy = MakeJvmtiUniquePtr(env, tmp);
+ *signature_ptr = reinterpret_cast<char*>(tmp);
+ }
+
+ // TODO: Support generic signature.
+ if (generic_ptr != nullptr) {
+ *generic_ptr = nullptr;
+ }
+
+ // Everything is fine, release the buffers.
+ name_copy.release();
+ signature_copy.release();
+
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (declaring_class_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ art::ObjPtr<art::mirror::Class> field_klass = art_field->GetDeclaringClass();
+
+ *declaring_class_ptr = soa.AddLocalReference<jclass>(field_klass);
+
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::GetFieldModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (modifiers_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ // Note: Keep this code in sync with Field.getModifiers.
+ uint32_t modifiers = art_field->GetAccessFlags() & 0xFFFF;
+
+ *modifiers_ptr = modifiers;
+ return ERR(NONE);
+}
+
+jvmtiError FieldUtil::IsFieldSynthetic(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr) {
+ if (klass == nullptr) {
+ return ERR(INVALID_CLASS);
+ }
+ if (field == nullptr) {
+ return ERR(INVALID_FIELDID);
+ }
+ if (is_synthetic_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtField* art_field = art::jni::DecodeArtField(field);
+ uint32_t modifiers = art_field->GetAccessFlags();
+
+ *is_synthetic_ptr = ((modifiers & art::kAccSynthetic) != 0) ? JNI_TRUE : JNI_FALSE;
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_field.h b/runtime/openjdkjvmti/ti_field.h
new file mode 100644
index 0000000..9a29f81
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_field.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class FieldUtil {
+ public:
+ static jvmtiError GetFieldName(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ static jvmtiError GetFieldDeclaringClass(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr);
+
+ static jvmtiError GetFieldModifiers(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr);
+
+ static jvmtiError IsFieldSynthetic(jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_FIELD_H_
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index a0a0923..02b6090 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -37,6 +37,7 @@
#include "jni_internal.h"
#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
namespace openjdkjvmti {
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 838a92a..bad48a4 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -61,6 +61,56 @@
return ret;
}
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jboolean is_interface = JNI_FALSE;
+ jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsInterface: %s\n", err);
+ return JNI_FALSE;
+ }
+ return is_interface;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass(
+ JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jboolean is_array_class = JNI_FALSE;
+ jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsArrayClass: %s\n", err);
+ return JNI_FALSE;
+ }
+ return is_array_class;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
+ jint count = 0;
+ jfieldID* fields = nullptr;
+ jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetClassFields: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ jint modifiers;
+ // Ignore any errors for simplicity.
+ jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
+ constexpr jint kStatic = 0x8;
+ return env->ToReflectedField(klass,
+ fields[i],
+ (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
+ };
+ return CreateObjectArray(env, count, "java/lang/Object", callback);
+}
+
// Don't do anything
jint OnLoad(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 71b22f4..bf591c7 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -5,3 +5,13 @@
[L$Proxy0;, null]
[I, null]
[[D, null]
+int interface=false array=false
+$Proxy0 interface=false array=false
+java.lang.Runnable interface=true array=false
+java.lang.String interface=false array=false
+[I interface=false array=true
+[Ljava.lang.Runnable; interface=false array=true
+[Ljava.lang.String; interface=false array=true
+[public static final int java.lang.Integer.BYTES, static final char[] java.lang.Integer.DigitOnes, static final char[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
+[]
+[]
diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java
index 025584e..a7f3b3f 100644
--- a/test/912-classes/src/Main.java
+++ b/test/912-classes/src/Main.java
@@ -34,6 +34,19 @@
testClass(int.class);
testClass(double[].class);
+
+ testClassType(int.class);
+ testClassType(getProxyClass());
+ testClassType(Runnable.class);
+ testClassType(String.class);
+
+ testClassType(int[].class);
+ testClassType(Runnable[].class);
+ testClassType(String[].class);
+
+ testClassFields(Integer.class);
+ testClassFields(int.class);
+ testClassFields(String[].class);
}
private static Class<?> proxyClass = null;
@@ -57,5 +70,20 @@
System.out.println(Arrays.toString(result));
}
+ private static void testClassType(Class<?> c) throws Exception {
+ boolean isInterface = isInterface(c);
+ boolean isArray = isArrayClass(c);
+ System.out.println(c.getName() + " interface=" + isInterface + " array=" + isArray);
+ }
+
+ private static void testClassFields(Class<?> c) throws Exception {
+ System.out.println(Arrays.toString(getClassFields(c)));
+ }
+
private static native String[] getClassSignature(Class<?> c);
+
+ private static native boolean isInterface(Class<?> c);
+ private static native boolean isArrayClass(Class<?> c);
+
+ private static native Object[] getClassFields(Class<?> c);
}
diff --git a/test/918-fields/build b/test/918-fields/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/918-fields/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-build "$@" --experimental agents
diff --git a/test/918-fields/expected.txt b/test/918-fields/expected.txt
new file mode 100644
index 0000000..39d3e70
--- /dev/null
+++ b/test/918-fields/expected.txt
@@ -0,0 +1,16 @@
+[PI, D, null]
+class java.lang.Math
+25
+false
+[value, I, null]
+class java.lang.Integer
+18
+false
+[this$0, LMain;, null]
+class Main$Foo
+4112
+true
+[VAL, I, null]
+interface Main$Bar
+25
+false
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
new file mode 100644
index 0000000..c7fca06
--- /dev/null
+++ b/test/918-fields/fields.cc
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "fields.h"
+
+#include <stdio.h>
+
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test918Fields {
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getFieldName(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ char* name;
+ char* sig;
+ char* gen;
+ // Note: technically putting the caller class here is wrong, but we don't need it, anyways.
+ jvmtiError result = jvmti_env->GetFieldName(klass, id, &name, &sig, &gen);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldName: %s\n", err);
+ return nullptr;
+ }
+
+ auto callback = [&](jint i) {
+ if (i == 0) {
+ return name == nullptr ? nullptr : env->NewStringUTF(name);
+ } else if (i == 1) {
+ return sig == nullptr ? nullptr : env->NewStringUTF(sig);
+ } else {
+ return gen == nullptr ? nullptr : env->NewStringUTF(gen);
+ }
+ };
+ jobjectArray ret = CreateObjectArray(env, 3, "java/lang/String", callback);
+
+ // Need to deallocate the strings.
+ if (name != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
+ }
+ if (sig != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ if (gen != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
+ }
+
+ // Also run GetMethodName with all parameter pointers null to check for segfaults.
+ jvmtiError result2 = jvmti_env->GetFieldName(klass, id, nullptr, nullptr, nullptr);
+ if (result2 != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result2, &err);
+ printf("Failure running GetFieldName(null, null, null): %s\n", err);
+ return nullptr;
+ }
+
+ return ret;
+}
+
+extern "C" JNIEXPORT jclass JNICALL Java_Main_getFieldDeclaringClass(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jclass declaring_class;
+ jvmtiError result = jvmti_env->GetFieldDeclaringClass(klass, id, &declaring_class);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldDeclaringClass: %s\n", err);
+ return nullptr;
+ }
+
+ return declaring_class;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldModifiers(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jint modifiers;
+ jvmtiError result = jvmti_env->GetFieldModifiers(klass, id, &modifiers);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running GetFieldModifiers: %s\n", err);
+ return 0;
+ }
+
+ return modifiers;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isFieldSynthetic(
+ JNIEnv* env, jclass klass, jobject field) {
+ jfieldID id = env->FromReflectedField(field);
+
+ jboolean synth;
+ jvmtiError result = jvmti_env->IsFieldSynthetic(klass, id, &synth);
+ if (result != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(result, &err);
+ printf("Failure running IsFieldSynthetic: %s\n", err);
+ return 0;
+ }
+
+ return synth;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ SetAllCapabilities(jvmti_env);
+ return 0;
+}
+
+} // namespace Test918Fields
+} // namespace art
diff --git a/test/918-fields/fields.h b/test/918-fields/fields.h
new file mode 100644
index 0000000..89bd161
--- /dev/null
+++ b/test/918-fields/fields.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_TEST_918_FIELDS_FIELDS_H_
+#define ART_TEST_918_FIELDS_FIELDS_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test918Fields {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test918Fields
+} // namespace art
+
+#endif // ART_TEST_918_FIELDS_FIELDS_H_
diff --git a/test/918-fields/info.txt b/test/918-fields/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/918-fields/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/918-fields/run b/test/918-fields/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/918-fields/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
new file mode 100644
index 0000000..8af6e7b
--- /dev/null
+++ b/test/918-fields/src/Main.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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.reflect.Field;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ testField(Math.class, "PI");
+ testField(Integer.class, "value");
+ testField(Foo.class, "this$0");
+ testField(Bar.class, "VAL");
+ }
+
+ private static void testField(Class<?> base, String fieldName)
+ throws Exception {
+ Field f = base.getDeclaredField(fieldName);
+ String[] result = getFieldName(f);
+ System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getFieldDeclaringClass(f);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getFieldModifiers(f);
+ if (modifiers != f.getModifiers()) {
+ throw new RuntimeException("Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
+
+ boolean synth = isFieldSynthetic(f);
+ if (synth != f.isSynthetic()) {
+ throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ }
+ System.out.println(synth);
+ }
+
+ private static native String[] getFieldName(Field f);
+ private static native Class<?> getFieldDeclaringClass(Field f);
+ private static native int getFieldModifiers(Field f);
+ private static native boolean isFieldSynthetic(Field f);
+
+ private class Foo {
+ }
+
+ private static interface Bar {
+ public static int VAL = 1;
+ }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 5a2c902..26c4f93 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -260,6 +260,7 @@
"911-get-stack-trace/stack_trace.cc",
"912-classes/classes.cc",
"913-heaps/heaps.cc",
+ "918-fields/fields.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ec1f6ba..90dd36e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -290,6 +290,7 @@
915-obsolete-2 \
916-obsolete-jit \
917-fields-transformation \
+ 918-fields \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 79c17d7..821e254 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -37,6 +37,7 @@
#include "911-get-stack-trace/stack_trace.h"
#include "912-classes/classes.h"
#include "913-heaps/heaps.h"
+#include "918-fields/fields.h"
namespace art {
@@ -70,6 +71,7 @@
{ "915-obsolete-2", common_redefine::OnLoad, nullptr },
{ "916-obsolete-jit", common_redefine::OnLoad, nullptr },
{ "917-fields-transformation", common_redefine::OnLoad, nullptr },
+ { "918-fields", Test918Fields::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {