blob: ab25319732546a3c6a14080d90e8f6bd46a92ecf [file] [log] [blame]
Andreas Gampe3c252f02016-10-27 18:25:17 -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_method.h"
33
34#include "art_jvmti.h"
35#include "art_method-inl.h"
36#include "base/enums.h"
Andreas Gampe27dfa052017-02-16 15:04:36 -080037#include "dex_file_annotations.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070038#include "events-inl.h"
Andreas Gampe13b27842016-11-07 16:48:23 -080039#include "jni_internal.h"
Andreas Gampe27dfa052017-02-16 15:04:36 -080040#include "mirror/object_array-inl.h"
Andreas Gampe36bcd4f2016-10-28 18:07:18 -070041#include "modifiers.h"
Steven Morelande431e272017-07-18 16:53:49 -070042#include "nativehelper/ScopedLocalRef.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070043#include "runtime_callbacks.h"
Andreas Gampe3c252f02016-10-27 18:25:17 -070044#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070045#include "thread-current-inl.h"
Alex Lightd78ddec2017-04-18 15:20:38 -070046#include "thread_list.h"
Alex Light0af8cde2017-04-20 13:35:05 -070047#include "ti_phase.h"
Andreas Gampe3c252f02016-10-27 18:25:17 -070048
49namespace openjdkjvmti {
50
Alex Lightd78ddec2017-04-18 15:20:38 -070051struct TiMethodCallback : public art::MethodCallback {
52 void RegisterNativeMethod(art::ArtMethod* method,
53 const void* cur_method,
54 /*out*/void** new_method)
55 OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
56 if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
57 art::Thread* thread = art::Thread::Current();
Alex Light0af8cde2017-04-20 13:35:05 -070058 art::JNIEnvExt* jnienv = thread->GetJniEnv();
Alex Lightd78ddec2017-04-18 15:20:38 -070059 ScopedLocalRef<jthread> thread_jni(
Alex Light0af8cde2017-04-20 13:35:05 -070060 jnienv, PhaseUtil::IsLivePhase() ? jnienv->AddLocalReference<jthread>(thread->GetPeer())
61 : nullptr);
Alex Lightd78ddec2017-04-18 15:20:38 -070062 art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
63 event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(
64 thread,
Alex Light0af8cde2017-04-20 13:35:05 -070065 static_cast<JNIEnv*>(jnienv),
Alex Lightd78ddec2017-04-18 15:20:38 -070066 thread_jni.get(),
67 art::jni::EncodeArtMethod(method),
68 const_cast<void*>(cur_method),
69 new_method);
70 }
71 }
72
73 EventHandler* event_handler = nullptr;
74};
75
76TiMethodCallback gMethodCallback;
77
78void MethodUtil::Register(EventHandler* handler) {
79 gMethodCallback.event_handler = handler;
80 art::ScopedThreadStateChange stsc(art::Thread::Current(),
81 art::ThreadState::kWaitingForDebuggerToAttach);
82 art::ScopedSuspendAll ssa("Add method callback");
83 art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
84}
85
86void MethodUtil::Unregister() {
87 art::ScopedThreadStateChange stsc(art::Thread::Current(),
88 art::ThreadState::kWaitingForDebuggerToAttach);
89 art::ScopedSuspendAll ssa("Remove method callback");
90 art::Runtime* runtime = art::Runtime::Current();
91 runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
92}
93
Alex Light4c174282017-07-05 10:18:18 -070094jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
95 jmethodID method,
96 jint* size_ptr,
97 unsigned char** bytecode_ptr) {
98 if (method == nullptr) {
99 return ERR(INVALID_METHODID);
100 }
101 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
102
103 if (art_method->IsNative()) {
104 return ERR(NATIVE_METHOD);
105 }
106
107 if (size_ptr == nullptr || bytecode_ptr == nullptr) {
108 return ERR(NULL_POINTER);
109 }
110
111 art::ScopedObjectAccess soa(art::Thread::Current());
112 const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
113 if (code_item == nullptr) {
114 *size_ptr = 0;
115 *bytecode_ptr = nullptr;
116 return OK;
117 }
118 // 2 bytes per instruction for dex code.
119 *size_ptr = code_item->insns_size_in_code_units_ * 2;
120 jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
121 if (err != OK) {
122 return err;
123 }
124 memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
125 return OK;
126}
127
Andreas Gampef71832e2017-01-09 11:38:04 -0800128jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
129 jmethodID method,
130 jint* size_ptr) {
131 if (method == nullptr) {
132 return ERR(INVALID_METHODID);
133 }
134 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
135
136 if (art_method->IsNative()) {
137 return ERR(NATIVE_METHOD);
138 }
139
140 if (size_ptr == nullptr) {
141 return ERR(NULL_POINTER);
142 }
143
144 art::ScopedObjectAccess soa(art::Thread::Current());
145 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
Andreas Gampee1f79b62017-04-12 21:11:28 -0700146 // Use the shorty.
147 art::ArtMethod* base_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
148 size_t arg_count = art::ArtMethod::NumArgRegisters(base_method->GetShorty());
149 if (!base_method->IsStatic()) {
150 arg_count++;
151 }
152 *size_ptr = static_cast<jint>(arg_count);
Andreas Gampef71832e2017-01-09 11:38:04 -0800153 return ERR(NONE);
154 }
155
156 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
157 *size_ptr = art_method->GetCodeItem()->ins_size_;
158
159 return ERR(NONE);
160}
161
Alex Lightce68cc62017-07-26 10:30:38 -0700162jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env,
163 jmethodID method,
164 jint* entry_count_ptr,
165 jvmtiLocalVariableEntry** table_ptr) {
166 if (method == nullptr) {
167 return ERR(INVALID_METHODID);
168 }
169 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
170
171 if (art_method->IsNative()) {
172 return ERR(NATIVE_METHOD);
173 }
174
175 if (entry_count_ptr == nullptr || table_ptr == nullptr) {
176 return ERR(NULL_POINTER);
177 }
178
179 art::ScopedObjectAccess soa(art::Thread::Current());
180 const art::DexFile* dex_file = art_method->GetDexFile();
181 const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
182 // TODO code_item == nullptr means that the method is abstract (or native, but we check that
183 // earlier). We should check what is returned by the RI in this situation since it's not clear
184 // what the appropriate return value is from the spec.
185 if (dex_file == nullptr || code_item == nullptr) {
186 return ERR(ABSENT_INFORMATION);
187 }
188
189 struct LocalVariableContext {
190 explicit LocalVariableContext(jvmtiEnv* jenv) : env_(jenv), variables_(), err_(OK) {}
191
192 static void Callback(void* raw_ctx, const art::DexFile::LocalInfo& entry) {
193 reinterpret_cast<LocalVariableContext*>(raw_ctx)->Insert(entry);
194 }
195
196 void Insert(const art::DexFile::LocalInfo& entry) {
197 if (err_ != OK) {
198 return;
199 }
200 JvmtiUniquePtr<char[]> name_str = CopyString(env_, entry.name_, &err_);
201 if (err_ != OK) {
202 return;
203 }
204 JvmtiUniquePtr<char[]> sig_str = CopyString(env_, entry.descriptor_, &err_);
205 if (err_ != OK) {
206 return;
207 }
208 JvmtiUniquePtr<char[]> generic_sig_str = CopyString(env_, entry.signature_, &err_);
209 if (err_ != OK) {
210 return;
211 }
212 variables_.push_back({
213 .start_location = static_cast<jlocation>(entry.start_address_),
214 .length = static_cast<jint>(entry.end_address_ - entry.start_address_),
215 .name = name_str.release(),
216 .signature = sig_str.release(),
217 .generic_signature = generic_sig_str.release(),
218 .slot = entry.reg_,
219 });
220 }
221
222 jvmtiError Release(jint* out_entry_count_ptr, jvmtiLocalVariableEntry** out_table_ptr) {
223 jlong table_size = sizeof(jvmtiLocalVariableEntry) * variables_.size();
224 if (err_ != OK ||
225 (err_ = env_->Allocate(table_size,
226 reinterpret_cast<unsigned char**>(out_table_ptr))) != OK) {
227 Cleanup();
228 return err_;
229 } else {
230 *out_entry_count_ptr = variables_.size();
231 memcpy(*out_table_ptr, variables_.data(), table_size);
232 return OK;
233 }
234 }
235
236 void Cleanup() {
237 for (jvmtiLocalVariableEntry& e : variables_) {
238 env_->Deallocate(reinterpret_cast<unsigned char*>(e.name));
239 env_->Deallocate(reinterpret_cast<unsigned char*>(e.signature));
240 env_->Deallocate(reinterpret_cast<unsigned char*>(e.generic_signature));
241 }
242 }
243
244 jvmtiEnv* env_;
245 std::vector<jvmtiLocalVariableEntry> variables_;
246 jvmtiError err_;
247 };
248
249 LocalVariableContext context(env);
250 if (!dex_file->DecodeDebugLocalInfo(code_item,
251 art_method->IsStatic(),
252 art_method->GetDexMethodIndex(),
253 LocalVariableContext::Callback,
254 &context)) {
255 // Something went wrong with decoding the debug information. It might as well not be there.
256 return ERR(ABSENT_INFORMATION);
257 } else {
258 return context.Release(entry_count_ptr, table_ptr);
259 }
260}
261
Andreas Gampef71832e2017-01-09 11:38:04 -0800262jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED,
263 jmethodID method,
264 jint* max_ptr) {
265 if (method == nullptr) {
266 return ERR(INVALID_METHODID);
267 }
268 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
269
270 if (art_method->IsNative()) {
271 return ERR(NATIVE_METHOD);
272 }
273
274 if (max_ptr == nullptr) {
275 return ERR(NULL_POINTER);
276 }
277
278 art::ScopedObjectAccess soa(art::Thread::Current());
279 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
280 // This isn't specified as an error case, so return 0.
281 *max_ptr = 0;
282 return ERR(NONE);
283 }
284
285 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
286 *max_ptr = art_method->GetCodeItem()->registers_size_;
287
288 return ERR(NONE);
289}
290
Andreas Gampe3c252f02016-10-27 18:25:17 -0700291jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
292 jmethodID method,
293 char** name_ptr,
294 char** signature_ptr,
295 char** generic_ptr) {
296 art::ScopedObjectAccess soa(art::Thread::Current());
Andreas Gampe13b27842016-11-07 16:48:23 -0800297 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe3c252f02016-10-27 18:25:17 -0700298 art_method = art_method->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
299
Andreas Gampe54711412017-02-21 12:41:43 -0800300 JvmtiUniquePtr<char[]> name_copy;
Andreas Gampe3c252f02016-10-27 18:25:17 -0700301 if (name_ptr != nullptr) {
302 const char* method_name = art_method->GetName();
303 if (method_name == nullptr) {
304 method_name = "<error>";
305 }
Andreas Gampe54711412017-02-21 12:41:43 -0800306 jvmtiError ret;
307 name_copy = CopyString(env, method_name, &ret);
308 if (name_copy == nullptr) {
Andreas Gampe3c252f02016-10-27 18:25:17 -0700309 return ret;
310 }
Andreas Gampe54711412017-02-21 12:41:43 -0800311 *name_ptr = name_copy.get();
Andreas Gampe3c252f02016-10-27 18:25:17 -0700312 }
313
Andreas Gampe54711412017-02-21 12:41:43 -0800314 JvmtiUniquePtr<char[]> signature_copy;
Andreas Gampe3c252f02016-10-27 18:25:17 -0700315 if (signature_ptr != nullptr) {
316 const art::Signature sig = art_method->GetSignature();
317 std::string str = sig.ToString();
Andreas Gampe54711412017-02-21 12:41:43 -0800318 jvmtiError ret;
319 signature_copy = CopyString(env, str.c_str(), &ret);
320 if (signature_copy == nullptr) {
Andreas Gampe3c252f02016-10-27 18:25:17 -0700321 return ret;
322 }
Andreas Gampe54711412017-02-21 12:41:43 -0800323 *signature_ptr = signature_copy.get();
Andreas Gampe3c252f02016-10-27 18:25:17 -0700324 }
325
Andreas Gampe862bdd82016-11-18 13:31:13 -0800326 if (generic_ptr != nullptr) {
327 *generic_ptr = nullptr;
Andreas Gampe27dfa052017-02-16 15:04:36 -0800328 if (!art_method->GetDeclaringClass()->IsProxyClass()) {
329 art::mirror::ObjectArray<art::mirror::String>* str_array =
330 art::annotations::GetSignatureAnnotationForMethod(art_method);
331 if (str_array != nullptr) {
332 std::ostringstream oss;
333 for (int32_t i = 0; i != str_array->GetLength(); ++i) {
334 oss << str_array->Get(i)->ToModifiedUtf8();
335 }
336 std::string output_string = oss.str();
Andreas Gampe54711412017-02-21 12:41:43 -0800337 jvmtiError ret;
338 JvmtiUniquePtr<char[]> generic_copy = CopyString(env, output_string.c_str(), &ret);
339 if (generic_copy == nullptr) {
Andreas Gampe27dfa052017-02-16 15:04:36 -0800340 return ret;
341 }
Andreas Gampe54711412017-02-21 12:41:43 -0800342 *generic_ptr = generic_copy.release();
Andreas Gampe27dfa052017-02-16 15:04:36 -0800343 } else if (soa.Self()->IsExceptionPending()) {
344 // TODO: Should we report an error here?
345 soa.Self()->ClearException();
346 }
347 }
Andreas Gampe862bdd82016-11-18 13:31:13 -0800348 }
Andreas Gampe3c252f02016-10-27 18:25:17 -0700349
350 // Everything is fine, release the buffers.
351 name_copy.release();
352 signature_copy.release();
353
354 return ERR(NONE);
355}
356
Andreas Gampe368a2082016-10-28 17:33:13 -0700357jvmtiError MethodUtil::GetMethodDeclaringClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
358 jmethodID method,
359 jclass* declaring_class_ptr) {
360 if (declaring_class_ptr == nullptr) {
361 return ERR(NULL_POINTER);
362 }
363
Andreas Gampe13b27842016-11-07 16:48:23 -0800364 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe368a2082016-10-28 17:33:13 -0700365 // Note: No GetInterfaceMethodIfProxy, we want to actual class.
366
Andreas Gampe13b27842016-11-07 16:48:23 -0800367 art::ScopedObjectAccess soa(art::Thread::Current());
Andreas Gampe368a2082016-10-28 17:33:13 -0700368 art::mirror::Class* klass = art_method->GetDeclaringClass();
369 *declaring_class_ptr = soa.AddLocalReference<jclass>(klass);
370
371 return ERR(NONE);
372}
373
Andreas Gampef71832e2017-01-09 11:38:04 -0800374jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED,
375 jmethodID method,
376 jlocation* start_location_ptr,
377 jlocation* end_location_ptr) {
378 if (method == nullptr) {
379 return ERR(INVALID_METHODID);
380 }
381 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
382
383 if (art_method->IsNative()) {
384 return ERR(NATIVE_METHOD);
385 }
386
387 if (start_location_ptr == nullptr || end_location_ptr == nullptr) {
388 return ERR(NULL_POINTER);
389 }
390
391 art::ScopedObjectAccess soa(art::Thread::Current());
392 if (art_method->IsProxyMethod() || art_method->IsAbstract()) {
Andreas Gampee1f79b62017-04-12 21:11:28 -0700393 // This isn't specified as an error case, so return -1/-1 as the RI does.
394 *start_location_ptr = -1;
395 *end_location_ptr = -1;
Andreas Gampef71832e2017-01-09 11:38:04 -0800396 return ERR(NONE);
397 }
398
399 DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
400 *start_location_ptr = 0;
401 *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
402
403 return ERR(NONE);
404}
405
Andreas Gampe36bcd4f2016-10-28 18:07:18 -0700406jvmtiError MethodUtil::GetMethodModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED,
407 jmethodID method,
408 jint* modifiers_ptr) {
409 if (modifiers_ptr == nullptr) {
410 return ERR(NULL_POINTER);
411 }
412
Andreas Gampe13b27842016-11-07 16:48:23 -0800413 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
Andreas Gampe36bcd4f2016-10-28 18:07:18 -0700414 uint32_t modifiers = art_method->GetAccessFlags();
415
416 // Note: Keep this code in sync with Executable.fixMethodFlags.
417 if ((modifiers & art::kAccAbstract) != 0) {
418 modifiers &= ~art::kAccNative;
419 }
420 modifiers &= ~art::kAccSynchronized;
421 if ((modifiers & art::kAccDeclaredSynchronized) != 0) {
422 modifiers |= art::kAccSynchronized;
423 }
424 modifiers &= art::kAccJavaFlagsMask;
425
426 *modifiers_ptr = modifiers;
427 return ERR(NONE);
428}
429
Andreas Gampeda3e5612016-12-13 19:00:53 -0800430using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
431
432static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
433 LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
434 jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
435 static_cast<jint>(entry.line_) };
436 context->push_back(jvmti_entry);
437 return false; // Collect all, no early exit.
438}
439
440jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
441 jmethodID method,
442 jint* entry_count_ptr,
443 jvmtiLineNumberEntry** table_ptr) {
444 if (method == nullptr) {
445 return ERR(NULL_POINTER);
446 }
447 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
448 DCHECK(!art_method->IsRuntimeMethod());
449
450 const art::DexFile::CodeItem* code_item;
451 const art::DexFile* dex_file;
452 {
453 art::ScopedObjectAccess soa(art::Thread::Current());
454
455 if (art_method->IsProxyMethod()) {
456 return ERR(ABSENT_INFORMATION);
457 }
458 if (art_method->IsNative()) {
459 return ERR(NATIVE_METHOD);
460 }
461 if (entry_count_ptr == nullptr || table_ptr == nullptr) {
462 return ERR(NULL_POINTER);
463 }
464
465 code_item = art_method->GetCodeItem();
466 dex_file = art_method->GetDexFile();
467 DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
468 }
469
470 LineNumberContext context;
471 bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
472 if (!success) {
473 return ERR(ABSENT_INFORMATION);
474 }
475
476 unsigned char* data;
477 jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
478 jvmtiError alloc_error = env->Allocate(mem_size, &data);
479 if (alloc_error != ERR(NONE)) {
480 return alloc_error;
481 }
482 *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
483 memcpy(*table_ptr, context.data(), mem_size);
484 *entry_count_ptr = static_cast<jint>(context.size());
485
486 return ERR(NONE);
487}
488
Andreas Gampefdeef522017-01-09 14:40:25 -0800489template <typename T>
490static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
491 jmethodID method,
492 T test,
493 jboolean* is_t_ptr) {
494 if (method == nullptr) {
495 return ERR(INVALID_METHODID);
496 }
497 if (is_t_ptr == nullptr) {
498 return ERR(NULL_POINTER);
499 }
500
501 art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
502 *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
503
504 return ERR(NONE);
505}
506
507jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
508 auto test = [](art::ArtMethod* method) {
509 return method->IsNative();
510 };
511 return IsMethodT(env, m, test, is_native_ptr);
512}
513
514jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
515 auto test = [](art::ArtMethod* method) {
516 return method->IsObsolete();
517 };
518 return IsMethodT(env, m, test, is_obsolete_ptr);
519}
520
521jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
522 auto test = [](art::ArtMethod* method) {
523 return method->IsSynthetic();
524 };
525 return IsMethodT(env, m, test, is_synthetic_ptr);
526}
527
Andreas Gampe3c252f02016-10-27 18:25:17 -0700528} // namespace openjdkjvmti