changeset 23:db918c531e6d

streamlined class init, which was a mixed case of registerClass()/initializeClass() and pushRequiredClinits(). Now it is a single initializeClass(ti) method which combines the previous initializeClass(), pushRequiredClinits() and pushClinit() methods. The reason for combining these is the forthcoming replacement of separately locked clinits from different DirectCallStackFrames with a single synthetic frame that calls clinits from nested synchronized blocks. This is required to model hotspot, which does cause deadlocks with concurrent init of classes that cause subclass init during their clinit executions.
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Wed, 15 Apr 2015 22:40:21 -0700
parents cd7880ab73c7
children 6774e2e08d37
files .idea/compiler.xml .idea/misc.xml src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java src/main/gov/nasa/jpf/jvm/bytecode/NEW.java src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java src/main/gov/nasa/jpf/util/json/JSONObject.java src/main/gov/nasa/jpf/vm/ClassInfo.java src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java src/main/gov/nasa/jpf/vm/Instruction.java src/main/gov/nasa/jpf/vm/MJIEnv.java src/main/gov/nasa/jpf/vm/ThreadInfo.java src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java
diffstat 20 files changed, 77 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/.idea/compiler.xml	Tue Apr 14 15:01:25 2015 -0700
+++ b/.idea/compiler.xml	Wed Apr 15 22:40:21 2015 -0700
@@ -18,5 +18,6 @@
         <processorPath useClasspath="true" />
       </profile>
     </annotationProcessing>
+    <bytecodeTargetLevel target="1.8" />
   </component>
 </project>
\ No newline at end of file
--- a/.idea/misc.xml	Tue Apr 14 15:01:25 2015 -0700
+++ b/.idea/misc.xml	Wed Apr 15 22:40:21 2015 -0700
@@ -51,7 +51,7 @@
     <ConfirmationsSetting value="0" id="Add" />
     <ConfirmationsSetting value="0" id="Remove" />
   </component>
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
     <output url="file://$PROJECT_DIR$/build" />
   </component>
 </project>
\ No newline at end of file
--- a/src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java	Wed Apr 15 22:40:21 2015 -0700
@@ -56,7 +56,7 @@
 
     //--- check if this has to trigger class initialization
     ClassInfo ciField = fieldInfo.getClassInfo();
-    if (!mi.isClinit(ciField) && ciField.pushRequiredClinits(ti)) {
+    if (!mi.isClinit(ciField) && ciField.initializeClass(ti)) {
       // note - this returns the next insn in the topmost clinit that just got pushed
       return ti.getPC();
     }
--- a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java	Wed Apr 15 22:40:21 2015 -0700
@@ -105,10 +105,6 @@
         return ti.getPC();
       }
 
-      if (!fiClassInfo.isRegistered()){
-        fiClassInfo.registerClass(ti);
-      }
-
       if (fiClassInfo.initializeClass(ti)) {
         return ti.getPC();
       }
--- a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java	Wed Apr 15 22:40:21 2015 -0700
@@ -92,7 +92,7 @@
     // this can be actually different than (can be a base)
     ClassInfo ciCallee = callee.getClassInfo();
     
