bind: handle ClassNotFoundExceptions on older Androids

If a wrapped Java class is missing at runtime, for example because
the app is running on an older Android version, the resulting class
not found exception would cause the app to crash. Fix it by ignoring
missing classes, allowing the app to avoid using them according to a
runtime version check.

Change-Id: I9138c4e2a905b180959306ecbb997695236ab273
Reviewed-on: https://go-review.googlesource.com/35853
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/bind/genclasses.go b/bind/genclasses.go
index 6be2fee..234bb4b 100644
--- a/bind/genclasses.go
+++ b/bind/genclasses.go
@@ -302,7 +302,9 @@
 	g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(g.classes))
 	g.Printf("jclass clazz;\n")
 	for _, cls := range g.classes {
-		g.Printf("clazz = (*env)->FindClass(env, %q);\n", strings.Replace(cls.FindName, ".", "/", -1))
+		g.Printf("clazz = go_seq_find_class(%q);\n", strings.Replace(cls.FindName, ".", "/", -1))
+		g.Printf("if (clazz != NULL) {\n")
+		g.Indent()
 		g.Printf("class_%s = (*env)->NewGlobalRef(env, clazz);\n", cls.JNIName)
 		if _, ok := g.goClsMap[cls.Name]; ok {
 			g.Printf("sclass_%s = (*env)->GetSuperclass(env, clazz);\n", cls.JNIName)
@@ -331,6 +333,8 @@
 				}
 			}
 		}
+		g.Outdent()
+		g.Printf("}\n")
 	}
 	g.Printf("go_seq_pop_local_frame(env);\n")
 	g.Outdent()
