Fix broken JNI IsAssignableFrom.

The expected argument order was the opposite of what the spec
dictated.

Reported-By: Mikhail Naganov <mnaganov@google.com>
bug: 16531674

(cherry picked from commit 1268b742c8cff7318dc0b5b283cbaeabfe0725ba)

Change-Id: I2b6636998c4e15c1eb10dc96a57940aca56c4d0d
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 845691d..d7200af 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -645,13 +645,15 @@
     return soa.AddLocalReference<jclass>(c->GetSuperClass());
   }
 
+  // Note: java_class1 should be safely castable to java_class2, and
+  // not the other way around.
   static jboolean IsAssignableFrom(JNIEnv* env, jclass java_class1, jclass java_class2) {
     CHECK_NON_NULL_ARGUMENT_RETURN(java_class1, JNI_FALSE);
     CHECK_NON_NULL_ARGUMENT_RETURN(java_class2, JNI_FALSE);
     ScopedObjectAccess soa(env);
     mirror::Class* c1 = soa.Decode<mirror::Class*>(java_class1);
     mirror::Class* c2 = soa.Decode<mirror::Class*>(java_class2);
-    return c1->IsAssignableFrom(c2) ? JNI_TRUE : JNI_FALSE;
+    return c2->IsAssignableFrom(c1) ? JNI_TRUE : JNI_FALSE;
   }
 
   static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass java_class) {
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 8ef1cb6..83ffae1 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -949,8 +949,28 @@
   jclass string_class = env_->FindClass("java/lang/String");
   ASSERT_NE(string_class, nullptr);
 
-  ASSERT_TRUE(env_->IsAssignableFrom(object_class, string_class));
-  ASSERT_FALSE(env_->IsAssignableFrom(string_class, object_class));
+  // A superclass is assignable from an instance of its
+  // subclass but not vice versa.
+  ASSERT_TRUE(env_->IsAssignableFrom(string_class, object_class));
+  ASSERT_FALSE(env_->IsAssignableFrom(object_class, string_class));
+
+  jclass charsequence_interface = env_->FindClass("java/lang/CharSequence");
+  ASSERT_NE(charsequence_interface, nullptr);
+
+  // An interface is assignable from an instance of an implementing
+  // class but not vice versa.
+  ASSERT_TRUE(env_->IsAssignableFrom(string_class, charsequence_interface));
+  ASSERT_FALSE(env_->IsAssignableFrom(charsequence_interface, string_class));
+
+  // Check that arrays are covariant.
+  jclass string_array_class = env_->FindClass("[Ljava/lang/String;");
+  ASSERT_NE(string_array_class, nullptr);
+  jclass object_array_class = env_->FindClass("[Ljava/lang/Object;");
+  ASSERT_NE(object_array_class, nullptr);
+  ASSERT_TRUE(env_->IsAssignableFrom(string_array_class, object_array_class));
+  ASSERT_FALSE(env_->IsAssignableFrom(object_array_class, string_array_class));
+
+  // Primitive types are tested in 004-JniTest.
 
   // Null as either class should fail.
   CheckJniAbortCatcher jni_abort_catcher;
diff --git a/test/JniTest/JniTest.java b/test/JniTest/JniTest.java
index 33418a9..daf0767 100644
--- a/test/JniTest/JniTest.java
+++ b/test/JniTest/JniTest.java
@@ -29,6 +29,7 @@
         testShortMethod();
         testBooleanMethod();
         testCharMethod();
+        testIsAssignableFromOnPrimitiveTypes();
     }
 
     private static native void testFindClassOnAttachedNativeThread();
@@ -151,4 +152,19 @@
         }
       }
     }
+
+    // http://b/16531674
+    private static void testIsAssignableFromOnPrimitiveTypes() {
+      if (!nativeIsAssignableFrom(int.class, Integer.TYPE)) {
+        System.out.println("IsAssignableFrom(int.class, Integer.TYPE) returned false, expected true");
+        throw new AssertionError();
+      }
+
+      if (!nativeIsAssignableFrom(Integer.TYPE, int.class)) {
+        System.out.println("IsAssignableFrom(Integer.TYPE, int.class) returned false, expected true");
+        throw new AssertionError();
+      }
+    }
+
+    native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to);
 }
diff --git a/test/JniTest/jni_test.cc b/test/JniTest/jni_test.cc
index 36cad72..511d893 100644
--- a/test/JniTest/jni_test.cc
+++ b/test/JniTest/jni_test.cc
@@ -286,3 +286,8 @@
 
   return char_returns[c1];
 }
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_nativeIsAssignableFrom(JNIEnv* env, jclass,
+                                                                       jclass from, jclass to) {
+  return env->IsAssignableFrom(from, to);
+}