changeset 28:7be90179bb3b

Provided support for double colon operator used for lamabda expressions. Fixed a bug related to generating names for funcation object classes (by supporting double colon operator, a new stragety needed to generate unique names for function objects. To achive that the bootstrap ids are incorporated into names). Finally modified the method that retrieves the SAM from functional interfaces.
author nastaran <nastaran.shafiei@gmail.com>
date Thu, 25 Jun 2015 13:20:50 -0700
parents 8aded593a50f
children 820b89dd6c97
files src/main/gov/nasa/jpf/jvm/JVMClassInfo.java src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java src/main/gov/nasa/jpf/vm/ClassInfo.java src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java src/tests/java8/LambdaTest.java
diffstat 8 files changed, 140 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/gov/nasa/jpf/jvm/JVMClassInfo.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/JVMClassInfo.java	Thu Jun 25 13:20:50 2015 -0700
@@ -93,11 +93,21 @@
       int lambdaRefKind = cf.mhRefTypeAt(cpArgs[1]);
       
       int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]);
-      String clsName = cf.methodClassNameAt(mrefIdx);
+      String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.');
+      ClassInfo eclosingLambdaCls;
+      
+      if(!clsName.equals(JVMClassInfo.this.getName())) {
+        eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+      } else {
+        eclosingLambdaCls = JVMClassInfo.this;
+      }
+      
+      assert (eclosingLambdaCls!=null);
+      
       String mthName = cf.methodNameAt(mrefIdx);
       String signature = cf.methodDescriptorAt(mrefIdx);
       
-      MethodInfo lambdaBody = JVMClassInfo.this.getMethod(mthName + signature, false);
+      MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false);
       
       String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]);
             
@@ -641,7 +651,7 @@
     // creating a method corresponding to the single abstract method of the functional interface
     methods = new HashMap<String, MethodInfo>();
     
-    MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod(samUniqueName);
+    MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod();
     int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT);
     int nLocals = fiMethod.getArgumentsSize();
     int nOperands = this.nInstanceFields + nLocals;
@@ -856,6 +866,10 @@
     case ClassFile.REF_INVOKEVIRTUAL:
       cb.invokevirtual(calleeClass, calleeName, calleeSig);
       break;
+    case ClassFile.REF_NEW_INVOKESPECIAL:
+      cb.new_(calleeClass);
+      cb.invokespecial(calleeClass, calleeName, calleeSig);
+      break;
     case ClassFile.REF_INVOKESPECIAL:
       cb.invokespecial(calleeClass, calleeName, calleeSig);
       break;
--- a/src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java	Thu Jun 25 13:20:50 2015 -0700
@@ -1171,6 +1171,10 @@
     add( insnFactory.new_(cf.classNameAt(cpClassIndex)));
     pc+=3;
   }
+  public void new_(String className) {
+    add( insnFactory.new_(className));
+    pc+=3;
+  }
 
   @Override public void newarray(int typeCode) {
     add( insnFactory.newarray(typeCode));
--- a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java	Thu Jun 25 13:20:50 2015 -0700
@@ -116,10 +116,9 @@
       VM vm = VM.getVM();
       FunctionObjectFactory funcObjFactory = vm.getFunctionObjectFacotry();
       
-      String samUniqueName = samMethodName + bmi.getSamDescriptor();
       Object[] freeVariableValues = frame.getArgumentsValues(ti, freeVariableTypes);
       
-      funcObjRef = funcObjFactory.getFunctionObject(ti, fiClassInfo, samUniqueName, bmi, freeVariableTypeNames, freeVariableValues);
+      funcObjRef = funcObjFactory.getFunctionObject(bootstrapMethodIndex, ti, fiClassInfo, samMethodName, bmi, freeVariableTypeNames, freeVariableValues);
       lastFuncObj = ti.getHeap().get(funcObjRef);
     }
     
--- a/src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java	Thu Jun 25 13:20:50 2015 -0700
@@ -47,7 +47,7 @@
   
   @Override
   public String toString() {
-    return "BootstrapMethodInfo[" + enclosingClass.getName() + "." + lambdaBody.getName() + 
+    return "BootstrapMethodInfo[" + enclosingClass.getName() + "." + lambdaBody.getBaseName() + 
         "[SAM descriptor:" + samDescriptor + "]]";
   }
   
--- a/src/main/gov/nasa/jpf/vm/ClassInfo.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/ClassInfo.java	Thu Jun 25 13:20:50 2015 -0700
@@ -1109,19 +1109,34 @@
     return mi;
   }
   
