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
Java_art_Test912_isModifiableClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassSignature(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_isInterface(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_isArrayClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassModifiers(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassFields(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassMethods(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getImplementedInterfaces(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassStatus(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassLoader(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_getClassLoaderClasses(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jobject jclassloader)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
Java_art_Test912_getClassVersion(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
GetClassName(jvmtiEnv * jenv,JNIEnv * jni_env,jclass klass)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
Java_art_Test912_getClassLoadMessages(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED)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:
ClassLoadCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread,jclass klass)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
ClassPrepareCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread,jclass klass)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
GetThreadName(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread)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
Java_art_Test912_enableClassLoadPreparePrintEvents(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jboolean enable,jthread thread)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>
RunEventThread(const std::string & name,jvmtiEnv * jvmti,JNIEnv * env,void (* func)(jvmtiEnv *,JNIEnv *,T *),T * data)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
JoinTread(JNIEnv * env,jthread thr)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
AgentThreadTest(jvmtiEnv * jvmti ATTRIBUTE_UNUSED,JNIEnv * env,jobject * obj_global)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
ClassLoadCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED,jclass klass)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
ClassPrepareCallback(jvmtiEnv * jenv,JNIEnv * jni_env,jthread thread ATTRIBUTE_UNUSED,jclass klass)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
SetOrCompare(JNIEnv * jni_env,jobject value,bool set)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
CheckFound()538 static void CheckFound() {
539 CHECK(found_);
540 CHECK(compared_);
541 }
542
Free(JNIEnv * env)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
Java_art_Test912_setEqualityEventStorageClass(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jclass klass)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
Java_art_Test912_enableClassLoadPrepareEqualityEvents(JNIEnv * env,jclass Main_klass ATTRIBUTE_UNUSED,jboolean b)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