Fixes default method resolution (#159)
authorjtoman <jtoman@users.noreply.github.com>
Sat, 1 Sep 2018 19:50:00 +0000 (12:50 -0700)
committercyrille-artho <cyrille-artho@users.noreply.github.com>
Sat, 1 Sep 2018 19:50:00 +0000 (21:50 +0200)
* Fixes default method resolution

src/main/gov/nasa/jpf/vm/ClassInfo.java
src/tests/java8/DefaultMethodTest.java

index d9e5331835833c8f8e7635daf78b2f584eca2de3..83b3f5b9f63e79a3d33246d341053016a5a59744 100644 (file)
@@ -1091,17 +1091,21 @@ public class ClassInfo extends InfoObject implements Iterable<MethodInfo>, Gener
   public MethodInfo getDefaultMethod (String uniqueName) {
     MethodInfo mi = null;
     
-    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
-      for (ClassInfo ciIfc : ci.interfaces){
-        MethodInfo miIfc = ciIfc.getMethod(uniqueName, true);
-        if (miIfc != null && !miIfc.isAbstract()){
-          if (mi != null && !mi.equals(miIfc)){
+    for (ClassInfo ciIfc : this.getAllInterfaces()){
+      MethodInfo miIfc = ciIfc.getMethod(uniqueName, false);
+      if (miIfc != null && !miIfc.isAbstract() && !miIfc.isPrivate() && !miIfc.isStatic()){
+        if (mi != null && !mi.equals(miIfc)){
+          if(miIfc.getClassInfo().isSubInterfaceOf(mi.getClassInfo())) {
+            mi = miIfc;
+          } else if(mi.getClassInfo().isSubInterfaceOf(miIfc.getClassInfo())) {
+            continue;
+          } else {
             // this has to throw a IncompatibleClassChangeError in the client since Java prohibits ambiguous default methods
             String msg = "Conflicting default methods: " + mi.getFullName() + ", " + miIfc.getFullName();
             throw new ClassChangeException(msg);
-          } else {
-            mi = miIfc;
           }
+        } else {
+          mi = miIfc;
         }
       }
     }
@@ -1109,6 +1113,11 @@ public class ClassInfo extends InfoObject implements Iterable<MethodInfo>, Gener
     return mi;
   }
   
+  private boolean isSubInterfaceOf(ClassInfo classInfo) {
+    assert this.isInterface() && classInfo.isInterface();
+    return this == classInfo || this.getAllInterfaces().contains(classInfo);
+  }
+
   /**
    * This retrieves the SAM from this functional interface. Note that this is only
    * called on functional interface expecting to have a SAM. This shouldn't expect 
index d1c897717634bc97bc2270468e1e1d97e8366127..70f00b257a949d83cbdef70d5c2e6416b4529561 100644 (file)
@@ -140,7 +140,63 @@ public class DefaultMethodTest extends TestJPF {
       o.bar();
     }
   }
+  // -- most specific implementations
+  
+  public static interface Intf1 {
+    default int getInt() {
+      return 4;
+    }
+  }
+  
+  public static interface Intf2 extends Intf1 {
+    @Override
+    default int getInt() {
+      return 3;
+    }
+  }
+  public static abstract class A implements Intf1 {
+    
+  }
+  
+  public static class B extends A implements Intf2 {
+  }
+  
+  @Test
+  public void testMostSpecificImplementationResolution() {
+    if(verifyNoPropertyViolation()) {
+      B b = new B();
+      int x = b.getInt();
+      assertEquals(x, 3);
+    }
+  }
 
+  // --- inherited default methods
+  
+  interface SuperIntf {
+    default String getString() {
+      return "Hello World";
+    }
+  }
+  
+  interface SubIntf extends SuperIntf {
+    default int getZero() {
+      return 0;
+    }
+  }
+  
+  public class ConcreteClass implements SubIntf {
+    
+  }
+  
+  @Test
+  public void testInheritedDefaultMethod() {
+    if(verifyNoPropertyViolation()) {
+      ConcreteClass cls = new ConcreteClass();
+      assertEquals(cls.getString(), "Hello World");
+    }
+  }
+  
   // <2do> how to test IncompatibleClassChangeError without explicit classfile restore?
 }
 
+