diff --git a/bind/testdata/classes.java.c.golden b/bind/testdata/classes.java.c.golden
index 579f33a..bb0c974 100644
--- a/bind/testdata/classes.java.c.golden
+++ b/bind/testdata/classes.java.c.golden
@@ -140,88 +140,128 @@
 void init_proxies() {
 	JNIEnv *env = go_seq_push_local_frame(20);
 	jclass clazz;
-	clazz = (*env)->FindClass(env, "java/lang/Runnable");
-	class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
-	clazz = (*env)->FindClass(env, "java/io/InputStream");
-	class_java_io_InputStream = (*env)->NewGlobalRef(env, clazz);
-	m_java_io_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
-	m_java_io_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
-	m_java_io_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
-	m_java_io_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/util/concurrent/Future");
-	class_java_util_concurrent_Future = (*env)->NewGlobalRef(env, clazz);
-	m_java_util_concurrent_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
-	m_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
-	clazz = (*env)->FindClass(env, "java/lang/Object");
-	class_java_lang_Object = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/util/concurrent/TimeUnit");
-	class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz);
-	m_java_util_concurrent_TimeUnit_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/util/Spliterators");
-	class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz);
-	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator;)Ljava/util/Iterator;");
-	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfInt;)Ljava/util/PrimitiveIterator$OfInt;");
-	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfLong;)Ljava/util/PrimitiveIterator$OfLong;");
-	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfDouble;)Ljava/util/PrimitiveIterator$OfDouble;");
-	m_java_util_Spliterators_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/lang/System");
-	class_java_lang_System = (*env)->NewGlobalRef(env, clazz);
-	m_s_java_lang_System_console = go_seq_get_static_method_id(clazz, "console", "()Ljava/io/Console;");
-	m_java_lang_System_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/Future");
-	class_java_Future = (*env)->NewGlobalRef(env, clazz);
-	sclass_java_Future = (*env)->GetSuperclass(env, clazz);
-	sclass_java_Future = (*env)->NewGlobalRef(env, sclass_java_Future);
-	m_java_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
-	sm_java_Future_get__ = go_seq_get_method_id(sclass_java_Future, "get", "()Ljava/lang/Object;");
-	m_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
-	sm_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(sclass_java_Future, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
-	clazz = (*env)->FindClass(env, "java/InputStream");
-	class_java_InputStream = (*env)->NewGlobalRef(env, clazz);
-	sclass_java_InputStream = (*env)->GetSuperclass(env, clazz);
-	sclass_java_InputStream = (*env)->NewGlobalRef(env, sclass_java_InputStream);
-	m_java_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
-	sm_java_InputStream_read__ = go_seq_get_method_id(sclass_java_InputStream, "read", "()I");
-	m_java_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
-	sm_java_InputStream_read___3B = go_seq_get_method_id(sclass_java_InputStream, "read", "([B)I");
-	m_java_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
-	sm_java_InputStream_read___3BII = go_seq_get_method_id(sclass_java_InputStream, "read", "([BII)I");
-	m_java_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	sm_java_InputStream_toString = go_seq_get_method_id(sclass_java_InputStream, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/Object");
-	class_java_Object = (*env)->NewGlobalRef(env, clazz);
-	sclass_java_Object = (*env)->GetSuperclass(env, clazz);
-	sclass_java_Object = (*env)->NewGlobalRef(env, sclass_java_Object);
-	m_java_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	sm_java_Object_toString = go_seq_get_method_id(sclass_java_Object, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/Runnable");
-	class_java_Runnable = (*env)->NewGlobalRef(env, clazz);
-	sclass_java_Runnable = (*env)->GetSuperclass(env, clazz);
-	sclass_java_Runnable = (*env)->NewGlobalRef(env, sclass_java_Runnable);
-	m_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
-	sm_java_Runnable_run = go_seq_get_method_id(sclass_java_Runnable, "run", "()V");
-	clazz = (*env)->FindClass(env, "java/util/Iterator");
-	class_java_util_Iterator = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/Spliterator");
-	class_java_util_Spliterator = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfInt");
-	class_java_util_PrimitiveIterator_OfInt = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfInt");
-	class_java_util_Spliterator_OfInt = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfLong");
-	class_java_util_PrimitiveIterator_OfLong = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfLong");
-	class_java_util_Spliterator_OfLong = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfDouble");
-	class_java_util_PrimitiveIterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfDouble");
-	class_java_util_Spliterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/io/Console");
-	class_java_io_Console = (*env)->NewGlobalRef(env, clazz);
-	m_java_io_Console_flush = go_seq_get_method_id(clazz, "flush", "()V");
-	m_java_io_Console_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	clazz = go_seq_find_class("java/lang/Runnable");
+	if (clazz != NULL) {
+		class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
+	}
+	clazz = go_seq_find_class("java/io/InputStream");
+	if (clazz != NULL) {
+		class_java_io_InputStream = (*env)->NewGlobalRef(env, clazz);
+		m_java_io_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
+		m_java_io_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
+		m_java_io_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
+		m_java_io_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/util/concurrent/Future");
+	if (clazz != NULL) {
+		class_java_util_concurrent_Future = (*env)->NewGlobalRef(env, clazz);
+		m_java_util_concurrent_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
+		m_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
+	}
+	clazz = go_seq_find_class("java/lang/Object");
+	if (clazz != NULL) {
+		class_java_lang_Object = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/util/concurrent/TimeUnit");
+	if (clazz != NULL) {
+		class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz);
+		m_java_util_concurrent_TimeUnit_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/util/Spliterators");
+	if (clazz != NULL) {
+		class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz);
+		m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator;)Ljava/util/Iterator;");
+		m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfInt;)Ljava/util/PrimitiveIterator$OfInt;");
+		m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfLong;)Ljava/util/PrimitiveIterator$OfLong;");
+		m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfDouble;)Ljava/util/PrimitiveIterator$OfDouble;");
+		m_java_util_Spliterators_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/lang/System");
+	if (clazz != NULL) {
+		class_java_lang_System = (*env)->NewGlobalRef(env, clazz);
+		m_s_java_lang_System_console = go_seq_get_static_method_id(clazz, "console", "()Ljava/io/Console;");
+		m_java_lang_System_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/Future");
+	if (clazz != NULL) {
+		class_java_Future = (*env)->NewGlobalRef(env, clazz);
+		sclass_java_Future = (*env)->GetSuperclass(env, clazz);
+		sclass_java_Future = (*env)->NewGlobalRef(env, sclass_java_Future);
+		m_java_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
+		sm_java_Future_get__ = go_seq_get_method_id(sclass_java_Future, "get", "()Ljava/lang/Object;");
+		m_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(clazz, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
+		sm_java_Future_get__JLjava_util_concurrent_TimeUnit_2 = go_seq_get_method_id(sclass_java_Future, "get", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;");
+	}
+	clazz = go_seq_find_class("java/InputStream");
+	if (clazz != NULL) {
+		class_java_InputStream = (*env)->NewGlobalRef(env, clazz);
+		sclass_java_InputStream = (*env)->GetSuperclass(env, clazz);
+		sclass_java_InputStream = (*env)->NewGlobalRef(env, sclass_java_InputStream);
+		m_java_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
+		sm_java_InputStream_read__ = go_seq_get_method_id(sclass_java_InputStream, "read", "()I");
+		m_java_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
+		sm_java_InputStream_read___3B = go_seq_get_method_id(sclass_java_InputStream, "read", "([B)I");
+		m_java_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
+		sm_java_InputStream_read___3BII = go_seq_get_method_id(sclass_java_InputStream, "read", "([BII)I");
+		m_java_InputStream_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+		sm_java_InputStream_toString = go_seq_get_method_id(sclass_java_InputStream, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/Object");
+	if (clazz != NULL) {
+		class_java_Object = (*env)->NewGlobalRef(env, clazz);
+		sclass_java_Object = (*env)->GetSuperclass(env, clazz);
+		sclass_java_Object = (*env)->NewGlobalRef(env, sclass_java_Object);
+		m_java_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+		sm_java_Object_toString = go_seq_get_method_id(sclass_java_Object, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/Runnable");
+	if (clazz != NULL) {
+		class_java_Runnable = (*env)->NewGlobalRef(env, clazz);
+		sclass_java_Runnable = (*env)->GetSuperclass(env, clazz);
+		sclass_java_Runnable = (*env)->NewGlobalRef(env, sclass_java_Runnable);
+		m_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
+		sm_java_Runnable_run = go_seq_get_method_id(sclass_java_Runnable, "run", "()V");
+	}
+	clazz = go_seq_find_class("java/util/Iterator");
+	if (clazz != NULL) {
+		class_java_util_Iterator = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/Spliterator");
+	if (clazz != NULL) {
+		class_java_util_Spliterator = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/PrimitiveIterator$OfInt");
+	if (clazz != NULL) {
+		class_java_util_PrimitiveIterator_OfInt = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/Spliterator$OfInt");
+	if (clazz != NULL) {
+		class_java_util_Spliterator_OfInt = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/PrimitiveIterator$OfLong");
+	if (clazz != NULL) {
+		class_java_util_PrimitiveIterator_OfLong = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/Spliterator$OfLong");
+	if (clazz != NULL) {
+		class_java_util_Spliterator_OfLong = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/PrimitiveIterator$OfDouble");
+	if (clazz != NULL) {
+		class_java_util_PrimitiveIterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/util/Spliterator$OfDouble");
+	if (clazz != NULL) {
+		class_java_util_Spliterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/io/Console");
+	if (clazz != NULL) {
+		class_java_io_Console = (*env)->NewGlobalRef(env, clazz);
+		m_java_io_Console_flush = go_seq_get_method_id(clazz, "flush", "()V");
+		m_java_io_Console_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
 	go_seq_pop_local_frame(env);
 }
 
diff --git a/bind/testdata/java.java.c.golden b/bind/testdata/java.java.c.golden
index 980a4dd..607b5a9 100644
--- a/bind/testdata/java.java.c.golden
+++ b/bind/testdata/java.java.c.golden
@@ -19,23 +19,35 @@
 void init_proxies() {
 	JNIEnv *env = go_seq_push_local_frame(6);
 	jclass clazz;
-	clazz = (*env)->FindClass(env, "java/lang/Float");
-	class_java_lang_Float = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Float_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/lang/Long");
-	class_java_lang_Long = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Long_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/lang/Object");
-	class_java_lang_Object = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/lang/Runnable");
-	class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
-	clazz = (*env)->FindClass(env, "java/lang/Character");
-	class_java_lang_Character = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Character_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
-	clazz = (*env)->FindClass(env, "java/lang/Character$Subset");
-	class_java_lang_Character_Subset = (*env)->NewGlobalRef(env, clazz);
-	m_java_lang_Character_Subset_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	clazz = go_seq_find_class("java/lang/Float");
+	if (clazz != NULL) {
+		class_java_lang_Float = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Float_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/lang/Long");
+	if (clazz != NULL) {
+		class_java_lang_Long = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Long_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/lang/Object");
+	if (clazz != NULL) {
+		class_java_lang_Object = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Object_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/lang/Runnable");
+	if (clazz != NULL) {
+		class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
+	}
+	clazz = go_seq_find_class("java/lang/Character");
+	if (clazz != NULL) {
+		class_java_lang_Character = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Character_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
+	clazz = go_seq_find_class("java/lang/Character$Subset");
+	if (clazz != NULL) {
+		class_java_lang_Character_Subset = (*env)->NewGlobalRef(env, clazz);
+		m_java_lang_Character_Subset_toString = go_seq_get_method_id(clazz, "toString", "()Ljava/lang/String;");
+	}
 	go_seq_pop_local_frame(env);
 }