• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdio.h>
18 
19 #include <mutex>
20 #include <vector>
21 
22 #include "android-base/macros.h"
23 #include "android-base/stringprintf.h"
24 
25 #include "jni.h"
26 #include "jvmti.h"
27 
28 // Test infrastructure
29 #include "jni_helper.h"
30 #include "jvmti_helper.h"
31 #include "scoped_local_ref.h"
32 #include "scoped_utf_chars.h"
33 #include "test_env.h"
34 
35 namespace art {
36 namespace Test912Classes {
37 
Java_art_Test912_isModifiableClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)38 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isModifiableClass(
39     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
40   jboolean res = JNI_FALSE;
41   jvmtiError result = jvmti_env->IsModifiableClass(klass, &res);
42   JvmtiErrorToException(env, jvmti_env, result);
43   return res;
44 }
45 
Java_art_Test912_getClassSignature(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)46 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassSignature(
47     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
48   char* sig;
49   char* gen;
50   jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen);
51   if (JvmtiErrorToException(env, jvmti_env, result)) {
52     return nullptr;
53   }
54 
55   auto callback = [&](jint i) {
56     if (i == 0) {
57       return sig == nullptr ? nullptr : env->NewStringUTF(sig);
58     } else {
59       return gen == nullptr ? nullptr : env->NewStringUTF(gen);
60     }
61   };
62   jobjectArray ret = CreateObjectArray(env, 2, "java/lang/String", callback);
63 
64   // Need to deallocate the strings.
65   if (sig != nullptr) {
66     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
67   }
68   if (gen != nullptr) {
69     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
70   }
71 
72   return ret;
73 }
74 
Java_art_Test912_isInterface(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)75 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isInterface(
76     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
77   jboolean is_interface = JNI_FALSE;
78   jvmtiError result = jvmti_env->IsInterface(klass, &is_interface);
79   JvmtiErrorToException(env, jvmti_env, result);
80   return is_interface;
81 }
82 
Java_art_Test912_isArrayClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)83 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isArrayClass(
84     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
85   jboolean is_array_class = JNI_FALSE;
86   jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class);
87   JvmtiErrorToException(env, jvmti_env, result);
88   return is_array_class;
89 }
90 
Java_art_Test912_getClassModifiers(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)91 extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassModifiers(
92     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
93   jint mod;
94   jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod);
95   JvmtiErrorToException(env, jvmti_env, result);
96   return mod;
97 }
98 
Java_art_Test912_getClassFields(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)99 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassFields(
100     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
101   jint count = 0;
102   jfieldID* fields = nullptr;
103   jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields);
104   if (JvmtiErrorToException(env, jvmti_env, result)) {
105     return nullptr;
106   }
107 
108   auto callback = [&](jint i) {
109     jint modifiers;
110     // Ignore any errors for simplicity.
111     jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers);
112     constexpr jint kStatic = 0x8;
113     return env->ToReflectedField(klass,
114                                  fields[i],
115                                  (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
116   };
117   jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
118   if (fields != nullptr) {
119     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
120   }
121   return ret;
122 }
123 
Java_art_Test912_getClassMethods(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)124 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassMethods(
125     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
126   jint count = 0;
127   jmethodID* methods = nullptr;
128   jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods);
129   if (JvmtiErrorToException(env, jvmti_env, result)) {
130     return nullptr;
131   }
132 
133   auto callback = [&](jint i) {
134     jint modifiers;
135     // Ignore any errors for simplicity.
136     jvmti_env->GetMethodModifiers(methods[i], &modifiers);
137     constexpr jint kStatic = 0x8;
138     return env->ToReflectedMethod(klass,
139                                   methods[i],
140                                   (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE);
141   };
142   jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback);
143   if (methods != nullptr) {
144     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods));
145   }
146   return ret;
147 }
148 
Java_art_Test912_getImplementedInterfaces(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)149 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getImplementedInterfaces(
150     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
151   jint count = 0;
152   jclass* classes = nullptr;
153   jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes);
154   if (JvmtiErrorToException(env, jvmti_env, result)) {
155     return nullptr;
156   }
157 
158   auto callback = [&](jint i) {
159     return classes[i];
160   };
161   jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
162   if (classes != nullptr) {
163     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
164   }
165   return ret;
166 }
167 
Java_art_Test912_getClassStatus(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)168 extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassStatus(
169     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
170   jint status;
171   jvmtiError result = jvmti_env->GetClassStatus(klass, &status);
172   JvmtiErrorToException(env, jvmti_env, result);
173   return status;
174 }
175 
Java_art_Test912_getClassLoader(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)176 extern "C" JNIEXPORT jobject JNICALL Java_art_Test912_getClassLoader(
177     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
178   jobject classloader;
179   jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader);
180   JvmtiErrorToException(env, jvmti_env, result);
181   return classloader;
182 }
183 
Java_art_Test912_getClassLoaderClasses(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jobject jclassloader)184 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoaderClasses(
185     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) {
186   jint count = 0;
187   jclass* classes = nullptr;
188   jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes);
189   if (JvmtiErrorToException(env, jvmti_env, result)) {
190     return nullptr;
191   }
192 
193   auto callback = [&](jint i) {
194     return classes[i];
195   };
196   jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback);
197   if (classes != nullptr) {
198     jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes));
199   }
200   return ret;
201 }
202 
Java_art_Test912_getClassVersion(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)203 extern "C" JNIEXPORT jintArray JNICALL Java_art_Test912_getClassVersion(
204     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
205   jint major, minor;
206   jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
207   if (JvmtiErrorToException(env, jvmti_env, result)) {
208     return nullptr;
209   }
210 
211   jintArray int_array = env->NewIntArray(2);
212   if (int_array == nullptr) {
213     return nullptr;
214   }
215   jint buf[2] = { major, minor };
216   env->SetIntArrayRegion(int_array, 0, 2, buf);
217 
218   return int_array;
219 }
220 
GetClassName(jvmtiEnv * jenv,JNIEnv * jni_env,jclass klass)221 static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) {
222   char* name;
223   jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr);
224   if (result != JVMTI_ERROR_NONE) {
225     if (jni_env != nullptr) {
226       JvmtiErrorToException(jni_env, jenv, result);
227     } else {
228       printf("Failed to get class signature.\n");
229     }
230     return "";
231   }
232 
233   std::string tmp(name);
234   jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
235 
236   return tmp;
237 }
238 
239 static void EnableEvents(JNIEnv* env,
240                          jboolean enable,
241                          decltype(jvmtiEventCallbacks().ClassLoad) class_load,
242                          decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) {
243   if (enable == JNI_FALSE) {
244     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
245                                                          JVMTI_EVENT_CLASS_LOAD,
246                                                          nullptr);
247     if (JvmtiErrorToException(env, jvmti_env, ret)) {
248       return;
249     }
250     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
251                                               JVMTI_EVENT_CLASS_PREPARE,
252                                               nullptr);
253     JvmtiErrorToException(env, jvmti_env, ret);
254     return;
255   }
256 
257   jvmtiEventCallbacks callbacks;
258   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
259   callbacks.ClassLoad = class_load;
260   callbacks.ClassPrepare = class_prepare;
261   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
262   if (JvmtiErrorToException(env, jvmti_env, ret)) {
263     return;
264   }
265 
266   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
267                                             JVMTI_EVENT_CLASS_LOAD,
268                                             nullptr);
269   if (JvmtiErrorToException(env, jvmti_env, ret)) {
270     return;
271   }
272   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
273                                             JVMTI_EVENT_CLASS_PREPARE,
274                                             nullptr);
275   JvmtiErrorToException(env, jvmti_env, ret);
276 }
277 
278 static std::mutex gEventsMutex;
279 static std::vector<std::string> gEvents;
280 
Java_art_Test912_getClassLoadMessages(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)281 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoadMessages(
282     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
283   std::lock_guard<std::mutex> guard(gEventsMutex);
284   jobjectArray ret = CreateObjectArray(env,
285                                        static_cast<jint>(gEvents.size()),
286                                        "java/lang/String",
287                                        [&](jint i) {
288     return env->NewStringUTF(gEvents[i].c_str());
289   });
290   gEvents.clear();
291   return ret;
292 }
293 
294 class ClassLoadPreparePrinter {
295  public:
ClassLoadCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread,jclass klass)296   static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
297                                         JNIEnv* jni_env,
298                                         jthread thread,
299                                         jclass klass) {
300     std::string name = GetClassName(jenv, jni_env, klass);
301     if (name == "") {
302       return;
303     }
304     std::string thread_name = GetThreadName(jenv, jni_env, thread);
305     if (thread_name == "") {
306       return;
307     }
308     if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
309       return;
310     }
311 
312     std::lock_guard<std::mutex> guard(gEventsMutex);
313     gEvents.push_back(android::base::StringPrintf("Load: %s on %s",
314                                                   name.c_str(),
315                                                   thread_name.c_str()));
316   }
317 
ClassPrepareCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread,jclass klass)318   static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
319                                            JNIEnv* jni_env,
320                                            jthread thread,
321                                            jclass klass) {
322     std::string name = GetClassName(jenv, jni_env, klass);
323     if (name == "") {
324       return;
325     }
326     std::string thread_name = GetThreadName(jenv, jni_env, thread);
327     if (thread_name == "") {
328       return;
329     }
330     if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) {
331       return;
332     }
333     std::string cur_thread_name = GetThreadName(jenv, jni_env, nullptr);
334 
335     std::lock_guard<std::mutex> guard(gEventsMutex);
336     gEvents.push_back(android::base::StringPrintf("Prepare: %s on %s (cur=%s)",
337                                                   name.c_str(),
338                                                   thread_name.c_str(),
339                                                   cur_thread_name.c_str()));
340   }
341 
GetThreadName(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread)342   static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) {
343     jvmtiThreadInfo info;
344     jvmtiError result = jenv->GetThreadInfo(thread, &info);
345     if (result != JVMTI_ERROR_NONE) {
346       if (jni_env != nullptr) {
347         JvmtiErrorToException(jni_env, jenv, result);
348       } else {
349         printf("Failed to get thread name.\n");
350       }
351       return "";
352     }
353 
354     std::string tmp(info.name);
355     jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name));
356     jni_env->DeleteLocalRef(info.context_class_loader);
357     jni_env->DeleteLocalRef(info.thread_group);
358 
359     return tmp;
360   }
361 
362   static std::string thread_name_filter_;
363 };
364 std::string ClassLoadPreparePrinter::thread_name_filter_;
365 
Java_art_Test912_enableClassLoadPreparePrintEvents(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jboolean enable,jthread thread)366 extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPreparePrintEvents(
367     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable, jthread thread) {
368   if (thread != nullptr) {
369     ClassLoadPreparePrinter::thread_name_filter_ =
370         ClassLoadPreparePrinter::GetThreadName(jvmti_env, env, thread);
371   } else {
372     ClassLoadPreparePrinter::thread_name_filter_ = "";
373   }
374 
375   EnableEvents(env,
376                enable,
377                ClassLoadPreparePrinter::ClassLoadCallback,
378                ClassLoadPreparePrinter::ClassPrepareCallback);
379 }
380 
381 class ClassLoadPrepareEquality {
382  public:
383   static constexpr const char* kClassName = "Lart/Test912$ClassE;";
384   static constexpr const char* kStorageFieldName = "STATIC";
385   static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;";
386   static constexpr const char* kStorageWeakFieldName = "WEAK";
387   static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;";
388   static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference";
389   static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V";
390   static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;";
391 
ClassLoadCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED,jclass klass)392   static void JNICALL ClassLoadCallback(jvmtiEnv* jenv,
393                                         JNIEnv* jni_env,
394                                         jthread thread ATTRIBUTE_UNUSED,
395                                         jclass klass) {
396     std::string name = GetClassName(jenv, jni_env, klass);
397     if (name == kClassName) {
398       found_ = true;
399       stored_class_ = jni_env->NewGlobalRef(klass);
400       weakly_stored_class_ = jni_env->NewWeakGlobalRef(klass);
401       // The following is bad and relies on implementation details. But otherwise a test would be
402       // a lot more complicated.
403       local_stored_class_ = jni_env->NewLocalRef(klass);
404       // Store the value into a field in the heap.
405       SetOrCompare(jni_env, klass, true);
406     }
407   }
408 
ClassPrepareCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED,jclass klass)409   static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv,
410                                            JNIEnv* jni_env,
411                                            jthread thread ATTRIBUTE_UNUSED,
412                                            jclass klass) {
413     std::string name = GetClassName(jenv, jni_env, klass);
414     if (name == kClassName) {
415       CHECK(stored_class_ != nullptr);
416       CHECK(jni_env->IsSameObject(stored_class_, klass));
417       CHECK(jni_env->IsSameObject(weakly_stored_class_, klass));
418       CHECK(jni_env->IsSameObject(local_stored_class_, klass));
419       // Look up the value in a field in the heap.
420       SetOrCompare(jni_env, klass, false);
421       compared_ = true;
422     }
423   }
424 
SetOrCompare(JNIEnv * jni_env,jobject value,bool set)425   static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) {
426     CHECK(storage_class_ != nullptr);
427 
428     // Simple direct storage.
429     jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig);
430     CHECK(field != nullptr);
431 
432     if (set) {
433       jni_env->SetStaticObjectField(storage_class_, field, value);
434       CHECK(!jni_env->ExceptionCheck());
435     } else {
436       ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field));
437       CHECK(jni_env->IsSameObject(value, stored.get()));
438     }
439 
440     // Storage as a reference.
441     ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName));
442     CHECK(weak_ref_class.get() != nullptr);
443     jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_,
444                                                     kStorageWeakFieldName,
445                                                     kStorageWeakFieldSig);
446     CHECK(weak_field != nullptr);
447     if (set) {
448       // Create a WeakReference.
449       jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig);
450       CHECK(weak_init != nullptr);
451       ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(),
452                                                                    weak_init,
453                                                                    value));
454       CHECK(weak_obj.get() != nullptr);
455       jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get());
456       CHECK(!jni_env->ExceptionCheck());
457     } else {
458       // Check the reference value.
459       jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig);
460       CHECK(get_referent != nullptr);
461       ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_,
462                                                                               weak_field));
463       CHECK(weak_obj.get() != nullptr);
464       ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(),
465                                                                                get_referent));
466       CHECK(weak_referent.get() != nullptr);
467       CHECK(jni_env->IsSameObject(value, weak_referent.get()));
468     }
469   }
470 
CheckFound()471   static void CheckFound() {
472     CHECK(found_);
473     CHECK(compared_);
474   }
475 
Free(JNIEnv * env)476   static void Free(JNIEnv* env) {
477     if (stored_class_ != nullptr) {
478       env->DeleteGlobalRef(stored_class_);
479       DCHECK(weakly_stored_class_ != nullptr);
480       env->DeleteWeakGlobalRef(weakly_stored_class_);
481       // Do not attempt to delete the local ref. It will be out of date by now.
482     }
483   }
484 
485   static jclass storage_class_;
486 
487  private:
488   static jobject stored_class_;
489   static jweak weakly_stored_class_;
490   static jobject local_stored_class_;
491   static bool found_;
492   static bool compared_;
493 };
494 jclass ClassLoadPrepareEquality::storage_class_ = nullptr;
495 jobject ClassLoadPrepareEquality::stored_class_ = nullptr;
496 jweak ClassLoadPrepareEquality::weakly_stored_class_ = nullptr;
497 jobject ClassLoadPrepareEquality::local_stored_class_ = nullptr;
498 bool ClassLoadPrepareEquality::found_ = false;
499 bool ClassLoadPrepareEquality::compared_ = false;
500 
Java_art_Test912_setEqualityEventStorageClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)501 extern "C" JNIEXPORT void JNICALL Java_art_Test912_setEqualityEventStorageClass(
502     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
503   ClassLoadPrepareEquality::storage_class_ =
504       reinterpret_cast<jclass>(env->NewGlobalRef(klass));
505 }
506 
Java_art_Test912_enableClassLoadPrepareEqualityEvents(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jboolean b)507 extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPrepareEqualityEvents(
508     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) {
509   EnableEvents(env,
510                b,
511                ClassLoadPrepareEquality::ClassLoadCallback,
512                ClassLoadPrepareEquality::ClassPrepareCallback);
513   if (b == JNI_FALSE) {
514     ClassLoadPrepareEquality::Free(env);
515     ClassLoadPrepareEquality::CheckFound();
516     env->DeleteGlobalRef(ClassLoadPrepareEquality::storage_class_);
517     ClassLoadPrepareEquality::storage_class_ = nullptr;
518   }
519 }
520 
521 }  // namespace Test912Classes
522 }  // namespace art
523