OSDN Git Service

Make JNI work correctly with default methods.
authorAlex Light <allight@google.com>
Mon, 22 Feb 2016 21:43:29 +0000 (13:43 -0800)
committerAlex Light <allight@google.com>
Tue, 23 Feb 2016 21:24:05 +0000 (13:24 -0800)
Also adds some tests for JNI and DefaultMethods.

Bug: 27259142
Bug: 24618811

Change-Id: I31222e3e41059d803be1dbb0f40e1144ac4bf457

15 files changed:
compiler/driver/compiler_driver.cc
compiler/image_writer.cc
compiler/oat_test.cc
runtime/art_method.h
runtime/class_linker.cc
runtime/class_linker_test.cc
runtime/mirror/class-inl.h
runtime/modifiers.h
test/004-JniTest/expected.txt
test/004-JniTest/jni_test.cc
test/004-JniTest/smali/AbstractInterface.smali [new file with mode: 0644]
test/004-JniTest/smali/ConcreteClass.smali [new file with mode: 0644]
test/004-JniTest/smali/ConflictInterface.smali [new file with mode: 0644]
test/004-JniTest/smali/DefaultInterface.smali [new file with mode: 0644]
test/004-JniTest/src/Main.java

index a51dd32..9ed5ec8 100644 (file)
@@ -1077,10 +1077,8 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c,
                              image_classes);
     }
     for (auto& m : c->GetVirtualMethods(pointer_size)) {
-      if (m.IsMiranda() || (true)) {
-        StackHandleScope<1> hs2(self);
-        MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
-      }
+      StackHandleScope<1> hs2(self);
+      MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
     }
     if (klass->IsArrayClass()) {
       StackHandleScope<1> hs2(self);
index d50528e..3d31309 100644 (file)
@@ -917,7 +917,7 @@ void ImageWriter::PruneNonImageClasses() {
       // Copied methods may be held live by a class which was not an image class but have a
       // declaring class which is an image class. Set it to the resolution method to be safe and
       // prevent dangling pointers.
-      if (method->MightBeCopied() || !KeepClass(declaring_class)) {
+      if (method->IsCopied() || !KeepClass(declaring_class)) {
         mirror::DexCache::SetElementPtrSize(resolved_methods,
                                             i,
                                             resolution_method,
index d3b404a..fead839 100644 (file)
@@ -416,7 +416,7 @@ TEST_F(OatTest, WriteRead) {
     // TODO We should also check copied methods in this test.
     for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
       if (!klass->IsInterface()) {
-        EXPECT_FALSE(m.MightBeCopied());
+        EXPECT_FALSE(m.IsCopied());
       }
       CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
       ++method_index;
index f3e8d6b..ec00a7b 100644 (file)
@@ -132,9 +132,12 @@ class ArtMethod FINAL {
     return (GetAccessFlags() & kAccFinal) != 0;
   }
 
-  // Returns true if this method might be copied from another class.
-  bool MightBeCopied() {
-    return IsMiranda() || IsDefault() || IsDefaultConflicting();
+  bool IsCopied() {
+    const bool copied = (GetAccessFlags() & kAccCopied) != 0;
+    // (IsMiranda() || IsDefaultConflicting()) implies copied
+    DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
+        << "Miranda or default-conflict methods must always be copied.";
+    return copied;
   }
 
   bool IsMiranda() {
index 936c988..9ea0827 100644 (file)
@@ -759,7 +759,7 @@ static void SanityCheckArtMethod(ArtMethod* m,
     SHARED_REQUIRES(Locks::mutator_lock_) {
   if (m->IsRuntimeMethod()) {
     CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
-  } else if (m->MightBeCopied()) {
+  } else if (m->IsCopied()) {
     CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
   } else if (expected_class != nullptr) {
     CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
@@ -1137,18 +1137,18 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
 
   virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
     GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*));
-    const bool maybe_copied = method->MightBeCopied();
+    const bool is_copied = method->IsCopied();
     if (resolved_types != nullptr) {
       bool in_image_space = false;
-      if (kIsDebugBuild || maybe_copied) {
+      if (kIsDebugBuild || is_copied) {
         in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
             reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin());
       }
       // Must be in image space for non-miranda method.
-      DCHECK(maybe_copied || in_image_space)
+      DCHECK(is_copied || in_image_space)
           << resolved_types << " is not in image starting at "
           << reinterpret_cast<void*>(header_.GetImageBegin());
-      if (!maybe_copied || in_image_space) {
+      if (!is_copied || in_image_space) {
         // Go through the array so that we don't need to do a slow map lookup.
         method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types),
                                          sizeof(void*));
@@ -1157,15 +1157,15 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
     ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*));
     if (resolved_methods != nullptr) {
       bool in_image_space = false;
-      if (kIsDebugBuild || maybe_copied) {
+      if (kIsDebugBuild || is_copied) {
         in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
               reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
       }
       // Must be in image space for non-miranda method.
-      DCHECK(maybe_copied || in_image_space)
+      DCHECK(is_copied || in_image_space)
           << resolved_methods << " is not in image starting at "
           << reinterpret_cast<void*>(header_.GetImageBegin());
-      if (!maybe_copied || in_image_space) {
+      if (!is_copied || in_image_space) {
         // Go through the array so that we don't need to do a slow map lookup.
         method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods),
                                            sizeof(void*));
@@ -6459,7 +6459,7 @@ bool ClassLinker::LinkInterfaceMethods(
     for (ArtMethod* mir_method : miranda_methods) {
       ArtMethod& new_method = *out;
       new_method.CopyFrom(mir_method, image_pointer_size_);
-      new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda);
+      new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied);
       DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
           << "Miranda method should be abstract!";
       move_table.emplace(mir_method, &new_method);
@@ -6478,7 +6478,9 @@ bool ClassLinker::LinkInterfaceMethods(
       // yet it shouldn't have methods that are skipping access checks.
       // TODO This is rather arbitrary. We should maybe support classes where only some of its
       // methods are skip_access_checks.
-      new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks);
+      constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
+      constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
+      new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
       move_table.emplace(def_method, &new_method);
       ++out;
     }
@@ -6489,7 +6491,7 @@ bool ClassLinker::LinkInterfaceMethods(
       // this as a default, non-abstract method, since thats what it is. Also clear the
       // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
       // methods that are skipping access checks.
-      constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict;
+      constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
       constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
       new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
       DCHECK(new_method.IsDefaultConflicting());
index 5c3029a..488826b 100644 (file)
@@ -263,7 +263,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
     for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) {
       AssertMethod(&method);
       EXPECT_FALSE(method.IsDirect());
-      EXPECT_TRUE(method.MightBeCopied());
+      EXPECT_TRUE(method.IsCopied());
       EXPECT_TRUE(method.GetDeclaringClass()->IsInterface())
           << "declaring class: " << PrettyClass(method.GetDeclaringClass());
       EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get()))
index 3f806d3..19584ed 100644 (file)
@@ -502,7 +502,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho
   if (method->IsDirect()) {
     return method;
   }
-  if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) {
+  if (method->GetDeclaringClass()->IsInterface() && !method->IsCopied()) {
     return FindVirtualMethodForInterface(method, pointer_size);
   }
   return FindVirtualMethodForVirtual(method, pointer_size);
index ed4c5fc..c31b22e 100644 (file)
@@ -50,6 +50,11 @@ static constexpr uint32_t kAccSkipAccessChecks =      0x00080000;  // method (de
 // Used by a class to denote that the verifier has attempted to check it at least once.
 static constexpr uint32_t kAccVerificationAttempted = 0x00080000;  // class (runtime)
 static constexpr uint32_t kAccFastNative =            0x00080000;  // method (dex only)
+// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
+// that it was copied from its declaring class into another class. All methods marked kAccMiranda
+// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
+// array of a concrete class will also have this bit set.
+static constexpr uint32_t kAccCopied =                0x00100000;  // method (runtime)
 static constexpr uint32_t kAccMiranda =               0x00200000;  // method (dex only)
 static constexpr uint32_t kAccDefault =               0x00400000;  // method (runtime)
 // This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
index 86ab37e..155c6ae 100644 (file)
@@ -28,3 +28,30 @@ Subclass.<init>
 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()
index 7045482..f632331 100644 (file)
@@ -659,3 +659,65 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enterJniCriticalSection(JNIEnv* env,
     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 (file)
index 0000000..52b2fc5
--- /dev/null
@@ -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 (file)
index 0000000..a9c072f
--- /dev/null
@@ -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 (file)
index 0000000..fc3d474
--- /dev/null
@@ -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 (file)
index 0000000..1ee8721
--- /dev/null
@@ -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
index 5c39ede..9f4a852 100644 (file)
@@ -39,8 +39,11 @@ public class Main {
         testRemoveLocalObject();
         testProxyGetMethodID();
         testJniCriticalSectionAndGc();
+        testCallDefaultMethods();
     }
 
+    private static native void testCallDefaultMethods();
+
     private static native void testFindClassOnAttachedNativeThread();
 
     private static boolean testFindFieldOnAttachedNativeThreadField;
@@ -121,7 +124,7 @@ public class Main {
     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);