Add trampoline invoke and ART callback tests for native-bridge

Create trampolines for JNI functions in native-bridge. Test trampoline
invoking from ART side.

Add tests for ART callbacks which called from native-bridge. In this test
they are invoked from the trampoline of JNI_OnLoad.

Change-Id: Ibaa5103fdf49a6b29a1c12f45410fe5f82d46b1e
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index f852620..5b41606 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -1,13 +1,55 @@
 Ready for native bridge tests.
 Native bridge initialized.
 Checking for support.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
-Getting trampoline.
+Getting trampoline for JNI_OnLoad with shorty (null).
+Test ART callbacks: all JNI function number is 9.
+    name:booleanMethod, signature:(ZZZZZZZZZZ)Z, shorty:ZZZZZZZZZZZ.
+    name:byteMethod, signature:(BBBBBBBBBB)B, shorty:BBBBBBBBBBB.
+    name:charMethod, signature:(CCCCCCCCCC)C, shorty:CCCCCCCCCCC.
+    name:shortMethod, signature:(SSSSSSSSSS)S, shorty:SSSSSSSSSSS.
+    name:testCallStaticVoidMethodOnSubClassNative, signature:()V, shorty:V.
+    name:testFindClassOnAttachedNativeThread, signature:()V, shorty:V.
+    name:testFindFieldOnAttachedNativeThreadNative, signature:()V, shorty:V.
+    name:testGetMirandaMethodNative, signature:()Ljava/lang/reflect/Method;, shorty:L.
+    name:testZeroLengthByteBuffers, signature:()V, shorty:V.
+trampoline_JNI_OnLoad called!
+Getting trampoline for Java_Main_testFindClassOnAttachedNativeThread with shorty V.
+trampoline_Java_Main_testFindClassOnAttachedNativeThread called!
+Getting trampoline for Java_Main_testFindFieldOnAttachedNativeThreadNative with shorty V.
+trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative called!
+Getting trampoline for Java_Main_testCallStaticVoidMethodOnSubClassNative with shorty V.
+trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative called!
+Getting trampoline for Java_Main_testGetMirandaMethodNative with shorty L.
+trampoline_Java_Main_testGetMirandaMethodNative called!
+Getting trampoline for Java_Main_testZeroLengthByteBuffers with shorty V.
+trampoline_Java_Main_testZeroLengthByteBuffers called!
+Getting trampoline for Java_Main_byteMethod with shorty BBBBBBBBBBB.
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+trampoline_Java_Main_byteMethod called!
+Getting trampoline for Java_Main_shortMethod with shorty SSSSSSSSSSS.
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+trampoline_Java_Main_shortMethod called!
+Getting trampoline for Java_Main_booleanMethod with shorty ZZZZZZZZZZZ.
+trampoline_Java_Main_booleanMethod called!
+trampoline_Java_Main_booleanMethod called!
+Getting trampoline for Java_Main_charMethod with shorty CCCCCCCCCCC.
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
+trampoline_Java_Main_charMethod called!
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index bd3ae13..82211a5 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -44,13 +44,192 @@
   bool (*isSupported)(const char* libpath);
 };
 
+struct NativeBridgeMethod {
+  const char* name;
+  const char* signature;
+  bool static_method;
+  void* fnPtr;
+  void* trampoline;
+};
 
+static NativeBridgeMethod* find_native_bridge_method(const char *name);
+static NativeBridgeArtCallbacks* gNativeBridgeArtCallbacks;
 
