Make JNI work correctly with default methods.
Also adds some tests for JNI and DefaultMethods.
Bug: 27259142
Bug: 24618811
(cherry picked from commit 3612149aee482ab7a17da68b0ef5fef3879729a2)
Change-Id: I31222e3e41059d803be1dbb0f40e1144ac4bf457
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index 86ab37e..155c6ae 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -28,3 +28,30 @@
RUNNING sub object, sub class, sub nonstatic
Subclass.nonstaticMethod
PASSED sub object, sub class, sub nonstatic
+Calling method ConcreteClass->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethodWithSuper on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethodWithSuper
+DefaultInterface.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method ConcreteClass->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method ConcreteClass->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method DefaultInterface->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method DefaultInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method DefaultInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method AbstractInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 7045482..f632331 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -659,3 +659,65 @@
env->ReleasePrimitiveArrayCritical(array0, data0, 0);
}
}
+
+class JniCallDefaultMethodsTest {
+ public:
+ explicit JniCallDefaultMethodsTest(JNIEnv* env)
+ : env_(env), concrete_class_(env_->FindClass("ConcreteClass")) {
+ assert(!env_->ExceptionCheck());
+ assert(concrete_class_ != nullptr);
+ }
+
+ void Test() {
+ TestCalls("ConcreteClass", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethodWithSuper",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("DefaultInterface", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("AbstractInterface", { "JniCallSoftConflictMethod" });
+ TestCalls("ConflictInterface", { "JniCallConflictDefaultMethod" });
+ }
+
+ private:
+ void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
+ jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
+ jobject obj = env_->NewObject(concrete_class_, new_method);
+ assert(!env_->ExceptionCheck());
+ assert(obj != nullptr);
+ jclass decl_class = env_->FindClass(declaring_class);
+ assert(!env_->ExceptionCheck());
+ assert(decl_class != nullptr);
+ for (const char* method : methods) {
+ jmethodID method_id = env_->GetMethodID(decl_class, method, "()V");
+ assert(!env_->ExceptionCheck());
+ printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method);
+ env_->CallVoidMethod(obj, method_id);
+ if (env_->ExceptionCheck()) {
+ jthrowable thrown = env_->ExceptionOccurred();
+ env_->ExceptionClear();
+ jmethodID to_string = env_->GetMethodID(
+ env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
+ jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string);
+ assert(!env_->ExceptionCheck());
+ const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr);
+ assert(!env_->ExceptionCheck());
+ assert(exception_string_utf8 != nullptr);
+ printf("EXCEPTION OCCURED: %s\n", exception_string_utf8);
+ env_->ReleaseStringUTFChars(exception_string, exception_string_utf8);
+ }
+ }
+ }
+
+ JNIEnv* env_;
+ jclass concrete_class_;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) {
+ JniCallDefaultMethodsTest(env).Test();
+}
diff --git a/test/004-JniTest/smali/AbstractInterface.smali b/test/004-JniTest/smali/AbstractInterface.smali
new file mode 100644
index 0000000..52b2fc5
--- /dev/null
+++ b/test/004-JniTest/smali/AbstractInterface.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LAbstractInterface;
+.super Ljava/lang/Object;
+
+# public interface AbstractInterface {
+# public void JniCallSoftConflictMethod();
+# }
+
+.method public abstract JniCallSoftConflictMethod()V
+.end method
+
diff --git a/test/004-JniTest/smali/ConcreteClass.smali b/test/004-JniTest/smali/ConcreteClass.smali
new file mode 100644
index 0000000..a9c072f
--- /dev/null
+++ b/test/004-JniTest/smali/ConcreteClass.smali
@@ -0,0 +1,72 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LConcreteClass;
+.super Ljava/lang/Object;
+.implements LDefaultInterface;
+.implements LConflictInterface;
+.implements LAbstractInterface;
+
+# public class ConcreteClass implements DefaultInterface, ConflictInterface, AbstractInterface {
+# public void JniCallOverridenAbstractMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenAbstractMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethodWithSuper() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethodWithSuper");
+# DefaultInterface.super.JniCallOverridenDefaultMethod();
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public JniCallOverridenAbstractMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenAbstractMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethodWithSuper()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethodWithSuper"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-super {p0}, LDefaultInterface;->JniCallOverridenDefaultMethod()V
+
+ return-void
+.end method
diff --git a/test/004-JniTest/smali/ConflictInterface.smali b/test/004-JniTest/smali/ConflictInterface.smali
new file mode 100644
index 0000000..fc3d474
--- /dev/null
+++ b/test/004-JniTest/smali/ConflictInterface.smali
@@ -0,0 +1,35 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LConflictInterface;
+.super Ljava/lang/Object;
+
+# public interface ConflictInterface {
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("ConflictInterface.JniCallConflictDefaultMethod");
+# }
+#
+# }
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConflictInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
diff --git a/test/004-JniTest/smali/DefaultInterface.smali b/test/004-JniTest/smali/DefaultInterface.smali
new file mode 100644
index 0000000..1ee8721
--- /dev/null
+++ b/test/004-JniTest/smali/DefaultInterface.smali
@@ -0,0 +1,77 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LDefaultInterface;
+.super Ljava/lang/Object;
+
+# public interface DefaultInterface {
+# public default void JniCallNonOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallNonOverridenDefaultMethod");
+# }
+#
+# public default void JniCallOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenAbstractMethod();
+#
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallConflictDefaultMethod");
+# }
+#
+# public default void JniCallSoftConflictMethod() {
+# System.out.println("DefaultInterface.JniCallSoftConflictMethod");
+# }
+# }
+
+.method public JniCallNonOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallNonOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public abstract JniCallOverridenAbstractMethod()V
+.end method
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallSoftConflictMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallSoftConflictMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 5c39ede..9f4a852 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -39,8 +39,11 @@
testRemoveLocalObject();
testProxyGetMethodID();
testJniCriticalSectionAndGc();
+ testCallDefaultMethods();
}
+ private static native void testCallDefaultMethods();
+
private static native void testFindClassOnAttachedNativeThread();
private static boolean testFindFieldOnAttachedNativeThreadField;
@@ -121,7 +124,7 @@
private static void testRemoveLocalObject() {
removeLocalObject(new Object());
}
-
+
private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);