blob: 450f75a813955c70f5f95c03dc31f52cbdf118c8 [file] [log] [blame]
Andreas Gampee492ae32016-10-28 19:34:57 -07001/* Copyright (C) 2016 The Android Open Source Project
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This file implements interfaces from the file jvmti.h. This implementation
5 * is licensed under the same terms as the file jvmti.h. The
6 * copyright and license information for the file jvmti.h follows.
7 *
8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10 *
11 * This code is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 only, as
13 * published by the Free Software Foundation. Oracle designates this
14 * particular file as subject to the "Classpath" exception as provided
15 * by Oracle in the LICENSE file that accompanied this code.
16 *
17 * This code is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * version 2 for more details (a copy is included in the LICENSE file that
21 * accompanied this code).
22 *
23 * You should have received a copy of the GNU General Public License version
24 * 2 along with this work; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28 * or visit www.oracle.com if you need additional information or have any
29 * questions.
30 */
31
32#include "ti_class.h"
33
Andreas Gampee6377462017-01-20 17:37:50 -080034#include <mutex>
35#include <unordered_set>
36
Andreas Gampee492ae32016-10-28 19:34:57 -070037#include "art_jvmti.h"
Andreas Gampee6377462017-01-20 17:37:50 -080038#include "base/macros.h"
Andreas Gampe70f16392017-01-16 14:20:10 -080039#include "class_table-inl.h"
40#include "class_linker.h"
Andreas Gampee6377462017-01-20 17:37:50 -080041#include "events-inl.h"
42#include "handle.h"
43#include "jni_env_ext-inl.h"
Andreas Gampeac587272017-01-05 15:21:34 -080044#include "jni_internal.h"
Andreas Gampe70f16392017-01-16 14:20:10 -080045#include "runtime.h"
Andreas Gampee6377462017-01-20 17:37:50 -080046#include "runtime_callbacks.h"
47#include "ScopedLocalRef.h"
Andreas Gampee492ae32016-10-28 19:34:57 -070048#include "scoped_thread_state_change-inl.h"
49#include "thread-inl.h"
Andreas Gampee6377462017-01-20 17:37:50 -080050#include "thread_list.h"
Andreas Gampee492ae32016-10-28 19:34:57 -070051
52namespace openjdkjvmti {
53
Andreas Gampee6377462017-01-20 17:37:50 -080054struct ClassCallback : public art::ClassLoadCallback {
55 void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
56 if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
57 art::Thread* thread = art::Thread::Current();
58 ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
59 thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
60 ScopedLocalRef<jclass> jthread(
61 thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
62 {
63 art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
64 event_handler->DispatchEvent(thread,
65 ArtJvmtiEvent::kClassLoad,
66 reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
67 jthread.get(),
68 jklass.get());
69 }
70 AddTempClass(thread, jklass.get());
71 }
72 }
73
74 void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
75 art::Handle<art::mirror::Class> klass)
76 REQUIRES_SHARED(art::Locks::mutator_lock_) {
77 if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) {
78 art::Thread* thread = art::Thread::Current();
79 ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
80 thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
81 ScopedLocalRef<jclass> jthread(
82 thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
83 art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
84 event_handler->DispatchEvent(thread,
85 ArtJvmtiEvent::kClassPrepare,
86 reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
87 jthread.get(),
88 jklass.get());
89 }
90 }
91
92 void AddTempClass(art::Thread* self, jclass klass) {
93 std::unique_lock<std::mutex> mu(temp_classes_lock);
94 temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass)));
95 }
96
97 void HandleTempClass(art::Handle<art::mirror::Class> temp_klass,
98 art::Handle<art::mirror::Class> klass)
99 REQUIRES_SHARED(art::Locks::mutator_lock_) {
100 std::unique_lock<std::mutex> mu(temp_classes_lock);
101 if (temp_classes.empty()) {
102 return;
103 }
104
105 art::Thread* self = art::Thread::Current();
106 for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) {
107 if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) {
108 temp_classes.erase(it);
109 FixupTempClass(temp_klass, klass);
110 }
111 }
112 }
113
114 void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED,
115 art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED)
116 REQUIRES_SHARED(art::Locks::mutator_lock_) {
117 // TODO: Implement.
118 }
119
120 // A set of all the temp classes we have handed out. We have to fix up references to these.
121 // For simplicity, we store the temp classes as JNI global references in a vector. Normally a
122 // Prepare event will closely follow, so the vector should be small.
123 std::mutex temp_classes_lock;
124 std::vector<jclass> temp_classes;
125
126 EventHandler* event_handler = nullptr;
127};
128
129ClassCallback gClassCallback;
130
131void ClassUtil::Register(EventHandler* handler) {
132 gClassCallback.event_handler = handler;
133 art::ScopedThreadStateChange stsc(art::Thread::Current(),
134 art::ThreadState::kWaitingForDebuggerToAttach);
135 art::ScopedSuspendAll ssa("Add load callback");
136 art::Runtime::Current()->GetRuntimeCallbacks()->AddClassLoadCallback(&gClassCallback);
137}
138
139void ClassUtil::Unregister() {
140 art::ScopedThreadStateChange stsc(art::Thread::Current(),
141 art::ThreadState::kWaitingForDebuggerToAttach);
142 art::ScopedSuspendAll ssa("Remove thread callback");
143 art::Runtime* runtime = art::Runtime::Current();
144 runtime->GetRuntimeCallbacks()->RemoveClassLoadCallback(&gClassCallback);
145}
146
Andreas Gampeac587272017-01-05 15:21:34 -0800147jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
148 jclass jklass,
149 jint* field_count_ptr,
150 jfieldID** fields_ptr) {
151 art::ScopedObjectAccess soa(art::Thread::Current());
152 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
153 if (klass == nullptr) {
154 return ERR(INVALID_CLASS);
155 }
156
157 if (field_count_ptr == nullptr || fields_ptr == nullptr) {
158 return ERR(NULL_POINTER);
159 }
160
Andreas Gampeac587272017-01-05 15:21:34 -0800161 art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields();
162 art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields();
163 size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
164
165 unsigned char* out_ptr;
166 jvmtiError allocError = env->Allocate(array_size * sizeof(jfieldID), &out_ptr);
167 if (allocError != ERR(NONE)) {
168 return allocError;
169 }
170 jfieldID* field_array = reinterpret_cast<jfieldID*>(out_ptr);
171
172 size_t array_idx = 0;
173 for (art::ArtField& field : sfields) {
174 field_array[array_idx] = art::jni::EncodeArtField(&field);
175 ++array_idx;
176 }
177 for (art::ArtField& field : ifields) {
178 field_array[array_idx] = art::jni::EncodeArtField(&field);
179 ++array_idx;
180 }
181
182 *field_count_ptr = static_cast<jint>(array_size);
183 *fields_ptr = field_array;
184
185 return ERR(NONE);
186}
187
Andreas Gampe18fee4d2017-01-06 11:36:35 -0800188jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env,
189 jclass jklass,
190 jint* method_count_ptr,
191 jmethodID** methods_ptr) {
192 art::ScopedObjectAccess soa(art::Thread::Current());
193 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
194 if (klass == nullptr) {
195 return ERR(INVALID_CLASS);
196 }
197
198 if (method_count_ptr == nullptr || methods_ptr == nullptr) {
199 return ERR(NULL_POINTER);
200 }
201
202 size_t array_size = klass->NumDeclaredVirtualMethods() + klass->NumDirectMethods();
203 unsigned char* out_ptr;
204 jvmtiError allocError = env->Allocate(array_size * sizeof(jmethodID), &out_ptr);
205 if (allocError != ERR(NONE)) {
206 return allocError;
207 }
208 jmethodID* method_array = reinterpret_cast<jmethodID*>(out_ptr);
209
210 if (art::kIsDebugBuild) {
211 size_t count = 0;
212 for (auto& m ATTRIBUTE_UNUSED : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
213 count++;
214 }
215 CHECK_EQ(count, klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods());
216 }
217
218 size_t array_idx = 0;
219 for (auto& m : klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
220 method_array[array_idx] = art::jni::EncodeArtMethod(&m);
221 ++array_idx;
222 }
223
224 *method_count_ptr = static_cast<jint>(array_size);
225 *methods_ptr = method_array;
226
227 return ERR(NONE);
228}
229
Andreas Gampe8b07e472017-01-06 14:20:39 -0800230jvmtiError ClassUtil::GetImplementedInterfaces(jvmtiEnv* env,
231 jclass jklass,
232 jint* interface_count_ptr,
233 jclass** interfaces_ptr) {
234 art::ScopedObjectAccess soa(art::Thread::Current());
235 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
236 if (klass == nullptr) {
237 return ERR(INVALID_CLASS);
238 }
239
240 if (interface_count_ptr == nullptr || interfaces_ptr == nullptr) {
241 return ERR(NULL_POINTER);
242 }
243
244 // Need to handle array specifically. Arrays implement Serializable and Cloneable, but the
245 // spec says these should not be reported.
246 if (klass->IsArrayClass()) {
247 *interface_count_ptr = 0;
248 *interfaces_ptr = nullptr; // TODO: Should we allocate a dummy here?
249 return ERR(NONE);
250 }
251
252 size_t array_size = klass->NumDirectInterfaces();
253 unsigned char* out_ptr;
254 jvmtiError allocError = env->Allocate(array_size * sizeof(jclass), &out_ptr);
255 if (allocError != ERR(NONE)) {
256 return allocError;
257 }
258 jclass* interface_array = reinterpret_cast<jclass*>(out_ptr);
259
260 art::StackHandleScope<1> hs(soa.Self());
261 art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
262
263 for (uint32_t idx = 0; idx != array_size; ++idx) {
264 art::ObjPtr<art::mirror::Class> inf_klass =
265 art::mirror::Class::ResolveDirectInterface(soa.Self(), h_klass, idx);
266 if (inf_klass == nullptr) {
267 soa.Self()->ClearException();
268 env->Deallocate(out_ptr);
269 // TODO: What is the right error code here?
270 return ERR(INTERNAL);
271 }
272 interface_array[idx] = soa.AddLocalReference<jclass>(inf_klass);
273 }
274
275 *interface_count_ptr = static_cast<jint>(array_size);
276 *interfaces_ptr = interface_array;
277
278 return ERR(NONE);
279}
Andreas Gampe18fee4d2017-01-06 11:36:35 -0800280
Andreas Gampee492ae32016-10-28 19:34:57 -0700281jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
282 jclass jklass,
283 char** signature_ptr,
284 char** generic_ptr) {
285 art::ScopedObjectAccess soa(art::Thread::Current());
286 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
287 if (klass == nullptr) {
288 return ERR(INVALID_CLASS);
289 }
290
291 JvmtiUniquePtr sig_copy;
292 if (signature_ptr != nullptr) {
293 std::string storage;
294 const char* descriptor = klass->GetDescriptor(&storage);
295
296 unsigned char* tmp;
297 jvmtiError ret = CopyString(env, descriptor, &tmp);
298 if (ret != ERR(NONE)) {
299 return ret;
300 }
301 sig_copy = MakeJvmtiUniquePtr(env, tmp);
302 *signature_ptr = reinterpret_cast<char*>(tmp);
303 }
304
305 // TODO: Support generic signature.
Andreas Gampee6377462017-01-20 17:37:50 -0800306 if (generic_ptr != nullptr) {
307 *generic_ptr = nullptr;
308 }
Andreas Gampee492ae32016-10-28 19:34:57 -0700309
310 // Everything is fine, release the buffers.
311 sig_copy.release();
312
313 return ERR(NONE);
314}
315
Andreas Gampeff9d2092017-01-06 09:12:49 -0800316jvmtiError ClassUtil::GetClassStatus(jvmtiEnv* env ATTRIBUTE_UNUSED,
317 jclass jklass,
318 jint* status_ptr) {
319 art::ScopedObjectAccess soa(art::Thread::Current());
320 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
321 if (klass == nullptr) {
322 return ERR(INVALID_CLASS);
323 }
324
325 if (status_ptr == nullptr) {
326 return ERR(NULL_POINTER);
327 }
328
329 if (klass->IsArrayClass()) {
330 *status_ptr = JVMTI_CLASS_STATUS_ARRAY;
331 } else if (klass->IsPrimitive()) {
332 *status_ptr = JVMTI_CLASS_STATUS_PRIMITIVE;
333 } else {
334 *status_ptr = JVMTI_CLASS_STATUS_VERIFIED; // All loaded classes are structurally verified.
335 // This is finicky. If there's an error, we'll say it wasn't prepared.
336 if (klass->IsResolved()) {
337 *status_ptr |= JVMTI_CLASS_STATUS_PREPARED;
338 }
339 if (klass->IsInitialized()) {
340 *status_ptr |= JVMTI_CLASS_STATUS_INITIALIZED;
341 }
342 // Technically the class may be erroneous for other reasons, but we do not have enough info.
343 if (klass->IsErroneous()) {
344 *status_ptr |= JVMTI_CLASS_STATUS_ERROR;
345 }
346 }
347
348 return ERR(NONE);
349}
350
Andreas Gampe4fd66ec2017-01-05 14:42:13 -0800351template <typename T>
352static jvmtiError ClassIsT(jclass jklass, T test, jboolean* is_t_ptr) {
353 art::ScopedObjectAccess soa(art::Thread::Current());
354 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
355 if (klass == nullptr) {
356 return ERR(INVALID_CLASS);
357 }
358
359 if (is_t_ptr == nullptr) {
360 return ERR(NULL_POINTER);
361 }
362
363 *is_t_ptr = test(klass) ? JNI_TRUE : JNI_FALSE;
364 return ERR(NONE);
365}
366
367jvmtiError ClassUtil::IsInterface(jvmtiEnv* env ATTRIBUTE_UNUSED,
368 jclass jklass,
369 jboolean* is_interface_ptr) {
370 auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
371 return klass->IsInterface();
372 };
373 return ClassIsT(jklass, test, is_interface_ptr);
374}
375
376jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
377 jclass jklass,
378 jboolean* is_array_class_ptr) {
379 auto test = [](art::ObjPtr<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
380 return klass->IsArrayClass();
381 };
382 return ClassIsT(jklass, test, is_array_class_ptr);
383}
384
Andreas Gampe64013e52017-01-06 13:07:19 -0800385// Keep this in sync with Class.getModifiers().
386static uint32_t ClassGetModifiers(art::Thread* self, art::ObjPtr<art::mirror::Class> klass)
387 REQUIRES_SHARED(art::Locks::mutator_lock_) {
388 if (klass->IsArrayClass()) {
389 uint32_t component_modifiers = ClassGetModifiers(self, klass->GetComponentType());
390 if ((component_modifiers & art::kAccInterface) != 0) {
391 component_modifiers &= ~(art::kAccInterface | art::kAccStatic);
392 }
393 return art::kAccAbstract | art::kAccFinal | component_modifiers;
394 }
395
396 uint32_t modifiers = klass->GetAccessFlags() & art::kAccJavaFlagsMask;
397
398 art::StackHandleScope<1> hs(self);
399 art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass));
400 return art::mirror::Class::GetInnerClassFlags(h_klass, modifiers);
401}
402
403jvmtiError ClassUtil::GetClassModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
404 jclass jklass,
405 jint* modifiers_ptr) {
406 art::ScopedObjectAccess soa(art::Thread::Current());
407 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
408 if (klass == nullptr) {
409 return ERR(INVALID_CLASS);
410 }
411
412 if (modifiers_ptr == nullptr) {
413 return ERR(NULL_POINTER);
414 }
415
416 *modifiers_ptr = ClassGetModifiers(soa.Self(), klass);
417
418 return ERR(NONE);
419}
420
Andreas Gampe8f5b6032017-01-06 15:50:55 -0800421jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED,
422 jclass jklass,
423 jobject* classloader_ptr) {
424 art::ScopedObjectAccess soa(art::Thread::Current());
425 art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass);
426 if (klass == nullptr) {
427 return ERR(INVALID_CLASS);
428 }
429
430 if (classloader_ptr == nullptr) {
431 return ERR(NULL_POINTER);
432 }
433
434 *classloader_ptr = soa.AddLocalReference<jobject>(klass->GetClassLoader());
435
436 return ERR(NONE);
437}
438
Andreas Gampe70f16392017-01-16 14:20:10 -0800439jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env,
440 jobject initiating_loader,
441 jint* class_count_ptr,
442 jclass** classes_ptr) {
443 UNUSED(env, initiating_loader, class_count_ptr, classes_ptr);
444
445 if (class_count_ptr == nullptr || classes_ptr == nullptr) {
446 return ERR(NULL_POINTER);
447 }
448 art::Thread* self = art::Thread::Current();
449 if (!self->GetJniEnv()->IsInstanceOf(initiating_loader,
450 art::WellKnownClasses::java_lang_ClassLoader)) {
451 return ERR(ILLEGAL_ARGUMENT);
452 }
453 if (self->GetJniEnv()->IsInstanceOf(initiating_loader,
454 art::WellKnownClasses::java_lang_BootClassLoader)) {
455 // Need to use null for the BootClassLoader.
456 initiating_loader = nullptr;
457 }
458
459 art::ScopedObjectAccess soa(self);
460 art::ObjPtr<art::mirror::ClassLoader> class_loader =
461 soa.Decode<art::mirror::ClassLoader>(initiating_loader);
462
463 art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
464
465 art::ReaderMutexLock mu(self, *art::Locks::classlinker_classes_lock_);
466
467 art::ClassTable* class_table = class_linker->ClassTableForClassLoader(class_loader);
468 if (class_table == nullptr) {
469 // Nothing loaded.
470 *class_count_ptr = 0;
471 *classes_ptr = nullptr;
472 return ERR(NONE);
473 }
474
475 struct ClassTableCount {
476 bool operator()(art::ObjPtr<art::mirror::Class> klass) {
477 DCHECK(klass != nullptr);
478 ++count;
479 return true;
480 }
481
482 size_t count = 0;
483 };
484 ClassTableCount ctc;
485 class_table->Visit(ctc);
486
487 if (ctc.count == 0) {
488 // Nothing loaded.
489 *class_count_ptr = 0;
490 *classes_ptr = nullptr;
491 return ERR(NONE);
492 }
493
494 unsigned char* data;
495 jvmtiError data_result = env->Allocate(ctc.count * sizeof(jclass), &data);
496 if (data_result != ERR(NONE)) {
497 return data_result;
498 }
499 jclass* class_array = reinterpret_cast<jclass*>(data);
500
501 struct ClassTableFill {
502 bool operator()(art::ObjPtr<art::mirror::Class> klass)
503 REQUIRES_SHARED(art::Locks::mutator_lock_) {
504 DCHECK(klass != nullptr);
505 DCHECK_LT(count, ctc_ref.count);
506 local_class_array[count++] = soa_ptr->AddLocalReference<jclass>(klass);
507 return true;
508 }
509
510 jclass* local_class_array;
511 const ClassTableCount& ctc_ref;
512 art::ScopedObjectAccess* soa_ptr;
513 size_t count;
514 };
515 ClassTableFill ctf = { class_array, ctc, &soa, 0 };
516 class_table->Visit(ctf);
517 DCHECK_EQ(ctc.count, ctf.count);
518
519 *class_count_ptr = ctc.count;
520 *classes_ptr = class_array;
521
522 return ERR(NONE);
523}
524
Andreas Gampe812a2442017-01-19 22:04:46 -0800525jvmtiError ClassUtil::GetClassVersionNumbers(jvmtiEnv* env ATTRIBUTE_UNUSED,
526 jclass jklass,
527 jint* minor_version_ptr,
528 jint* major_version_ptr) {
529 art::ScopedObjectAccess soa(art::Thread::Current());
530 if (jklass == nullptr) {
531 return ERR(INVALID_CLASS);
532 }
533 art::ObjPtr<art::mirror::Object> jklass_obj = soa.Decode<art::mirror::Object>(jklass);
534 if (!jklass_obj->IsClass()) {
535 return ERR(INVALID_CLASS);
536 }
537 art::ObjPtr<art::mirror::Class> klass = jklass_obj->AsClass();
538 if (klass->IsPrimitive() || klass->IsArrayClass()) {
539 return ERR(INVALID_CLASS);
540 }
541
542 if (minor_version_ptr == nullptr || major_version_ptr == nullptr) {
543 return ERR(NULL_POINTER);
544 }
545
546 // Note: proxies will show the dex file version of java.lang.reflect.Proxy, as that is
547 // what their dex cache copies from.
548 uint32_t version = klass->GetDexFile().GetHeader().GetVersion();
549
550 *major_version_ptr = static_cast<jint>(version);
551 *minor_version_ptr = 0;
552
553 return ERR(NONE);
554}
555
Andreas Gampee492ae32016-10-28 19:34:57 -0700556} // namespace openjdkjvmti