-static std::vector<void*> symbols;
+static jint trampoline_JNI_OnLoad(JavaVM* vm, void* reserved) {
+  JNIEnv* env = nullptr;
+  typedef jint (*FnPtr_t)(JavaVM*, void*);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("JNI_OnLoad")->fnPtr);
+
+  vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
+  if (env == nullptr) {
+    return 0;
+  }
+
+  jclass klass = env->FindClass("Main");
+  if (klass != nullptr) {
+    int i, count1, count2;
+    count1 = gNativeBridgeArtCallbacks->getNativeMethodCount(env, klass);
+    std::unique_ptr<JNINativeMethod[]> methods(new JNINativeMethod[count1]);
+    if (methods == nullptr) {
+      return 0;
+    }
+    count2 = gNativeBridgeArtCallbacks->getNativeMethods(env, klass, methods.get(), count1);
+    if (count1 == count2) {
+      printf("Test ART callbacks: all JNI function number is %d.\n", count1);
+    }
+
+    for (i = 0; i < count1; i++) {
+      NativeBridgeMethod* nb_method = find_native_bridge_method(methods[i].name);
+      if (nb_method != nullptr) {
+        jmethodID mid = nullptr;
+        if (nb_method->static_method) {
+          mid = env->GetStaticMethodID(klass, methods[i].name, nb_method->signature);
+        } else {
+          mid = env->GetMethodID(klass, methods[i].name, nb_method->signature);
+        }
+        if (mid != nullptr) {
+          const char* shorty = gNativeBridgeArtCallbacks->getMethodShorty(env, mid);
+          if (strcmp(shorty, methods[i].signature) == 0) {
+            printf("    name:%s, signature:%s, shorty:%s.\n",
+                   methods[i].name, nb_method->signature, shorty);
+          }
+        }
+      }
+    }
+    methods.release();
+  }
+
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(vm, reserved);
+}
+
+static void trampoline_Java_Main_testFindClassOnAttachedNativeThread(JNIEnv* env,
+                                                                     jclass klass) {
+  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
+    (find_native_bridge_method("testFindClassOnAttachedNativeThread")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass);
+}
+
+static void trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv* env,
+                                                                           jclass klass) {
+  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
+    (find_native_bridge_method("testFindFieldOnAttachedNativeThreadNative")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass);
+}
+
+static void trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
+                                                                          jclass klass) {
+  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
+    (find_native_bridge_method("testCallStaticVoidMethodOnSubClassNative")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass);
+}
+
+static jobject trampoline_Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass klass) {
+  typedef jobject (*FnPtr_t)(JNIEnv*, jclass);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
+    (find_native_bridge_method("testGetMirandaMethodNative")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass);
+}
+
+static void trampoline_Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass klass) {
+  typedef void (*FnPtr_t)(JNIEnv*, jclass);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
+    (find_native_bridge_method("testZeroLengthByteBuffers")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass);
+}
+
+static jbyte trampoline_Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
+                                             jbyte b3, jbyte b4, jbyte b5, jbyte b6,
+                                             jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
+  typedef jbyte (*FnPtr_t)(JNIEnv*, jclass, jbyte, jbyte, jbyte, jbyte, jbyte,
+                           jbyte, jbyte, jbyte, jbyte, jbyte);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("byteMethod")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10);
+}
+
+static jshort trampoline_Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
+                                               jshort s3, jshort s4, jshort s5, jshort s6,
+                                               jshort s7, jshort s8, jshort s9, jshort s10) {
+  typedef jshort (*FnPtr_t)(JNIEnv*, jclass, jshort, jshort, jshort, jshort, jshort,
+                            jshort, jshort, jshort, jshort, jshort);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("shortMethod")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10);
+}
+
+static jboolean trampoline_Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
+                                                   jboolean b2, jboolean b3, jboolean b4,
+                                                   jboolean b5, jboolean b6, jboolean b7,
+                                                   jboolean b8, jboolean b9, jboolean b10) {
+  typedef jboolean (*FnPtr_t)(JNIEnv*, jclass, jboolean, jboolean, jboolean, jboolean, jboolean,
+                              jboolean, jboolean, jboolean, jboolean, jboolean);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("booleanMethod")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10);
+}
+
+static jchar trampoline_Java_Main_charMethod(JNIEnv* env, jclass klass, jchar c1, jchar c2,
+                                             jchar c3, jchar c4, jchar c5, jchar c6,
+                                             jchar c7, jchar c8, jchar c9, jchar c10) {
+  typedef jchar (*FnPtr_t)(JNIEnv*, jclass, jchar, jchar, jchar, jchar, jchar,
+                           jchar, jchar, jchar, jchar, jchar);
+  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("charMethod")->fnPtr);
+  printf("%s called!\n", __FUNCTION__);
+  return fnPtr(env, klass, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10);
+}
+
+NativeBridgeMethod gNativeBridgeMethods[] = {
+  { "JNI_OnLoad", "", true, nullptr,
+    reinterpret_cast<void*>(trampoline_JNI_OnLoad) },
+  { "booleanMethod", "(ZZZZZZZZZZ)Z", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_booleanMethod) },
+  { "byteMethod", "(BBBBBBBBBB)B", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_byteMethod) },
+  { "charMethod", "(CCCCCCCCCC)C", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_charMethod) },
+  { "shortMethod", "(SSSSSSSSSS)S", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_shortMethod) },
+  { "testCallStaticVoidMethodOnSubClassNative", "()V", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative) },
+  { "testFindClassOnAttachedNativeThread", "()V", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_testFindClassOnAttachedNativeThread) },
+  { "testFindFieldOnAttachedNativeThreadNative", "()V", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative) },
+  { "testGetMirandaMethodNative", "()Ljava/lang/reflect/Method;", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_testGetMirandaMethodNative) },
+  { "testZeroLengthByteBuffers", "()V", true, nullptr,
+    reinterpret_cast<void*>(trampoline_Java_Main_testZeroLengthByteBuffers) },
+};
+
+static NativeBridgeMethod* find_native_bridge_method(const char *name) {
+  const char* pname = name;
+  if (strncmp(name, "Java_Main_", 10) == 0) {
+    pname += 10;
+  }
+
+  for (size_t i = 0; i < sizeof(gNativeBridgeMethods) / sizeof(gNativeBridgeMethods[0]); i++) {
+    if (strcmp(pname, gNativeBridgeMethods[i].name) == 0) {
+      return &gNativeBridgeMethods[i];
+    }
+  }
+  return nullptr;
+}
 
 // NativeBridgeCallbacks implementations
 extern "C" bool native_bridge_initialize(NativeBridgeArtCallbacks* art_cbs) {
-  printf("Native bridge initialized.\n");
+  if (art_cbs != nullptr) {
+    gNativeBridgeArtCallbacks = art_cbs;
+    printf("Native bridge initialized.\n");
+  }
   return true;
 }
 
@@ -80,17 +259,16 @@
 
 extern "C" void* native_bridge_getTrampoline(void* handle, const char* name, const char* shorty,
                                              uint32_t len) {
-  printf("Getting trampoline.\n");
+  printf("Getting trampoline for %s with shorty %s.\n", name, shorty);
 
   // The name here is actually the JNI name, so we can directly do the lookup.
   void* sym = dlsym(handle, name);
-  if (sym != nullptr) {
-    symbols.push_back(sym);
-  }
+  NativeBridgeMethod* method = find_native_bridge_method(name);
+  if (method == nullptr)
+    return nullptr;
+  method->fnPtr = sym;
 
-  // As libarttest is the same arch as the host, we can actually directly use the code and do not
-  // need to create a trampoline. :-)
-  return sym;
+  return method->trampoline;
 }
 
 extern "C" bool native_bridge_isSupported(const char* libpath) {
@@ -109,6 +287,3 @@
   .getTrampoline = &native_bridge_getTrampoline,
   .isSupported = &native_bridge_isSupported
 };
-
-
-