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 <condition_variable> 20 #include <mutex> 21 #include <vector> 22 23 #include "android-base/macros.h" 24 #include "android-base/stringprintf.h" 25 26 #include "jni.h" 27 #include "jvmti.h" 28 29 // Test infrastructure 30 #include "jni_helper.h" 31 #include "jvmti_helper.h" 32 #include "scoped_local_ref.h" 33 #include "scoped_utf_chars.h" 34 #include "test_env.h" 35 36 namespace art { 37 namespace Test912Classes { 38 39 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isModifiableClass( 40 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 41 jboolean res = JNI_FALSE; 42 jvmtiError result = jvmti_env->IsModifiableClass(klass, &res); 43 JvmtiErrorToException(env, jvmti_env, result); 44 return res; 45 } 46 47 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassSignature( 48 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 49 char* sig; 50 char* gen; 51 jvmtiError result = jvmti_env->GetClassSignature(klass, &sig, &gen); 52 if (JvmtiErrorToException(env, jvmti_env, result)) { 53 return nullptr; 54 } 55 56 auto callback = [&](jint i) { 57 if (i == 0) { 58 return sig == nullptr ? nullptr : env->NewStringUTF(sig); 59 } else { 60 return gen == nullptr ? nullptr : env->NewStringUTF(gen); 61 } 62 }; 63 jobjectArray ret = CreateObjectArray(env, 2, "java/lang/String", callback); 64 65 // Need to deallocate the strings. 66 if (sig != nullptr) { 67 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig)); 68 } 69 if (gen != nullptr) { 70 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen)); 71 } 72 73 return ret; 74 } 75 76 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isInterface( 77 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 78 jboolean is_interface = JNI_FALSE; 79 jvmtiError result = jvmti_env->IsInterface(klass, &is_interface); 80 JvmtiErrorToException(env, jvmti_env, result); 81 return is_interface; 82 } 83 84 extern "C" JNIEXPORT jboolean JNICALL Java_art_Test912_isArrayClass( 85 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 86 jboolean is_array_class = JNI_FALSE; 87 jvmtiError result = jvmti_env->IsArrayClass(klass, &is_array_class); 88 JvmtiErrorToException(env, jvmti_env, result); 89 return is_array_class; 90 } 91 92 extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassModifiers( 93 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 94 jint mod; 95 jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod); 96 JvmtiErrorToException(env, jvmti_env, result); 97 return mod; 98 } 99 100 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassFields( 101 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 102 jint count = 0; 103 jfieldID* fields = nullptr; 104 jvmtiError result = jvmti_env->GetClassFields(klass, &count, &fields); 105 if (JvmtiErrorToException(env, jvmti_env, result)) { 106 return nullptr; 107 } 108 109 auto callback = [&](jint i) { 110 jint modifiers; 111 // Ignore any errors for simplicity. 112 jvmti_env->GetFieldModifiers(klass, fields[i], &modifiers); 113 constexpr jint kStatic = 0x8; 114 return env->ToReflectedField(klass, 115 fields[i], 116 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); 117 }; 118 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback); 119 if (fields != nullptr) { 120 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); 121 } 122 return ret; 123 } 124 125 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassMethods( 126 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 127 jint count = 0; 128 jmethodID* methods = nullptr; 129 jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods); 130 if (JvmtiErrorToException(env, jvmti_env, result)) { 131 return nullptr; 132 } 133 134 auto callback = [&](jint i) { 135 jint modifiers; 136 // Ignore any errors for simplicity. 137 jvmti_env->GetMethodModifiers(methods[i], &modifiers); 138 constexpr jint kStatic = 0x8; 139 return env->ToReflectedMethod(klass, 140 methods[i], 141 (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); 142 }; 143 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback); 144 if (methods != nullptr) { 145 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods)); 146 } 147 return ret; 148 } 149 150 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getImplementedInterfaces( 151 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 152 jint count = 0; 153 jclass* classes = nullptr; 154 jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes); 155 if (JvmtiErrorToException(env, jvmti_env, result)) { 156 return nullptr; 157 } 158 159 auto callback = [&](jint i) { 160 return classes[i]; 161 }; 162 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback); 163 if (classes != nullptr) { 164 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)); 165 } 166 return ret; 167 } 168 169 extern "C" JNIEXPORT jint JNICALL Java_art_Test912_getClassStatus( 170 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 171 jint status; 172 jvmtiError result = jvmti_env->GetClassStatus(klass, &status); 173 JvmtiErrorToException(env, jvmti_env, result); 174 return status; 175 } 176 177 extern "C" JNIEXPORT jobject JNICALL Java_art_Test912_getClassLoader( 178 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 179 jobject classloader; 180 jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader); 181 JvmtiErrorToException(env, jvmti_env, result); 182 return classloader; 183 } 184 185 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoaderClasses( 186 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) { 187 jint count = 0; 188 jclass* classes = nullptr; 189 jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes); 190 if (JvmtiErrorToException(env, jvmti_env, result)) { 191 return nullptr; 192 } 193 194 auto callback = [&](jint i) { 195 return classes[i]; 196 }; 197 jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback); 198 if (classes != nullptr) { 199 jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)); 200 } 201 return ret; 202 } 203 204 extern "C" JNIEXPORT jintArray JNICALL Java_art_Test912_getClassVersion( 205 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 206 jint major, minor; 207 jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major); 208 if (JvmtiErrorToException(env, jvmti_env, result)) { 209 return nullptr; 210 } 211 212 jintArray int_array = env->NewIntArray(2); 213 if (int_array == nullptr) { 214 return nullptr; 215 } 216 jint buf[2] = { major, minor }; 217 env->SetIntArrayRegion(int_array, 0, 2, buf); 218 219 return int_array; 220 } 221 222 static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) { 223 char* name; 224 jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr); 225 if (result != JVMTI_ERROR_NONE) { 226 if (jni_env != nullptr) { 227 JvmtiErrorToException(jni_env, jenv, result); 228 } else { 229 printf("Failed to get class signature.\n"); 230 } 231 return ""; 232 } 233 234 std::string tmp(name); 235 jenv->Deallocate(reinterpret_cast<unsigned char*>(name)); 236 237 return tmp; 238 } 239 240 static void EnableEvents(JNIEnv* env, 241 jboolean enable, 242 decltype(jvmtiEventCallbacks().ClassLoad) class_load, 243 decltype(jvmtiEventCallbacks().ClassPrepare) class_prepare) { 244 if (enable == JNI_FALSE) { 245 jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 246 JVMTI_EVENT_CLASS_LOAD, 247 nullptr); 248 if (JvmtiErrorToException(env, jvmti_env, ret)) { 249 return; 250 } 251 ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, 252 JVMTI_EVENT_CLASS_PREPARE, 253 nullptr); 254 JvmtiErrorToException(env, jvmti_env, ret); 255 return; 256 } 257 258 jvmtiEventCallbacks callbacks; 259 memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); 260 callbacks.ClassLoad = class_load; 261 callbacks.ClassPrepare = class_prepare; 262 jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); 263 if (JvmtiErrorToException(env, jvmti_env, ret)) { 264 return; 265 } 266 267 ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 268 JVMTI_EVENT_CLASS_LOAD, 269 nullptr); 270 if (JvmtiErrorToException(env, jvmti_env, ret)) { 271 return; 272 } 273 ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, 274 JVMTI_EVENT_CLASS_PREPARE, 275 nullptr); 276 JvmtiErrorToException(env, jvmti_env, ret); 277 } 278 279 static std::mutex gEventsMutex; 280 static std::vector<std::string> gEvents; 281 282 extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test912_getClassLoadMessages( 283 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) { 284 std::lock_guard<std::mutex> guard(gEventsMutex); 285 jobjectArray ret = CreateObjectArray(env, 286 static_cast<jint>(gEvents.size()), 287 "java/lang/String", 288 [&](jint i) { 289 return env->NewStringUTF(gEvents[i].c_str()); 290 }); 291 gEvents.clear(); 292 return ret; 293 } 294 295 class ClassLoadPreparePrinter { 296 public: 297 static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, 298 JNIEnv* jni_env, 299 jthread thread, 300 jclass klass) { 301 std::string name = GetClassName(jenv, jni_env, klass); 302 if (name == "") { 303 return; 304 } 305 std::string thread_name = GetThreadName(jenv, jni_env, thread); 306 if (thread_name == "") { 307 return; 308 } 309 if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) { 310 return; 311 } 312 313 std::lock_guard<std::mutex> guard(gEventsMutex); 314 gEvents.push_back(android::base::StringPrintf("Load: %s on %s", 315 name.c_str(), 316 thread_name.c_str())); 317 } 318 319 static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, 320 JNIEnv* jni_env, 321 jthread thread, 322 jclass klass) { 323 std::string name = GetClassName(jenv, jni_env, klass); 324 if (name == "") { 325 return; 326 } 327 std::string thread_name = GetThreadName(jenv, jni_env, thread); 328 if (thread_name == "") { 329 return; 330 } 331 if (thread_name_filter_ != "" && thread_name_filter_ != thread_name) { 332 return; 333 } 334 std::string cur_thread_name = GetThreadName(jenv, jni_env, nullptr); 335 336 std::lock_guard<std::mutex> guard(gEventsMutex); 337 gEvents.push_back(android::base::StringPrintf("Prepare: %s on %s (cur=%s)", 338 name.c_str(), 339 thread_name.c_str(), 340 cur_thread_name.c_str())); 341 } 342 343 static std::string GetThreadName(jvmtiEnv* jenv, JNIEnv* jni_env, jthread thread) { 344 jvmtiThreadInfo info; 345 jvmtiError result = jenv->GetThreadInfo(thread, &info); 346 if (result != JVMTI_ERROR_NONE) { 347 if (jni_env != nullptr) { 348 JvmtiErrorToException(jni_env, jenv, result); 349 } else { 350 printf("Failed to get thread name.\n"); 351 } 352 return ""; 353 } 354 355 std::string tmp(info.name); 356 jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)); 357 jni_env->DeleteLocalRef(info.context_class_loader); 358 jni_env->DeleteLocalRef(info.thread_group); 359 360 return tmp; 361 } 362 363 static std::string thread_name_filter_; 364 }; 365 std::string ClassLoadPreparePrinter::thread_name_filter_; // NOLINT [runtime/string] [4] 366 367 extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPreparePrintEvents( 368 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean enable, jthread thread) { 369 if (thread != nullptr) { 370 ClassLoadPreparePrinter::thread_name_filter_ = 371 ClassLoadPreparePrinter::GetThreadName(jvmti_env, env, thread); 372 } else { 373 ClassLoadPreparePrinter::thread_name_filter_ = ""; 374 } 375 376 EnableEvents(env, 377 enable, 378 ClassLoadPreparePrinter::ClassLoadCallback, 379 ClassLoadPreparePrinter::ClassPrepareCallback); 380 } 381 382 template<typename T> 383 static jthread RunEventThread(const std::string& name, 384 jvmtiEnv* jvmti, 385 JNIEnv* env, 386 void (*func)(jvmtiEnv*, JNIEnv*, T*), 387 T* data) { 388 // Create a Thread object. 389 std::string name_str = name; 390 name_str += ": JVMTI_THREAD-Test912"; 391 ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name_str.c_str())); 392 CHECK(thread_name.get() != nullptr); 393 394 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread")); 395 CHECK(thread_klass.get() != nullptr); 396 397 ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get())); 398 CHECK(thread.get() != nullptr); 399 400 jmethodID initID = env->GetMethodID(thread_klass.get(), "<init>", "(Ljava/lang/String;)V"); 401 CHECK(initID != nullptr); 402 403 env->CallNonvirtualVoidMethod(thread.get(), thread_klass.get(), initID, thread_name.get()); 404 CHECK(!env->ExceptionCheck()); 405 406 // Run agent thread. 407 CheckJvmtiError(jvmti, jvmti->RunAgentThread(thread.get(), 408 reinterpret_cast<jvmtiStartFunction>(func), 409 reinterpret_cast<void*>(data), 410 JVMTI_THREAD_NORM_PRIORITY)); 411 return thread.release(); 412 } 413 414 static void JoinTread(JNIEnv* env, jthread thr) { 415 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread")); 416 CHECK(thread_klass.get() != nullptr); 417 418 jmethodID joinID = env->GetMethodID(thread_klass.get(), "join", "()V"); 419 CHECK(joinID != nullptr); 420 421 env->CallVoidMethod(thr, joinID); 422 } 423 424 class ClassLoadPrepareEquality { 425 public: 426 static constexpr const char* kClassName = "Lart/Test912$ClassE;"; 427 static constexpr const char* kStorageFieldName = "STATIC"; 428 static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;"; 429 static constexpr const char* kStorageWeakFieldName = "WEAK"; 430 static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;"; 431 static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference"; 432 static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V"; 433 static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;"; 434 435 static void AgentThreadTest(jvmtiEnv* jvmti ATTRIBUTE_UNUSED, 436 JNIEnv* env, 437 jobject* obj_global) { 438 jobject target = *obj_global; 439 jobject target_local = env->NewLocalRef(target); 440 { 441 std::unique_lock<std::mutex> lk(mutex_); 442 started_ = true; 443 cond_started_.notify_all(); 444 cond_finished_.wait(lk, [] { return finished_; }); 445 CHECK(finished_); 446 } 447 CHECK(env->IsSameObject(target, target_local)); 448 } 449 450 static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, 451 JNIEnv* jni_env, 452 jthread thread ATTRIBUTE_UNUSED, 453 jclass klass) { 454 std::string name = GetClassName(jenv, jni_env, klass); 455 if (name == kClassName) { 456 found_ = true; 457 stored_class_ = jni_env->NewGlobalRef(klass); 458 weakly_stored_class_ = jni_env->NewWeakGlobalRef(klass); 459 // Check that we update the local refs. 460 agent_thread_ = static_cast<jthread>(jni_env->NewGlobalRef(RunEventThread<jobject>( 461 "local-ref", jenv, jni_env, &AgentThreadTest, static_cast<jobject*>(&stored_class_)))); 462 { 463 std::unique_lock<std::mutex> lk(mutex_); 464 cond_started_.wait(lk, [] { return started_; }); 465 } 466 // Store the value into a field in the heap. 467 SetOrCompare(jni_env, klass, true); 468 } 469 } 470 471 static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, 472 JNIEnv* jni_env, 473 jthread thread ATTRIBUTE_UNUSED, 474 jclass klass) { 475 std::string name = GetClassName(jenv, jni_env, klass); 476 if (name == kClassName) { 477 CHECK(stored_class_ != nullptr); 478 CHECK(jni_env->IsSameObject(stored_class_, klass)); 479 CHECK(jni_env->IsSameObject(weakly_stored_class_, klass)); 480 { 481 std::unique_lock<std::mutex> lk(mutex_); 482 finished_ = true; 483 cond_finished_.notify_all(); 484 } 485 // Look up the value in a field in the heap. 486 SetOrCompare(jni_env, klass, false); 487 JoinTread(jni_env, agent_thread_); 488 compared_ = true; 489 } 490 } 491 492 static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) { 493 CHECK(storage_class_ != nullptr); 494 495 // Simple direct storage. 496 jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig); 497 CHECK(field != nullptr); 498 499 if (set) { 500 jni_env->SetStaticObjectField(storage_class_, field, value); 501 CHECK(!jni_env->ExceptionCheck()); 502 } else { 503 ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field)); 504 CHECK(jni_env->IsSameObject(value, stored.get())); 505 } 506 507 // Storage as a reference. 508 ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName)); 509 CHECK(weak_ref_class.get() != nullptr); 510 jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_, 511 kStorageWeakFieldName, 512 kStorageWeakFieldSig); 513 CHECK(weak_field != nullptr); 514 if (set) { 515 // Create a WeakReference. 516 jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig); 517 CHECK(weak_init != nullptr); 518 ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(), 519 weak_init, 520 value)); 521 CHECK(weak_obj.get() != nullptr); 522 jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get()); 523 CHECK(!jni_env->ExceptionCheck()); 524 } else { 525 // Check the reference value. 526 jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig); 527 CHECK(get_referent != nullptr); 528 ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_, 529 weak_field)); 530 CHECK(weak_obj.get() != nullptr); 531 ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(), 532 get_referent)); 533 CHECK(weak_referent.get() != nullptr); 534 CHECK(jni_env->IsSameObject(value, weak_referent.get())); 535 } 536 } 537 538 static void CheckFound() { 539 CHECK(found_); 540 CHECK(compared_); 541 } 542 543 static void Free(JNIEnv* env) { 544 if (stored_class_ != nullptr) { 545 env->DeleteGlobalRef(stored_class_); 546 DCHECK(weakly_stored_class_ != nullptr); 547 env->DeleteWeakGlobalRef(weakly_stored_class_); 548 // Do not attempt to delete the local ref. It will be out of date by now. 549 } 550 } 551 552 static jclass storage_class_; 553 554 private: 555 static jobject stored_class_; 556 static jweak weakly_stored_class_; 557 static jthread agent_thread_; 558 static std::mutex mutex_; 559 static bool started_; 560 static std::condition_variable cond_finished_; 561 static bool finished_; 562 static std::condition_variable cond_started_; 563 static bool found_; 564 static bool compared_; 565 }; 566 567 jclass ClassLoadPrepareEquality::storage_class_ = nullptr; 568 jobject ClassLoadPrepareEquality::stored_class_ = nullptr; 569 jweak ClassLoadPrepareEquality::weakly_stored_class_ = nullptr; 570 jthread ClassLoadPrepareEquality::agent_thread_ = nullptr; 571 std::mutex ClassLoadPrepareEquality::mutex_; 572 bool ClassLoadPrepareEquality::started_ = false; 573 std::condition_variable ClassLoadPrepareEquality::cond_started_; 574 bool ClassLoadPrepareEquality::finished_ = false; 575 std::condition_variable ClassLoadPrepareEquality::cond_finished_; 576 bool ClassLoadPrepareEquality::found_ = false; 577 bool ClassLoadPrepareEquality::compared_ = false; 578 579 extern "C" JNIEXPORT void JNICALL Java_art_Test912_setEqualityEventStorageClass( 580 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { 581 ClassLoadPrepareEquality::storage_class_ = 582 reinterpret_cast<jclass>(env->NewGlobalRef(klass)); 583 } 584 585 extern "C" JNIEXPORT void JNICALL Java_art_Test912_enableClassLoadPrepareEqualityEvents( 586 JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { 587 EnableEvents(env, 588 b, 589 ClassLoadPrepareEquality::ClassLoadCallback, 590 ClassLoadPrepareEquality::ClassPrepareCallback); 591 if (b == JNI_FALSE) { 592 ClassLoadPrepareEquality::Free(env); 593 ClassLoadPrepareEquality::CheckFound(); 594 env->DeleteGlobalRef(ClassLoadPrepareEquality::storage_class_); 595 ClassLoadPrepareEquality::storage_class_ = nullptr; 596 } 597 } 598 599 } // namespace Test912Classes 600 } // namespace art 601