ART: Add Field methods
Add GetFieldName, GetFieldDeclaringClass, GetFieldModifiers
and IsFieldSynthetic support. Add tests.
Bug: 34113943
Test: m test-art-host-run-test-918-fields
Change-Id: Id33b8c54c250f73f0658ec45ccb2b55eccb37623
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 f46c25c..ddfa96a 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"
@@ -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_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/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) {