Merge "ART: Add method modifiers functions"
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 94afd2f..d364d6e 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -727,15 +727,15 @@
   }
 
   static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodNative(env, method, is_native_ptr);
   }
 
   static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr);
   }
 
   static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return MethodUtil::IsMethodObsolete(env, method, is_obsolete_ptr);
   }
 
   static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix) {
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index bb48a2b..2ddd64a 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -280,4 +280,43 @@
   return ERR(NONE);
 }
 
+template <typename T>
+static jvmtiError IsMethodT(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                            jmethodID method,
+                            T test,
+                            jboolean* is_t_ptr) {
+  if (method == nullptr) {
+    return ERR(INVALID_METHODID);
+  }
+  if (is_t_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+  *is_t_ptr = test(art_method) ? JNI_TRUE : JNI_FALSE;
+
+  return ERR(NONE);
+}
+
+jvmtiError MethodUtil::IsMethodNative(jvmtiEnv* env, jmethodID m, jboolean* is_native_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsNative();
+  };
+  return IsMethodT(env, m, test, is_native_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodObsolete(jvmtiEnv* env, jmethodID m, jboolean* is_obsolete_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsObsolete();
+  };
+  return IsMethodT(env, m, test, is_obsolete_ptr);
+}
+
+jvmtiError MethodUtil::IsMethodSynthetic(jvmtiEnv* env, jmethodID m, jboolean* is_synthetic_ptr) {
+  auto test = [](art::ArtMethod* method) {
+    return method->IsSynthetic();
+  };
+  return IsMethodT(env, m, test, is_synthetic_ptr);
+}
+
 }  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index ed60620..e5c1705 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -66,6 +66,10 @@
                                        jmethodID method,
                                        jint* entry_count_ptr,
                                        jvmtiLineNumberEntry** table_ptr);
+
+  static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr);
+  static jvmtiError IsMethodObsolete(jvmtiEnv* env, jmethodID method, jboolean* is_obsolete_ptr);
+  static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr);
 };
 
 }  // namespace openjdkjvmti
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index 539f85b..c913b3f 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -5,6 +5,9 @@
 Argument size: 1
 Location start: 0
 Location end: 40
+Is native: false
+Is obsolete: false
+Is synthetic: false
 [charAt, (I)C, null]
 class java.lang.String
 257
@@ -12,6 +15,9 @@
 Argument size: JVMTI_ERROR_NATIVE_METHOD
 Location start: JVMTI_ERROR_NATIVE_METHOD
 Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
 [sqrt, (D)D, null]
 class java.lang.Math
 265
@@ -19,6 +25,9 @@
 Argument size: JVMTI_ERROR_NATIVE_METHOD
 Location start: JVMTI_ERROR_NATIVE_METHOD
 Location end: JVMTI_ERROR_NATIVE_METHOD
+Is native: true
+Is obsolete: false
+Is synthetic: false
 [add, (Ljava/lang/Object;)Z, null]
 interface java.util.List
 1025
@@ -26,6 +35,9 @@
 Argument size: 0
 Location start: 0
 Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
 [run, ()V, null]
 class $Proxy0
 17
@@ -33,3 +45,15 @@
 Argument size: 0
 Location start: 0
 Location end: 0
+Is native: false
+Is obsolete: false
+Is synthetic: false
+class Main$NestedSynthetic
+4104
+Max locals: 1
+Argument size: 0
+Location start: 0
+Location end: 2
+Is native: false
+Is obsolete: false
+Is synthetic: true
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index d3e4987..0f8892e 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -188,6 +188,45 @@
   return end;
 }
 
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodNative(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_native;
+  jvmtiError result = jvmti_env->IsMethodNative(id, &is_native);
+  if (ErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_native;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodObsolete(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_obsolete;
+  jvmtiError result = jvmti_env->IsMethodObsolete(id, &is_obsolete);
+  if (ErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_obsolete;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodSynthetic(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject method) {
+  jmethodID id = env->FromReflectedMethod(method);
+
+  jboolean is_synthetic;
+  jvmtiError result = jvmti_env->IsMethodSynthetic(id, &is_synthetic);
+  if (ErrorToException(env, result)) {
+    return JNI_FALSE;
+  }
+
+  return is_synthetic;
+}
+
 // Don't do anything
 jint OnLoad(JavaVM* vm,
             char* options ATTRIBUTE_UNUSED,
diff --git a/test/910-methods/src/Main.java b/test/910-methods/src/Main.java
index 8f722e8..bf25a0d 100644
--- a/test/910-methods/src/Main.java
+++ b/test/910-methods/src/Main.java
@@ -32,6 +32,10 @@
     testMethod("java.util.List", "add", Object.class);
 
     testMethod(getProxyClass(), "run");
+
+    // Find a synthetic method in the dummy inner class. Do not print the name. Javac and Jack
+    // disagree on the naming of synthetic accessors.
+    testMethod(findSyntheticMethod(), NestedSynthetic.class, false);
   }
 
   private static Class<?> proxyClass = null;
@@ -54,8 +58,17 @@
   private static void testMethod(Class<?> base, String methodName, Class<?>... types)
       throws Exception {
     Method m = base.getDeclaredMethod(methodName, types);
+    testMethod(m, base, true);
+  }
+
+  private static void testMethod(Method m, Class<?> base, boolean printName) {
     String[] result = getMethodName(m);
-    System.out.println(Arrays.toString(result));
+    if (!result[0].equals(m.getName())) {
+      throw new RuntimeException("Name not equal: " + m.getName() + " vs " + result[0]);
+    }
+    if (printName) {
+      System.out.println(Arrays.toString(result));
+    }
 
     Class<?> declClass = getMethodDeclaringClass(m);
     if (base != declClass) {
@@ -96,6 +109,29 @@
     } catch (RuntimeException e) {
       System.out.println(e.getMessage());
     }
+
+    System.out.println("Is native: " + isMethodNative(m));
+    System.out.println("Is obsolete: " + isMethodObsolete(m));
+    System.out.println("Is synthetic: " + isMethodSynthetic(m));
+  }
+
+  private static class NestedSynthetic {
+    // Accessing this private field will create a synthetic accessor method;
+    private static String dummy;
+  }
+
+  private static void dummyAccess() {
+    System.out.println(NestedSynthetic.dummy);
+  }
+
+  private static Method findSyntheticMethod() throws Exception {
+    Method methods[] = NestedSynthetic.class.getDeclaredMethods();
+    for (Method m : methods) {
+      if (m.isSynthetic()) {
+        return m;
+      }
+    }
+    throw new RuntimeException("Could not find synthetic method");
   }
 
   private static native String[] getMethodName(Method m);
@@ -105,4 +141,7 @@
   private static native int getArgumentsSize(Method m);
   private static native long getMethodLocationStart(Method m);
   private static native long getMethodLocationEnd(Method m);
+  private static native boolean isMethodNative(Method m);
+  private static native boolean isMethodObsolete(Method m);
+  private static native boolean isMethodSynthetic(Method m);
 }