-    if (ciCallee.pushRequiredClinits(ti)) {
+    if (ciCallee.initializeClass(ti)) {
       // do class initialization before continuing
       // note - this returns the next insn in the topmost clinit that just got pushed
       return ti.getPC();
--- a/src/main/gov/nasa/jpf/jvm/bytecode/NEW.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/NEW.java	Wed Apr 15 22:40:21 2015 -0700
@@ -57,16 +57,11 @@
       return ti.getPC();
     }
 
-    if (!ci.isRegistered()){
-      ci.registerClass(ti);
+    if (ci.initializeClass(ti)){
+      // continue with the topframe and re-exec this insn once the clinits are done
+      return ti.getPC();
     }
 
-    // we might have to execute clinits
-      if (ci.initializeClass(ti)) {
-        // continue with the topframe and re-exec this insn once the clinits are done
-        return ti.getPC();
-      }
-
     if (heap.isOutOfMemory()) { // simulate OutOfMemoryError
       return ti.createAndThrowException("java.lang.OutOfMemoryError",
                                         "trying to allocate new " + cname);
--- a/src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java	Wed Apr 15 22:40:21 2015 -0700
@@ -67,7 +67,7 @@
 
     //--- check if this has to trigger class initialization
     ClassInfo ciField = fieldInfo.getClassInfo();
-    if (!mi.isClinit(ciField) && ciField.pushRequiredClinits(ti)) {
+    if (!mi.isClinit(ciField) && ciField.initializeClass(ti)) {
       return ti.getPC(); // this returns the next insn in the topmost clinit that just got pushed
     }
     ElementInfo eiFieldOwner = ciField.getModifiableStaticElementInfo();
--- a/src/main/gov/nasa/jpf/util/json/JSONObject.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/util/json/JSONObject.java	Wed Apr 15 22:40:21 2015 -0700
@@ -99,7 +99,7 @@
    */
   public boolean requiresClinitExecution (ClassInfo ci, ThreadInfo ti){
     while (ci != null){
-      if (ci.pushRequiredClinits(ti)){
+      if (ci.initializeClass(ti)){
         return true;
       }
 
@@ -183,7 +183,7 @@
         } else {
           // Not a special case. Fill it recursively
           ClassInfo ciField = fi.getTypeClassInfo();
-          if (ciField.pushRequiredClinits(env.getThreadInfo())){
+          if (ciField.initializeClass(env.getThreadInfo())){
             throw new ClinitRequired(ciField);
           }
           
--- a/src/main/gov/nasa/jpf/vm/ClassInfo.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/ClassInfo.java	Wed Apr 15 22:40:21 2015 -0700
@@ -1965,8 +1965,6 @@
     ClassLoaderInfo systemLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
     ClassInfo ci = systemLoader.getResolvedClassInfo(clsName);
 
-    ci.registerClass(ti); // this is safe to call on already loaded classes
-
     if (ci.initializeClass(ti)) {
       throw new ClinitRequired(ci);
     }
@@ -2144,19 +2142,56 @@
   }
   
   /**
-   * check if this class requires clinit execution. If so,
-   * push the corresponding DirectCallStackFrames.
-   * 
-   * clients have to be aware of that frames might get pushed
-   * and properly handle re-execution
+   * initialize this class and its superclasses (but not interfaces)
+   * this will cause execution of clinits of not-yet-initialized classes in this hierarchy
+   *
+   * note - we don't treat registration/initialization of a class as
+   * a sharedness-changing operation since it is done automatically by
+   * the VM and the triggering action in the SUT (e.g. static field access or method call)
+   * is the one that should update sharedness and/or break the transition accordingly
+   *
+   * @return true - if initialization pushed DirectCallStackFrames and caller has to re-execute
    */
-  public boolean pushRequiredClinits (ThreadInfo ti){
-    StaticElementInfo sei = getStaticElementInfo();    
-    if (sei == null) {
-      sei = registerClass(ti);
+  public boolean initializeClass(ThreadInfo ti){
+    int pushedFrames = 0;
+
+    // push clinits of class hierarchy (upwards, since call stack is LIFO)
+    for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      if (sei == null){
+        sei = ci.registerClass(ti);
+      }
+
+      int status = sei.getStatus();
+      if (status != INITIALIZED){
+        // we can't do setInitializing() yet because there is no global lock that
+        // covers the whole clinit chain, and we might have a context switch before executing
+        // a already pushed subclass clinit - there can be races as to which thread
+        // does the static init first. Note this case is checked in INVOKECLINIT
+        // (which is one of the reasons why we have it).
+
+        if (status != ti.getId()) {
+          // even if it is already initializing - if it does not happen in the current thread
+          // we have to sync, which we do by calling clinit
+          MethodInfo mi = ci.getMethod("<clinit>()V", false);
+          if (mi != null) {
+            DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
+            ti.pushFrame( frame);
+            pushedFrames++;
+
+          } else {
+            // it has no clinit, we can set it initialized
+            ci.setInitialized();
+          }
+        } else {
+          // ignore if it's already being initialized  by our own thread (recursive request)
+        }
+      } else {
+        break; // if this class is initialized, so are its superclasses
+      }
     }
 
-    return initializeClass(ti); // indicates if we pushed clinits
+    return (pushedFrames > 0);
   }
     
   public void setInitialized() {
@@ -2168,68 +2203,6 @@
     // notifications from the <clinit> execution before the classLoaded()
   }
 
-  /**
-   * perform static initialization of class
-   * this recursively initializes all super classes, but NOT the interfaces
-   *
-   * @param ti executing thread
-   * @return  true if clinit stackframes were pushed, idx.e. context instruction
-   * needs to be re-executed
-   */
-  public boolean initializeClass (ThreadInfo ti) {
-    int pushedFrames = 0;
-
-    // push clinits of class hierarchy (upwards, since call stack is LIFO)
-    for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
-      if (ci.pushClinit(ti)) {
-        
-        // note - we don't treat registration/initialization of a class as
-        // a sharedness-changing operation since it is done automatically by
-        // the VM and the triggering action in the SUT (e.g. static field access or method call)
-        // is the one that should update sharedness and/or break the transition accordingly
-        
-        // we can't do setInitializing() yet because there is no global lock that
-        // covers the whole clinit chain, and we might have a context switch before executing
-        // a already pushed subclass clinit - there can be races as to which thread
-        // does the static init first. Note this case is checked in INVOKECLINIT
-        // (which is one of the reasons why we have it).
-        pushedFrames++;
-      }
-    }
-
-    return (pushedFrames > 0);
-  }
-
-  /**
-   * local class initialization
-   * @return true if we pushed a &lt;clinit&gt; frame
-   */
-  protected boolean pushClinit (ThreadInfo ti) {
-    StaticElementInfo sei = getStaticElementInfo();
-    int stat = sei.getStatus();
-    
-    if (stat != INITIALIZED) {
-      if (stat != ti.getId()) {
-        // even if it is already initializing - if it does not happen in the current thread
-        // we have to sync, which we do by calling clinit
-        MethodInfo mi = getMethod("<clinit>()V", false);
-        if (mi != null) {
-          DirectCallStackFrame frame = createDirectCallStackFrame(ti, mi, 0);
-          ti.pushFrame( frame);
-          return true;
-
-        } else {
-          // it has no clinit, so it already is initialized
-          setInitialized();
-        }
-      } else {
-        // ignore if it's already being initialized  by our own thread (recursive request)
-      }
-    }
-
-    return false;
-  }
-
   public StaticElementInfo getStaticElementInfo() {
     if (id != -1) {
       return classLoader.getStatics().get( id);
--- a/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java	Wed Apr 15 22:40:21 2015 -0700
@@ -481,8 +481,6 @@
   public ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
     ClassInfo ci = getResolvedClassInfo(clsName);
 
-    ci.registerClass(ti); // this is safe to call on already loaded classes
-
     if (ci.initializeClass(ti)) {
       throw new ClinitRequired(ci);
     }
--- a/src/main/gov/nasa/jpf/vm/Instruction.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/Instruction.java	Wed Apr 15 22:40:21 2015 -0700
@@ -335,7 +335,7 @@
    * used in a number of external projects
    */
   public boolean requiresClinitExecution(ThreadInfo ti, ClassInfo ci) {
-    return ci.pushRequiredClinits(ti);
+    return ci.initializeClass(ti);
   }
 
   /**
--- a/src/main/gov/nasa/jpf/vm/MJIEnv.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/MJIEnv.java	Wed Apr 15 22:40:21 2015 -0700
@@ -1038,7 +1038,7 @@
   }
   
   public ElementInfo newElementInfo (ClassInfo ci){
-    if (ci.pushRequiredClinits(ti)){
+    if (ci.initializeClass(ti)){
       throw new ClinitRequired(ci);
     }
     
@@ -1291,7 +1291,7 @@
    *  Do a repeatInvocation() in this case 
    */
   public boolean requiresClinitExecution(ClassInfo ci) {
-    return ci.pushRequiredClinits(ti);
+    return ci.initializeClass(ti);
   }
   
   /**
@@ -1744,7 +1744,7 @@
     // NOTE: we have to repeat no matter what, since this is called from
     // a handler context (if we only had to create a class object w/o
     // calling clinit, we can't just go on)
-    ci.pushRequiredClinits(ti);
+    ci.initializeClass(ti);
     repeatInvocation();
   }
 
--- a/src/main/gov/nasa/jpf/vm/ThreadInfo.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/main/gov/nasa/jpf/vm/ThreadInfo.java	Wed Apr 15 22:40:21 2015 -0700
@@ -31,7 +31,6 @@
 import gov.nasa.jpf.util.Predicate;
 import gov.nasa.jpf.util.StringSetMatcher;
 import gov.nasa.jpf.vm.choice.BreakGenerator;
-import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet;
 
 import java.io.PrintWriter;
 import java.io.File;
@@ -1776,10 +1775,6 @@
    * thrown by the VM (or a listener)
    */
   public Instruction createAndThrowException (ClassInfo ci, String details) {
-    if (!ci.isRegistered()) {
-      ci.registerClass(this);
-    }
-
     if (ci.initializeClass(this)) {
       return getPC();
     }
--- a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java	Wed Apr 15 22:40:21 2015 -0700
@@ -19,12 +19,6 @@
 package gov.nasa.jpf.vm;
 
 import gov.nasa.jpf.annotation.MJI;
-import gov.nasa.jpf.vm.ClassInfo;
-import gov.nasa.jpf.vm.DirectCallStackFrame;
-import gov.nasa.jpf.vm.MJIEnv;
-import gov.nasa.jpf.vm.MethodInfo;
-import gov.nasa.jpf.vm.NativePeer;
-import gov.nasa.jpf.vm.ThreadInfo;
 
 
 public class JPF_gov_nasa_jpf_SerializationConstructor extends NativePeer {
@@ -57,7 +51,7 @@
       ti.pushFrame(frame);
       
       // check for & push required clinits
-      ci.pushRequiredClinits(ti);
+      ci.initializeClass(ti);
       env.repeatInvocation();
       return MJIEnv.NULL;
       
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java	Wed Apr 15 22:40:21 2015 -0700
@@ -63,7 +63,7 @@
     Instruction insn = ti.getPC();
     
     ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(DataRepresentation);
-    if (ci.pushRequiredClinits(ti)){
+    if (ci.initializeClass(ti)){
       env.repeatInvocation();
       return null;
     }
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java	Wed Apr 15 22:40:21 2015 -0700
@@ -57,7 +57,7 @@
       Instruction insn = ti.getPC();
       ClassInfo ci = env.getReferredClassInfo( robj).getComponentClassInfo();
 
-    if (ci.pushRequiredClinits(ti)){
+    if (ci.initializeClass(ti)){
         env.repeatInvocation();
         return MJIEnv.NULL;
       }
@@ -156,7 +156,7 @@
     ThreadInfo ti = env.getThreadInfo();
     Instruction insn = ti.getPC();
 
-    if (ci.pushRequiredClinits(ti)){
+    if (ci.initializeClass(ti)){
       env.repeatInvocation();
       return MJIEnv.NULL;
     }
@@ -247,7 +247,7 @@
       ti.pushFrame(frame);
       
       // check if we have to push clinits
-      ci.pushRequiredClinits(ti);
+      ci.initializeClass(ti);
       
       env.repeatInvocation();
       return MJIEnv.NULL;
@@ -495,7 +495,7 @@
     Instruction insn = ti.getPC();
     ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo( clsName);
     
-    if (ci.pushRequiredClinits(ti)){
+    if (ci.initializeClass(ti)){
       return null;
     } else {
       return ci;
@@ -505,7 +505,7 @@
   @MJI
   public void initialize0____V (MJIEnv env, int clsObjRef){
     ClassInfo ci = env.getReferredClassInfo( clsObjRef);
-    ci.pushRequiredClinits(ThreadInfo.currentThread);
+    ci.initializeClass(ThreadInfo.currentThread);
   }
 
   Set<ClassInfo> getInitializedInterfaces (MJIEnv env, ClassInfo ci){
@@ -514,7 +514,7 @@
 
     Set<ClassInfo> ifcs = ci.getAllInterfaceClassInfos();
     for (ClassInfo ciIfc : ifcs){
-    if (ciIfc.pushRequiredClinits(ti)){
+    if (ciIfc.initializeClass(ti)){
         return null;
       } 
     }
@@ -742,17 +742,12 @@
     if (ciEncl == null){
       return MJIEnv.NULL;
     }
-        
-    if (!ciEncl.isRegistered()){
-      ThreadInfo ti = env.getThreadInfo();
-      ciEncl.registerClass(ti);
-      
-      if (ciEncl.initializeClass(ti)){
-        env.repeatInvocation();
-        return 0;
-      }
+
+    if (ciEncl.initializeClass(env.getThreadInfo())) {
+      env.repeatInvocation();
+      return 0;
     }
-    
+
     return ciEncl.getClassObjectRef();
   }
 
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java	Wed Apr 15 22:40:21 2015 -0700
@@ -71,12 +71,6 @@
     StackFrame frame = ti.getTopFrame();
     ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("gov.nasa.jpf.ConsoleOutputStream");
 
-    // it's not really used, but it would be hack'ish to use a class whose
-    // super class hasn't been initialized yet
-    if (!ci.isRegistered()) {
-      ci.registerClass(ti);
-    }
-
     if (ci.initializeClass(ti)) {
       env.repeatInvocation();
       return MJIEnv.NULL;
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java	Wed Apr 15 22:40:21 2015 -0700
@@ -111,7 +111,7 @@
       }
       ti.pushFrame(frame);
        
-      ci.pushRequiredClinits(ti);
+      ci.initializeClass(ti);
       
       env.repeatInvocation();
       return MJIEnv.NULL;
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java	Wed Apr 15 22:40:21 2015 -0700
@@ -630,7 +630,7 @@
   static boolean isAvailable (MJIEnv env, FieldInfo fi, int ownerRef){
     if (fi.isStatic()){
       ClassInfo fci = fi.getClassInfo();
-      if (fci.pushRequiredClinits(env.getThreadInfo())){
+      if (fci.initializeClass(env.getThreadInfo())){
         env.repeatInvocation();
         return false;
       }
--- a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java	Tue Apr 14 15:01:25 2015 -0700
+++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java	Wed Apr 15 22:40:21 2015 -0700
@@ -476,7 +476,7 @@
       
       //--- check for and push required clinits
       if (miCallee.isStatic()){
-        calleeClass.pushRequiredClinits(ti);
+        calleeClass.initializeClass(ti);
       }
       
       return MJIEnv.NULL; // reexecute