-  public MethodInfo getInterfaceAbstractMethod (String uniqueName) {
-    MethodInfo mi = this.getMethod(uniqueName, true);
+  /**
+   * 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 
+   * this interface to have only one method which is abstract, since:
+   *    1. functional interface can declare the abstract methods from the java.lang.Object 
+   *       class.
+   *    2. functional interface can extend another interface which is functional, but it 
+   *       should not declare any new abstract methods.
+   *    3. functional interface can have one abstract method and any number of default
+   *       methods.
+   * 
+   * To retrieve the SAM, this method iterates over the methods of this interface and its 
+   * superinterfaces, and it returns the first method which is abstract and it does not 
+   * declare a method in java.lang.Object.
+   */
+  public MethodInfo getInterfaceAbstractMethod () {
+    ClassInfo objCi = ClassLoaderInfo.getCurrentResolvedClassInfo("java.lang.Object");
     
-    if(mi != null) {
-      return mi;
+    for(MethodInfo mi: this.methods.values()) {
+      if(mi.isAbstract() && objCi.getMethod(mi.getUniqueName(), false)==null) {
+        return mi;
+      }
     }
     
-    for (ClassInfo ci = this; ci != null && mi == null; ci = ci.superClass){
-      for (ClassInfo ciIfc : ci.interfaces){
-        mi = ciIfc.getMethod(uniqueName, true);
-        if (mi != null && mi.isAbstract()){
-          return mi;
-        }
+    for (ClassInfo ifc : this.interfaces){
+      MethodInfo mi = ifc.getInterfaceAbstractMethod();
+      if(mi!=null) {
+        return mi;
       }
     }
     
--- a/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java	Thu Jun 25 13:20:50 2015 -0700
@@ -448,8 +448,8 @@
    * This method returns a type which implements the given functional interface 
    * and contains a method that captures the behavior of the lambda expression.
    */
-  public ClassInfo getResolvedFuncObjType (ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, String[] freeVariableTypeNames) {
-    String typeName = bmi.enclosingClass.getName() + "$$" + bmi.lambdaBody.getName();
+  public ClassInfo getResolvedFuncObjType (int bsIdx, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, String[] freeVariableTypeNames) {
+    String typeName = bmi.enclosingClass.getName() + "$$Lambda$" + bsIdx;
     
     ClassInfo funcObjType = resolvedClasses.get( typeName);
     
--- a/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java	Thu Jun 25 13:20:50 2015 -0700
@@ -22,12 +22,12 @@
  */
 public class FunctionObjectFactory {
   
-  public int getFunctionObject(ThreadInfo ti, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, 
+  public int getFunctionObject(int bsIdx, ThreadInfo ti, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, 
                                          String[] freeVariableTypeNames, Object[] freeVariableValues) {
     
     ClassLoaderInfo cli = bmi.enclosingClass.getClassLoaderInfo();
     
-    ClassInfo funcObjType = cli.getResolvedFuncObjType(fiClassInfo, samUniqueName, bmi, freeVariableTypeNames);
+    ClassInfo funcObjType = cli.getResolvedFuncObjType(bsIdx, fiClassInfo, samUniqueName, bmi, freeVariableTypeNames);
     
     funcObjType.registerClass(ti);
 
--- a/src/tests/java8/LambdaTest.java	Mon May 11 12:17:18 2015 -0700
+++ b/src/tests/java8/LambdaTest.java	Thu Jun 25 13:20:50 2015 -0700
@@ -167,4 +167,92 @@
       }
     }
   }
+  
+  public static class C2 {
+    public static void throwException() {
+      throw new EnforcedException();
+    }
+  }
+  
+  @Test
+  public void testDoubleCloneOperator() {
+    if (verifyUnhandledException(EnforcedException.class.getName())) {
+      FI1 fi = C2::throwException;
+      fi.sam();
+    }
+  }
+  
+  static class A {
+    static {
+      if(true) {
+        throw new EnforcedException();
+      }
+    }
+  }
+
+  @Test
+  public void testInitDoubleCloneOperator() {
+    if (verifyUnhandledException(EnforcedException.class.getName())) {
+      new Thread(A::new).start();
+    }
+  }
+  
+  static class D {
+    static final B b = new B();
+  }
+  
+  static class B {
+    static final D a = new D();
+  }
+  
+  @Test
+  public void testClinitDeadlock() {
+    if(verifyDeadlock()) {
+      new Thread(D::new).start();
+      new B();
+    }
+  }
+  
+  @Test
+  public void testLambdaTypeName() {
+    if(verifyNoPropertyViolation()) {
+      Runnable r1 = (A::new);
+      Runnable r2 = (B::new);
+      
+      assertFalse(r1.getClass().getName().equals(r2.getClass().getName()));
+    }
+  }
+  
+  public interface FI {
+    default boolean returnTrue() {
+      return true;
+    }
+    @Override
+    public String toString();
+    public String toString(int i);
+  }
+  
+  @Test
+  public void testLambdaWithOverridenDefaultMethods() {
+    if(verifyNoPropertyViolation()) {
+      FI fi = (int i) -> {return "output:"+ i;};
+      assertEquals(fi.toString(10),"output:10");
+    }
+  }
+  
+  public interface FI4 {
+  }
+  
+  public interface FI5 extends FI {
+    @Override
+    public boolean equals(Object obj);
+  }
+  
+  @Test
+  public void testLambdaWithMultipleSuperInterfaces() {
+    if(verifyNoPropertyViolation()) {
+      FI5 fi = (int i) -> {return "output:"+ i;};
+      assertEquals(fi.toString(10),"output:10");
+    }
+  }
 }