Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / vm / ClassInfo.java
diff --git a/src/main/gov/nasa/jpf/vm/ClassInfo.java b/src/main/gov/nasa/jpf/vm/ClassInfo.java
new file mode 100644 (file)
index 0000000..d26abed
--- /dev/null
@@ -0,0 +1,2594 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+package gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.JPFListener;
+import gov.nasa.jpf.util.ImmutableList;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.LocationSpec;
+import gov.nasa.jpf.util.MethodSpec;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.OATHash;
+import gov.nasa.jpf.util.Source;
+
+import java.io.File;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.logging.Level;
+
+
+/**
+ * Describes the VM's view of a java class.  Contains descriptions of the
+ * static and dynamic fields, declaredMethods, and information relevant to the
+ * class.
+ * 
+ * Note that ClassInfos / classes have three different construction/initialization steps:
+ * (1) construction : recursively via ClassLoaderInfo.getResolvedClassInfo -> ClassFileContainer.createClassInfo
+ *     -> ClassInfo ctor -> resolveClass
+ *     this only creates the ClassInfo object, but it is not visible/usable from SUT code yet and hence not
+ *     observable from classLoaded() listeners
+ * (2) registration : create StaticElementInfo and add it to the respective ClassLoaderInfo statics, then create
+ *     the java.lang.Class companion object in the SUT
+ *     this makes the ClassInfo usable from SUT code
+ * (3) initialization : execute clinit (if the class has one)
+ * 
+ * Note that id/uniqueId are NOT set before registration takes place, and registration is not automatically performed since
+ * listeners/peers might create ClassInfos internally (e.g. for inspection), which should not be visible from the SUT or observable
+ * by other listeners.
+ * 
+ * Automatic registration from the ClassInfo ctors would require to pass a ThreadInfo context throughout the whole ClassLoaderInfo/
+ * ClassFileContainer/ClassInfo chain and could lead to false positives for sharedness based POR, which would record this
+ * thread as referencing even if this is a listener/peer internal request
+ */
+public class ClassInfo extends InfoObject implements Iterable<MethodInfo>, GenericSignatureHolder {
+
+  //--- ClassInfo states, in chronological order
+  // note the somewhat strange, decreasing values - >= 0 (=thread-id) means 
+  // we are in clinit
+  // ideally, we would have a separate RESOLVED state, but (a) this is somewhat
+  // orthogonal to REGISTERED, and - more importantly - (b) we need the
+  // superClass instance when initializing our Fields (instance field offsets).
+  // Doing the field initialization during resolveReferencedClass() seems awkward and
+  // error prone (there is not much you can do with an unresolved class then)
+  
+  // not registered or clinit'ed (but cached in loadedClasses)
+  public static final int UNINITIALIZED = -1;
+  // 'REGISTERED' simply means 'sei' is set (we have a StaticElementInfo)
+  // 'INITIALIZING' is any number >=0, which is the thread objRef that executes the clinit
+  public static final int INITIALIZED = -2;
+
+  protected static final String ID_FIELD = "nativeId"; 
+
+  protected static JPFLogger logger = JPF.getLogger("class");
+
+  protected static int nClassInfos; // for statistics
+  
+  protected static Config config;
+
+  /**
+   * ClassLoader that loaded this class.
+   */
+  protected static final ClassLoader thisClassLoader = ClassInfo.class.getClassLoader();  
+  
+  /**
+   * our abstract factory to createAndInitialize object and class fields
+   */
+  protected static FieldsFactory fieldsFactory;
+
+  
+  protected static final FieldInfo[] EMPTY_FIELDINFO_ARRAY = new FieldInfo[0];
+  protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+  protected static final String UNINITIALIZED_STRING = "UNINITIALIZED"; 
+  protected static final Map<String,MethodInfo> NO_METHODS = Collections.emptyMap();
+  protected static final Set<ClassInfo> NO_INTERFACES = new HashSet<ClassInfo>();
+  
+  /**
+   * support to auto-load listeners from annotations
+   */
+  protected static HashSet<String> autoloadAnnotations;
+  protected static HashSet<String> autoloaded;
+
+  /**
+   * Name of the class. e.g. "java.lang.String"
+   * NOTE - this is the expanded name for builtin types, e.g. "int", but NOT
+   * for arrays, which are for some reason in Ldot notation, e.g. "[Ljava.lang.String;" or "[I"
+   */
+  protected String name;
+  
+  /** type erased signature of the class. e.g. "Ljava/lang/String;" */
+  protected String signature;
+
+  /** Generic type signatures of the class as per para. 4.4.4 of the revised VM spec */
+  protected String genericSignature;
+
+  /** The classloader that defined (directly loaded) this class */
+  protected ClassLoaderInfo classLoader;
+  
+  // various class attributes
+  protected boolean      isClass = true;
+  protected boolean      isWeakReference = false;
+  protected boolean      isObjectClassInfo = false;
+  protected boolean      isStringClassInfo = false;
+  protected boolean      isThreadClassInfo = false;
+  protected boolean      isRefClassInfo = false;
+  protected boolean      isArray = false;
+  protected boolean      isEnum = false;
+  protected boolean      isReferenceArray = false;
+  protected boolean      isAbstract = false;
+  protected boolean      isBuiltin = false;
+
+  // that's ultimately where we keep the attributes
+  // <2do> this is currently quite redundant, but these are used in reflection
+  protected int modifiers;
+
+  protected MethodInfo   finalizer = null;
+
+  /** type based object attributes (for GC, partial order reduction and
+   * property checks)
+   */
+  protected int elementInfoAttrs = 0;
+
+  /**
+   * all our declared declaredMethods (we don't flatten, this is not
+   * a high-performance VM)
+   */
+  protected Map<String, MethodInfo> methods;
+
+  /**
+   * our instance fields.
+   * Note these are NOT flattened, idx.e. only contain the declared ones
+   */
+  protected FieldInfo[] iFields;
+
+  /** the storage size of instances of this class (stored as an int[]) */
+  protected int instanceDataSize;
+
+  /** where in the instance data array (int[]) do our declared fields start */
+  protected int instanceDataOffset;
+
+  /** total number of instance fields (flattened, not only declared ones) */
+  protected int nInstanceFields;
+
+  /**
+   * our static fields. Again, not flattened
+   */
+  protected FieldInfo[] sFields;
+
+  /** the storage size of static fields of this class (stored as an int[]) */
+  protected int staticDataSize;
+
+  /**
+   * we only set the superClassName upon creation, it is instantiated into
+   * a ClassInfo by resolveReferencedClass(), which is required to be called before
+   * we can createAndInitialize objects of this type
+   */
+  protected ClassInfo  superClass;
+  protected String superClassName;
+
+  protected String enclosingClassName;
+  protected String enclosingMethodName;
+
+  protected String[] innerClassNames = EMPTY_STRING_ARRAY;
+  protected BootstrapMethodInfo[] bootstrapMethods;
+    
+  /** direct ifcs implemented by this class */
+  protected String[] interfaceNames;
+
+  protected Set<ClassInfo> interfaces = new HashSet<ClassInfo>();
+  
+  /** cache of all interfaceNames (parent interfaceNames and interface parents) - lazy eval */
+  protected Set<ClassInfo> allInterfaces;
+  
+  /** Name of the package. */
+  protected String packageName;
+
+  /** this is only set if the classfile has a SourceFile class attribute */
+  protected String sourceFileName;
+
+  /** 
+   * Uniform resource locater for the class file. NOTE: since for builtin classes
+   * there is no class file assigned is set to the typeName 
+   */ 
+  protected String classFileUrl;
+
+  /** from where the corresponding classfile was loaded (if this is not a builtin) */
+  protected gov.nasa.jpf.vm.ClassFileContainer container;
+
+  
+  /**
+   *  a search global numeric id that is only unique within this ClassLoader namespace. Ids are
+   *  computed by the ClassLoaderInfo/Statics implementation during ClassInfo registration
+   */
+  protected int  id = -1;
+
+  /**
+   * A search global unique id associate with this class, which is comprised of the classLoader id
+   * and the (loader-specific) ClassInfo id. This is just a quick way to do search global checks for equality
+   * 
+   * NOTE - since this is based on the classloader-specific id, it can't be used before the ClassInfo is registered
+   */
+  protected long uniqueId = -1;
+
+  /**
+   * this is the object we use to enter declaredMethods in the underlying VM
+   * (it replaces Reflection)
+   */
+  protected NativePeer nativePeer;
+
+  /** Source file associated with the class.*/
+  protected Source source;
+
+  protected boolean enableAssertions;
+
+  /** actions to be taken when an object of this type is gc'ed */
+  protected ImmutableList<ReleaseAction> releaseActions; 
+          
+  
+  static boolean init (Config config) {
+
+    ClassInfo.config = config;
+    
+    setSourceRoots(config);
+    //buildBCELModelClassPath(config);
+
+    fieldsFactory = config.getEssentialInstance("vm.fields_factory.class",
+                                                FieldsFactory.class);
+
+    autoloadAnnotations = config.getNonEmptyStringSet("listener.autoload");
+    if (autoloadAnnotations != null) {
+      autoloaded = new HashSet<String>();
+
+      if (logger.isLoggable(Level.INFO)) {
+        for (String s : autoloadAnnotations){
+          logger.info("watching for autoload annotation @" + s);
+        }
+      }
+    }
+
+    return true;
+  }
+
+  public static boolean isObjectClassInfo (ClassInfo ci){
+    return ci.isObjectClassInfo();
+  }
+
+  public static boolean isStringClassInfo (ClassInfo ci){
+    return ci.isStringClassInfo();
+  }
+
+  
+   //--- initialization interface towards parsers (which might reside in other packages)
+    
+  protected void setClass(String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
+    String parsedName = Types.getClassNameFromTypeName(clsName);
+
+    if (name != null && !name.equals(parsedName)){
+      throw new ClassParseException("wrong class name (expected: " + name + ", found: " + parsedName + ')');
+    }
+    name = parsedName;
+    
+    // the enclosingClassName is set on demand since it requires loading enclosing class candidates
+    // to verify their innerClass attributes
+
+    int i = name.lastIndexOf('.');
+    packageName = (i > 0) ? name.substring(0, i) : "";
+
+    modifiers = flags;
+    
+    // annotations are interfaces too (not exposed by Modifier)
+    isClass = ((flags & Modifier.INTERFACE) == 0);
+
+    superClassName = superClsName;
+  }
+
+  public void setInnerClassNames(String[] clsNames) {
+    innerClassNames = clsNames;
+  }
+
+  public void setEnclosingClass (String clsName) {
+    enclosingClassName = clsName;
+  }
+  
+  public void setEnclosingMethod (String mthName){
+    enclosingMethodName = mthName;    
+  }
+
+  public void setInterfaceNames(String[] ifcNames) {
+    interfaceNames = ifcNames;
+  }
+  
+  public void setSourceFile (String fileName){
+    // prepend if we already know the package
+    if (packageName.length() > 0) {
+      // Source will take care of proper separator chars later
+      sourceFileName = packageName.replace('.', '/') + '/' + fileName;
+    } else {
+      sourceFileName = fileName;
+    }
+  }
+
+  public void setFields(FieldInfo[] fields) {
+    if (fields == null){
+      iFields = EMPTY_FIELDINFO_ARRAY;
+      sFields = EMPTY_FIELDINFO_ARRAY;
+      
+    } else { // there are fields, we have to tell them apart
+      int nInstance = 0, nStatic = 0;
+      for (int i = 0; i < fields.length; i++) {
+        if (fields[i].isStatic()) {
+          nStatic++;
+        } else {
+          nInstance++;
+        }
+      }
+
+      FieldInfo[] instanceFields = (nInstance > 0) ? new FieldInfo[nInstance] : EMPTY_FIELDINFO_ARRAY;
+      FieldInfo[] staticFields = (nStatic > 0) ? new FieldInfo[nStatic] : EMPTY_FIELDINFO_ARRAY;
+
+      int iInstance = 0;
+      int iStatic = 0;
+      for (int i = 0; i < fields.length; i++) {
+        FieldInfo fi = fields[i];
+
+        if (fi.isStatic()) {
+          staticFields[iStatic++] = fi;
+        } else {
+          instanceFields[iInstance++] = fi;
+        }
+        
+        processJPFAnnotations(fi);
+      }
+
+      iFields = instanceFields;
+      sFields = staticFields;
+
+      // we can't link the fields yet because we need the superclasses to be resolved
+    }
+  }
+
+  protected void setMethod (MethodInfo mi){
+    mi.linkToClass(this);
+    methods.put( mi.getUniqueName(), mi);
+    processJPFAnnotations(mi);
+  }
+  
+  public void setMethods (MethodInfo[] newMethods) {
+    if (newMethods != null && newMethods.length > 0) {
+      methods = new LinkedHashMap<String, MethodInfo>();
+
+      for (int i = 0; i < newMethods.length; i++) {
+        setMethod( newMethods[i]);
+      }
+    }
+  }
+  protected void processJPFAttrAnnotation(InfoObject infoObj){
+    AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.JPFAttribute");
+    if (ai != null){
+      String[] attrTypes = ai.getValueAsStringArray();
+      if (attrTypes != null){
+        ClassLoader loader = config.getClassLoader();
+
+        for (String clsName : attrTypes){
+          try {
+            Class<?> attrCls = loader.loadClass(clsName);
+            Object attr = attrCls.newInstance(); // needs to have a default ctor
+            infoObj.addAttr(attr);
+            
+          } catch (ClassNotFoundException cnfx){
+            logger.warning("attribute class not found: " + clsName);
+            
+          } catch (IllegalAccessException iax){
+            logger.warning("attribute class has no public default ctor: " + clsName);            
+            
+          } catch (InstantiationException ix){
+            logger.warning("attribute class has no default ctor: " + clsName);            
+          }
+        }
+      }
+    }    
+  }
+
+  protected void processNoJPFExecutionAnnotation(InfoObject infoObj) {
+    AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.NoJPFExecution");
+    if (ai != null) {
+      infoObj.addAttr(NoJPFExec.SINGLETON);
+    }
+  }
+
+  protected void processJPFAnnotations(InfoObject infoObj) {
+    processJPFAttrAnnotation(infoObj);
+    processNoJPFExecutionAnnotation(infoObj);
+  }
+
+    public AnnotationInfo getResolvedAnnotationInfo (String typeName){
+    return classLoader.getResolvedAnnotationInfo( typeName);
+  }
+  
+  @Override
+  public void setAnnotations(AnnotationInfo[] annotations) {
+    this.annotations = annotations;
+  }
+  
+  //--- end initialization interface
+  //--- the overridden annotation accessors (we need these because of inherited superclass annotations)
+  // note that we don't flatten annotations anymore, assuming the prevalent query will be getAnnotation(name)
+  
+  @Override
+  public boolean hasAnnotations(){
+    if (annotations.length > 0){
+      return true;
+    }
+    
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int j=0; j<a.length; j++){
+        if (a[j].isInherited()){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  /**
+   * return all annotations, which includes the ones inherited from our superclasses
+   * NOTE - this is not very efficient
+   */
+  @Override
+  public AnnotationInfo[] getAnnotations() {
+    int nAnnotations = annotations.length;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        if (a[i].isInherited()){
+          nAnnotations++;
+        }
+      }
+    }
+    
+    AnnotationInfo[] allAnnotations = new AnnotationInfo[nAnnotations];
+    System.arraycopy(annotations, 0, allAnnotations, 0, annotations.length);
+    int idx=annotations.length;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        if (a[i].isInherited()){
+          allAnnotations[idx++] = a[i];
+        }
+      }
+    }
+    
+    return allAnnotations;
+  }
+    
+  @Override
+  public AnnotationInfo getAnnotation (String annotationName){
+    AnnotationInfo[] a = annotations;
+    for (int i=0; i<a.length; i++){
+      if (a[i].getName().equals(annotationName)){
+        return a[i];
+      }
+    }
+    
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        AnnotationInfo ai = a[i];
+        if (ai.getName().equals(annotationName) && ai.isInherited()){
+          return ai;
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  protected ClassInfo (String name, ClassLoaderInfo cli, String classFileUrl){
+    nClassInfos++;
+    
+    this.name = name;
+    this.classLoader = cli;
+    this.classFileUrl = classFileUrl;
+    
+    this.methods = NO_METHODS;  // yet
+
+    // rest has to be initialized by concrete ctor, which should call resolveAndLink(parser)
+  }
+  
+  /**
+   * the initialization part that has to happen once we have super, fields, methods and annotations
+   * NOTE - this has to be called by concrete ctors after parsing class files
+   */
+  protected void resolveAndLink () throws ClassParseException {
+    
+    //--- these might get streamlined
+    isStringClassInfo = isStringClassInfo0();
+    isThreadClassInfo = isThreadClassInfo0();
+    isObjectClassInfo = isObjectClassInfo0();
+    isRefClassInfo = isRefClassInfo0();
+   // isWeakReference = isWeakReference0();
+    isAbstract = (modifiers & Modifier.ABSTRACT) != 0;
+   // isEnum = isEnum0();
+    
+    finalizer = getFinalizer0();
+
+    resolveClass(); // takes care of super classes and interfaces
+
+    // Used to enter native methods (in the host VM).
+    // This needs to be initialized AFTER we get our  MethodInfos, since it does a reverse lookup to determine which
+    // ones are handled by the peer (by means of setting MethodInfo attributes)
+    nativePeer = loadNativePeer();
+    checkUnresolvedNativeMethods();
+
+    linkFields(); // computes field offsets
+    
+    setAssertionStatus();
+    processJPFConfigAnnotation();
+    processJPFAnnotations(this);
+    loadAnnotationListeners();    
+  }
+  
+  protected ClassInfo(){
+    nClassInfos++;
+    
+    // for explicit subclass initialization
+  }
+  
+  /**
+   * ClassInfo ctor used for builtin types (arrays and primitive types)
+   * idx.e. classes we don't have class files for
+   */
+  protected ClassInfo (String builtinClassName, ClassLoaderInfo classLoader) {
+    nClassInfos++;
+
+    this.classLoader = classLoader;
+
+    isArray = (builtinClassName.charAt(0) == '[');
+    isReferenceArray = isArray && (builtinClassName.endsWith(";") || builtinClassName.charAt(1) == '[');
+    isBuiltin = true;
+
+    name = builtinClassName;
+
+    logger.log(Level.FINE, "generating builtin class: %1$s", name);
+
+    packageName = ""; // builtin classes don't reside in java.lang !
+    sourceFileName = null;
+    source = null;
+    genericSignature = "";
+
+    // no fields
+    iFields = EMPTY_FIELDINFO_ARRAY;
+    sFields = EMPTY_FIELDINFO_ARRAY;
+
+    if (isArray) {
+      if(classLoader.isSystemClassLoader()) {
+        superClass = ((SystemClassLoaderInfo)classLoader).getObjectClassInfo();
+      } else {
+        superClass = ClassLoaderInfo.getCurrentSystemClassLoader().getObjectClassInfo();
+      }
+      interfaceNames = loadArrayInterfaces();
+      methods = loadArrayMethods();
+    } else {
+      superClass = null; // strange, but true, a 'no object' class
+      interfaceNames = loadBuiltinInterfaces(name);
+      methods = loadBuiltinMethods(name);
+    }
+
+    enableAssertions = true; // doesn't really matter - no code associated
+
+    classFileUrl = name;
+    
+    // no fields or declaredMethods, so we don't have to link/resolve anything
+  }
+  
+  public static int getNumberOfLoadedClasses(){
+    return nClassInfos;
+  }
+  
+  //--- the VM type specific methods
+  // <2do> those should be abstract
+  
+  protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setDirectCallCode (MethodInfo miCallee, MethodInfo miStub){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setNativeCallCode (NativeMethodInfo miNative){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
+    // to be overridden by VM specific class
+  }
+  
+  /**
+   * createAndInitialize a fully synthetic implementation of an Annotation proxy
+   */
+  protected ClassInfo (ClassInfo annotationCls, String name, ClassLoaderInfo classLoader, String url) {
+    this.classLoader = classLoader;
+    
+    this.name = name;
+    isClass = true;
+
+    //superClass = objectClassInfo;
+    superClass = ClassLoaderInfo.getSystemResolvedClassInfo("gov.nasa.jpf.AnnotationProxyBase");
+
+    interfaceNames = new String[]{ annotationCls.name };    
+    packageName = annotationCls.packageName;
+    sourceFileName = annotationCls.sourceFileName;
+    genericSignature = annotationCls.genericSignature;
+
+    sFields = new FieldInfo[0]; // none
+    staticDataSize = 0;
+
+    methods = new HashMap<String, MethodInfo>();
+    iFields = new FieldInfo[annotationCls.methods.size()];
+    nInstanceFields = iFields.length;
+
+    // all accessor declaredMethods of ours make it into iField/method combinations
+    int idx = 0;
+    int off = 0;  // no super class
+    for (MethodInfo mi : annotationCls.getDeclaredMethodInfos()) {
+      String mname = mi.getName();
+      String mtype = mi.getReturnType();
+      String genericSignature = mi.getGenericSignature();
+
+      // create and initialize an instance field for it
+      FieldInfo fi = FieldInfo.create(mname, mtype, 0);
+      fi.linkToClass(this, idx, off);
+      fi.setGenericSignature(genericSignature);
+      iFields[idx++] = fi;
+      off += fi.getStorageSize();
+
+      MethodInfo pmi = new MethodInfo(this, mname, mi.getSignature(), Modifier.PUBLIC, 1, 2);
+      pmi.setGenericSignature(genericSignature);
+      
+      setAnnotationValueGetterCode( pmi, fi);
+      methods.put(pmi.getUniqueName(), pmi);
+    }
+
+    instanceDataSize = computeInstanceDataSize();
+    instanceDataOffset = 0;
+
+    classFileUrl = url;
+    linkFields();
+  }
+  
+  
+  //used to create synthetic classes that implement functional interfaces
+  protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
+   return null;
+ }
+ protected ClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String[] fieldTypesName) {
+   ClassInfo enclosingClass = bootstrapMethod.enclosingClass;
+   this.classLoader = enclosingClass.classLoader;
+
+   this.name = name;
+   isClass = true;
+
+   superClassName = "java.lang.Object";
+
+   interfaceNames = new String[]{ funcInterface.name };    
+   packageName = enclosingClass.getPackageName();
+
+   // creating fields used to capture free variables
+   int n = fieldTypesName.length;
+   
+   iFields = new FieldInfo[n];
+   nInstanceFields = n;
+   
+   sFields = new FieldInfo[0];
+   staticDataSize = 0;
+   
+   int idx = 0;
+   int off = 0;  // no super class
+   
+   int i = 0;
+   for(String type: fieldTypesName) {
+     FieldInfo fi = FieldInfo.create("arg" + i++, type, 0);
+     fi.linkToClass(this, idx, off);
+     iFields[idx++] = fi;
+     off += fi.getStorageSize();
+   }
+   
+   linkFields();
+ }
+  
+  // since id and hence uniqueId are not set before this class is registered, we can't use them
+  
+  @Override
+  public int hashCode() {
+    return OATHash.hash(name.hashCode(), classLoader.hashCode());
+  }
+  
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof ClassInfo) {
+      ClassInfo other = (ClassInfo)o;
+      if (classLoader == other.classLoader) {
+        // beware of ClassInfos that are not registered yet - in this case we have to equals names
+        if (name.equals(other.name)) {
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+
+  protected String computeSourceFileName(){
+    return name.replace('.', '/') + ".java";
+  }
+
+  protected void checkUnresolvedNativeMethods(){
+    for (MethodInfo mi : methods.values()){
+      if (mi.isUnresolvedNativeMethod()){
+        NativeMethodInfo nmi = new NativeMethodInfo(mi, null, nativePeer);
+        nmi.replace(mi);
+      }
+    }
+  }
+
+  protected void processJPFConfigAnnotation() {
+    AnnotationInfo ai = getAnnotation("gov.nasa.jpf.annotation.JPFConfig");
+    if (ai != null) {
+      for (String s : ai.getValueAsStringArray()) {
+        config.parse(s);
+      }
+    }
+  }
+
+  protected void loadAnnotationListeners () {
+    if (autoloadAnnotations != null) {
+      autoloadListeners(annotations); // class annotations
+
+      for (int i=0; i<sFields.length; i++) {
+        autoloadListeners(sFields[i].getAnnotations());
+      }
+
+      for (int i=0; i<iFields.length; i++) {
+        autoloadListeners(iFields[i].getAnnotations());
+      }
+
+      // method annotations are checked during method loading
+      // (to avoid extra iteration)
+    }
+  }
+
+  void autoloadListeners(AnnotationInfo[] annos) {
+    if ((annos != null) && (autoloadAnnotations != null)) {
+      for (AnnotationInfo ai : annos) {
+        String aName = ai.getName();
+        if (autoloadAnnotations.contains(aName)) {
+          if (!autoloaded.contains(aName)) {
+            autoloaded.add(aName);
+            String key = "listener." + aName;
+            String defClsName = aName + "Checker";
+            try {
+              JPFListener listener = config.getInstance(key, JPFListener.class, defClsName);
+              
+              JPF jpf = VM.getVM().getJPF(); // <2do> that's a BAD access path
+              jpf.addUniqueTypeListener(listener);
+
+              if (logger.isLoggable(Level.INFO)){
+                logger.info("autoload annotation listener: @", aName, " => ", listener.getClass().getName());
+              }
+
+            } catch (JPFConfigException cx) {
+              logger.warning("no autoload listener class for annotation " + aName +
+                             " : " + cx.getMessage());
+              autoloadAnnotations.remove(aName);
+            }
+          }
+        }
+      }
+
+      if (autoloadAnnotations.isEmpty()) {
+        autoloadAnnotations = null;
+      }
+    }
+  }
+
+  protected NativePeer loadNativePeer(){
+    return NativePeer.getNativePeer(this);
+  }
+  
+  /**
+   * Returns the class loader that 
+   */
+  public ClassLoaderInfo getClassLoaderInfo() {
+    return classLoader;
+  }
+
+  /**
+   * the container this is stored in
+   */
+  public Statics getStatics() {
+    return classLoader.getStatics();
+  }
+  
+  /**
+   * required by InfoObject interface
+   */
+  public ClassInfo getClassInfo() {
+    return this;
+  }
+
+  protected void setAssertionStatus() {
+    if(isInitialized()) {
+      return;
+    } else {
+      enableAssertions = classLoader.desiredAssertionStatus(name);
+    }
+  }
+
+  boolean getAssertionStatus () {
+    return enableAssertions;
+  }
+
+  public boolean desiredAssertionStatus() {
+    return classLoader.desiredAssertionStatus(name);
+  }
+
+  @Override
+  public String getGenericSignature() {
+    return genericSignature;
+  }
+
+  @Override
+  public void setGenericSignature(String sig){
+    genericSignature = sig;
+  }
+  
+  public boolean isArray () {
+    return isArray;
+  }
+
+  public boolean isEnum () {
+    return isEnum;
+  }
+
+  public boolean isAbstract() {
+    return isAbstract;
+  }
+
+  public boolean isBuiltin(){
+    return isBuiltin;
+  }
+  
+  public boolean isInterface() {
+    return ((modifiers & Modifier.INTERFACE) != 0);
+  }
+
+  public boolean isReferenceArray () {
+    return isReferenceArray;
+  }
+
+  public boolean isObjectClassInfo() {
+    return isObjectClassInfo;
+  }
+
+  public boolean isStringClassInfo() {
+    return isStringClassInfo;
+  }
+
+  public boolean isThreadClassInfo() {
+    return isThreadClassInfo;
+  }
+
+  protected void checkNoClinitInitialization(){
+    if (!isInitialized()){
+      ThreadInfo ti = ThreadInfo.getCurrentThread();
+      registerClass(ti);
+      setInitialized(); // we might want to check if there is a clinit
+    }
+  }
+  
+  protected ClassInfo createAnnotationProxy (String proxyName){
+    // to be overridden by VM specific ClassInfos
+    return null;
+  }
+  
+  public ClassInfo getAnnotationProxy (){
+    // <2do> test if this is a annotation ClassInfo
+    
+    checkNoClinitInitialization(); // annotation classes don't have clinits
+    
+    ClassInfo ciProxy = classLoader.getResolvedAnnotationProxy(this);
+    ciProxy.checkNoClinitInitialization();
+    
+    return ciProxy;
+  }
+  
+/**
+  public static ClassInfo getAnnotationProxy (ClassInfo ciAnnotation){
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+
+    // make sure the annotationCls is initialized (no code there)
+    if (!ciAnnotation.isInitialized()) {
+      ciAnnotation.registerClass(ti);
+      ciAnnotation.setInitialized(); // no clinit
+    }
+
+    String url = computeProxyUrl(ciAnnotation);
+    ClassInfo ci = null; // getOriginalClassInfo(url);
+
+    if (ci == null){
+      String cname = ciAnnotation.getName() + "$Proxy";
+      ci = new ClassInfo(ciAnnotation, cname, ciAnnotation.classLoader, url);
+      ciAnnotation.classLoader.addResolvedClass(ci);
+      if (!ci.isInitialized()){
+        ci.registerClass(ti);
+        ci.setInitialized();
+      }
+    }
+
+    return ci;
+  }
+**/
+
+  public boolean areAssertionsEnabled() {
+    return enableAssertions;
+  }
+
+  public boolean hasInstanceFields () {
+    return (instanceDataSize > 0);
+  }
+
+  public ElementInfo getClassObject(){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei != null){
+      int objref = sei.getClassObjectRef();
+      return VM.getVM().getElementInfo(objref);
+    }
+
+    return null;
+  }
+  
+  public ElementInfo getModifiableClassObject(){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei != null){
+      int objref = sei.getClassObjectRef();
+      return VM.getVM().getModifiableElementInfo(objref);
+    }
+
+    return null;
+  }
+  
+
+  public int getClassObjectRef () {
+    StaticElementInfo sei = getStaticElementInfo();    
+    return (sei != null) ? sei.getClassObjectRef() : MJIEnv.NULL;
+  }
+
+  public gov.nasa.jpf.vm.ClassFileContainer getContainer(){
+    return container;
+  }
+  
+  public String getClassFileUrl (){
+    return classFileUrl;
+  }
+
+  //--- type based object release actions
+  
+  public boolean hasReleaseAction (ReleaseAction action){
+    return (releaseActions != null) && releaseActions.contains(action);
+  }
+  
+  /**
+   * NOTE - this can only be set *before* subclasses are loaded (e.g. from classLoaded() notification) 
+   */
+  public void addReleaseAction (ReleaseAction action){
+    // flattened in ctor to super releaseActions
+    releaseActions = new ImmutableList<ReleaseAction>( action, releaseActions);
+  }
+  
+  /**
+   * recursively process release actions registered for this type or any of
+   * its super types (only classes). The releaseAction list is flattened during
+   * ClassInfo initialization, to reduce runtime overhead during GC sweep
+   */
+  public void processReleaseActions (ElementInfo ei){
+    if (superClass != null){
+      superClass.processReleaseActions(ei);
+    }
+    
+    if (releaseActions != null) {
+      for (ReleaseAction action : releaseActions) {
+        action.release(ei);
+      }
+    }
+  }
+  
+  public int getModifiers() {
+    return modifiers;
+  }
+
+  /**
+   * Note that 'uniqueName' is the name plus the argument type part of the
+   * signature, idx.e. everything that's relevant for overloading
+   * (besides saving some const space, we also ease reverse lookup
+   * of natives that way).
+   * Note also that we don't have to make any difference between
+   * class and instance declaredMethods, because that just matters in the
+   * INVOKExx instruction, when looking up the relevant ClassInfo to start
+   * searching in (either by means of the object type, or by means of the
+   * constpool classname entry).
+   */
+  public MethodInfo getMethod (String uniqueName, boolean isRecursiveLookup) {
+    MethodInfo mi = methods.get(uniqueName);
+
+    if ((mi == null) && isRecursiveLookup && (superClass != null)) {
+      mi = superClass.getMethod(uniqueName, true);
+    }
+
+    return mi;
+  }
+
+  /**
+   * if we don't know the return type
+   * signature is in paren/dot notation
+   */
+  public MethodInfo getMethod (String name, String signature, boolean isRecursiveLookup) {
+    MethodInfo mi = null;
+    String matchName = name + signature;
+
+    for (Map.Entry<String, MethodInfo>e : methods.entrySet()) {
+      if (e.getKey().startsWith(matchName)){
+        mi = e.getValue();
+        break;
+      }
+    }
+
+    if ((mi == null) && isRecursiveLookup && (superClass != null)) {
+      mi = superClass.getMethod(name, signature, true);
+    }
+
+    return mi;
+  }
+
+  
+  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){
+            // 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;
+          }
+        }
+      }
+    }
+    
+    return mi;
+  }
+  
+  /**
+   * 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");
+    
+    for(MethodInfo mi: this.methods.values()) {
+      if(mi.isAbstract() && objCi.getMethod(mi.getUniqueName(), false)==null) {
+        return mi;
+      }
+    }
+    
+    for (ClassInfo ifc : this.interfaces){
+      MethodInfo mi = ifc.getInterfaceAbstractMethod();
+      if(mi!=null) {
+        return mi;
+      }
+    }
+    
+    return null;
+  }
+
+  /**
+   * method lookup for use by reflection methods (java.lang.Class.getXMethod)
+   * 
+   * note this doesn't specify the return type, which means covariant return 
+   * types are not allowed in reflection lookup.
+   * 
+   * note also this includes interface methods, but only after the inheritance
+   * hierarchy has been searched
+   */
+  public MethodInfo getReflectionMethod (String fullName, boolean isRecursiveLookup) {
+        
+    // first look for methods within the class hierarchy
+    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
+      for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
+        String name = e.getKey();
+        if (name.startsWith(fullName)) {
+          return e.getValue();
+        }
+      }
+      if (!isRecursiveLookup){
+        return null;
+      }
+    }
+
+    // this is the recursive case - if none found, look for interface methods
+    for (ClassInfo ci : getAllInterfaces() ){
+      for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
+        String name = e.getKey();
+        if (name.startsWith(fullName)) {
+          return e.getValue();
+        }
+      }      
+    }    
+
+    return null;
+  }
+  
+  /**
+   * iterate over all declaredMethods of this class (and it's superclasses), until
+   * the provided MethodLocator tells us it's done
+   */
+  public void matchMethods (MethodLocator loc) {
+    for (MethodInfo mi : methods.values()) {
+      if (loc.match(mi)) {
+        return;
+      }
+    }
+    if (superClass != null) {
+      superClass.matchMethods(loc);
+    }
+  }
+
+  /**
+   * iterate over all declaredMethods declared in this class, until the provided
+   * MethodLocator tells us it's done
+   */
+  public void matchDeclaredMethods (MethodLocator loc) {
+    for (MethodInfo mi : methods.values()) {
+      if (loc.match(mi)) {
+        return;
+      }
+    }
+  }
+
+  @Override
+  public Iterator<MethodInfo> iterator() {
+    return new Iterator<MethodInfo>() {
+      ClassInfo ci = ClassInfo.this;
+      Iterator<MethodInfo> it = ci.methods.values().iterator();
+
+      @Override
+       public boolean hasNext() {
+        if (it.hasNext()) {
+          return true;
+        } else {
+          if (ci.superClass != null) {
+            ci = ci.superClass;
+            it = ci.methods.values().iterator();
+            return it.hasNext();
+          } else {
+            return false;
+          }
+        }
+      }
+
+      @Override
+       public MethodInfo next() {
+        if (hasNext()) {
+          return it.next();
+        } else {
+          throw new NoSuchElementException();
+        }
+      }
+
+      @Override
+       public void remove() {
+        // not supported
+        throw new UnsupportedOperationException("can't remove methods");
+      }
+    };
+  }
+  
+  public Iterator<MethodInfo> declaredMethodIterator() {
+    return methods.values().iterator();
+  }
+
+  /**
+   * Search up the class hierarchy to find a static field
+   * @param fName name of field
+   * @return null if field name not found (not declared)
+   */
+  public FieldInfo getStaticField (String fName) {
+    FieldInfo fi;
+    ClassInfo c = this;
+
+    while (c != null) {
+      fi = c.getDeclaredStaticField(fName);
+      if (fi != null) {
+        return fi;
+      }
+      c = c.superClass;
+    }
+
+    //interfaceNames can have static fields too
+    // <2do> why would that not be already resolved here ?
+    for (ClassInfo ci : getAllInterfaces()) {
+      fi = ci.getDeclaredStaticField(fName);
+      if (fi != null) {
+        return fi;
+      }
+    }
+
+    return null;
+  }
+
+  public Object getStaticFieldValueObject (String id){
+    ClassInfo c = this;
+    Object v;
+
+    while (c != null){
+      ElementInfo sei = c.getStaticElementInfo();
+      v = sei.getFieldValueObject(id);
+      if (v != null){
+        return v;
+      }
+      c = c.getSuperClass();
+    }
+
+    return null;
+  }
+
+  public FieldInfo[] getDeclaredStaticFields() {
+    return sFields;
+  }
+
+  public FieldInfo[] getDeclaredInstanceFields() {
+    return iFields;
+  }
+
+  /**
+   * FieldInfo lookup in the static fields that are declared in this class
+   * <2do> pcm - should employ a map at some point, but it's usually not that
+   * important since we can cash the returned FieldInfo in the PUT/GET_STATIC insns
+   */
+  public FieldInfo getDeclaredStaticField (String fName) {
+    for (int i=0; i<sFields.length; i++) {
+      if (sFields[i].getName().equals(fName)) return sFields[i];
+    }
+
+    return null;
+  }
+
+  /**
+   * base relative FieldInfo lookup - the workhorse
+   * <2do> again, should eventually use Maps
+   * @param fName the field name
+   */
+  public FieldInfo getInstanceField (String fName) {
+    FieldInfo fi;
+    ClassInfo c = this;
+
+    while (c != null) {
+      fi = c.getDeclaredInstanceField(fName);
+      if (fi != null) return fi;
+      c = c.superClass;
+    }
+
+    return null;
+  }
+
+  /**
+   * FieldInfo lookup in the fields that are declared in this class
+   */
+  public FieldInfo getDeclaredInstanceField (String fName) {
+    for (int i=0; i<iFields.length; i++) {
+      if (iFields[i].getName().equals(fName)) return iFields[i];
+    }
+
+    return null;
+  }
+  
+  public String getSignature() {
+    if (signature == null) {
+      signature = Types.getTypeSignature(name, false);
+    }
+    
+    return signature;     
+  }
+
+  /**
+   * Returns the name of the class.  e.g. "java.lang.String".  similar to
+   * java.lang.Class.getName().
+   */
+  public String getName () {
+    return name;
+  }
+
+  public String getSimpleName () {
+    int i;
+    String enclosingClassName = getEnclosingClassName();
+    
+    if(enclosingClassName!=null){
+      i = enclosingClassName.length();      
+    } else{
+      i = name.lastIndexOf('.');
+    }
+    
+    return name.substring(i+1);
+  }
+
+  public String getPackageName () {
+    return packageName;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public long getUniqueId() {
+    return uniqueId;
+  }
+
+  public int getFieldAttrs (int fieldIndex) {
+    fieldIndex = 0; // Get rid of IDE warning
+     
+    return 0;
+  }
+
+  public void setElementInfoAttrs (int attrs){
+    elementInfoAttrs = attrs;
+  }
+
+  public void addElementInfoAttr (int attr){
+    elementInfoAttrs |= attr;
+  }
+
+  public int getElementInfoAttrs () {
+    return elementInfoAttrs;
+  }
+
+  public Source getSource () {
+    if (source == null) {
+      source = loadSource();
+    }
+
+    return source;
+  }
+
+  public String getSourceFileName () {
+    return sourceFileName;
+  }
+
+  /**
+   * Returns the information about a static field.
+   */
+  public FieldInfo getStaticField (int index) {
+    return sFields[index];
+  }
+
+  /**
+   * Returns the name of a static field.
+   */
+  public String getStaticFieldName (int index) {
+    return getStaticField(index).getName();
+  }
+
+  /**
+   * Checks if a static method call is deterministic, but only for
+   * abtraction based determinism, due to Bandera.choose() calls
+   */
+  public boolean isStaticMethodAbstractionDeterministic (ThreadInfo th,
+                                                         MethodInfo mi) {
+    //    Reflection r = reflection.instantiate();
+    //    return r.isStaticMethodAbstractionDeterministic(th, mi);
+    // <2do> - still has to be implemented
+     
+    th = null;  // Get rid of IDE warning
+    mi = null;
+     
+    return true;
+  }
+
+  public String getSuperClassName() {
+    return superClassName;
+  }
+
+  /**
+   * Return the super class.
+   */
+  public ClassInfo getSuperClass () {
+    return superClass;
+  }
+
+  /**
+   * return the ClassInfo for the provided superclass name. If this is equals
+   * to ourself, return this (a little bit strange if we hit it in the first place)
+   */
+  public ClassInfo getSuperClass (String clsName) {
+    if (clsName.equals(name)) return this;
+
+    if (superClass != null) {
+      return superClass.getSuperClass(clsName);
+    } else {
+      return null;
+    }
+  }
+
+  public int getNumberOfSuperClasses(){
+    int n = 0;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      n++;
+    }
+    return n;
+  }
+  
+  /**
+   * beware - this loads (but not yet registers) the enclosing class
+   */
+  public String getEnclosingClassName(){
+    return enclosingClassName;
+  }
+  
+  /**
+   * beware - this loads (but not yet registers) the enclosing class
+   */
+  public ClassInfo getEnclosingClassInfo() {
+    String enclName = getEnclosingClassName();
+    return (enclName == null ? null : classLoader.getResolvedClassInfo(enclName)); // ? is this supposed to use the same classloader
+  }
+
+  public String getEnclosingMethodName(){
+    return enclosingMethodName;
+  }
+
+  /**
+   * same restriction as getEnclosingClassInfo() - might not be registered/initialized
+   */
+  public MethodInfo getEnclosingMethodInfo(){
+    MethodInfo miEncl = null;
+    
+    if (enclosingMethodName != null){
+      ClassInfo ciIncl = getEnclosingClassInfo();
+      miEncl = ciIncl.getMethod( enclosingMethodName, false);
+    }
+    
+    return miEncl;
+  }
+  
+  /**
+   * Returns true if the class is a system class.
+   */
+  public boolean isSystemClass () {
+    return name.startsWith("java.") || name.startsWith("javax.");
+  }
+
+  /**
+   * <2do> that's stupid - we should use subclasses for builtin and box types
+   */
+  public boolean isBoxClass () {
+    if (name.startsWith("java.lang.")) {
+      String rawType = name.substring(10);
+      if (rawType.startsWith("Boolean") ||
+          rawType.startsWith("Byte") ||
+          rawType.startsWith("Character") ||
+          rawType.startsWith("Integer") ||
+          rawType.startsWith("Float") ||
+          rawType.startsWith("Long") ||
+          rawType.startsWith("Double")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns the type of a class.
+   */
+  public String getType () {
+    if (!isArray) {
+      return "L" + name.replace('.', '/') + ";";
+    } else {
+      return name;
+    }
+  }
+
+  /**
+   * is this a (subclass of) WeakReference? this must be efficient, since it's
+   * called in the mark phase on all live objects
+   */
+  public boolean isWeakReference () {
+    return isWeakReference;
+  }
+
+  /**
+   * note this only returns true is this is really the java.lang.ref.Reference classInfo
+   */
+  public boolean isReferenceClassInfo () {
+    return isRefClassInfo;
+  }
+
+  /**
+   * whether this refers to a primitive type.
+   */
+  public boolean isPrimitive() {
+    return superClass == null && !isObjectClassInfo();
+  }
+
+
+  boolean hasRefField (int ref, Fields fv) {
+    ClassInfo c = this;
+
+    do {
+      FieldInfo[] fia = c.iFields;
+      for (int i=0; i<fia.length; i++) {
+        FieldInfo fi = c.iFields[i];
+        if (fi.isReference() && (fv.getIntValue( fi.getStorageOffset()) == ref)) return true;
+      }
+      c = c.superClass;
+    } while (c != null);
+
+    return false;
+  }
+
+  boolean hasImmutableInstances () {
+    return ((elementInfoAttrs & ElementInfo.ATTR_IMMUTABLE) != 0);
+  }
+
+  public boolean hasInstanceFieldInfoAttr (Class<?> type){
+    for (int i=0; i<nInstanceFields; i++){
+      if (getInstanceField(i).hasAttr(type)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public NativePeer getNativePeer () {
+    return nativePeer;
+  }
+  
+  /**
+   * Returns true if the given class is an instance of the class
+   * or interface specified.
+   */
+  public boolean isInstanceOf (String cname) {
+    if (isPrimitive()) {
+      return Types.getJNITypeCode(name).equals(cname);
+
+    } else {
+      cname = Types.getClassNameFromTypeName(cname);
+      ClassInfo ci = this.classLoader.getResolvedClassInfo(cname);
+      return isInstanceOf(ci);
+    }
+  }
+
+  /**
+   * Returns true if the given class is an instance of the class
+   * or interface specified.
+   */
+  public boolean isInstanceOf (ClassInfo ci) {
+    if (isPrimitive()) { // no inheritance for builtin types
+      return (this==ci);
+    } else {
+      for (ClassInfo c = this; c != null; c = c.superClass) {
+        if (c==ci) {
+          return true;
+        }
+      }
+
+      return getAllInterfaces().contains(ci);
+    }
+  }
+
+  public boolean isInnerClassOf (String enclosingName){
+    // don't register or initialize yet
+    ClassInfo ciEncl = classLoader.tryGetResolvedClassInfo( enclosingName);
+    if (ciEncl != null){
+      return ciEncl.hasInnerClass(name);
+    } else {
+      return false;
+    }
+  }
+  
+  public boolean hasInnerClass (String innerName){
+    for (int i=0; i<innerClassNames.length; i++){
+      if (innerClassNames[i].equals(innerName)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+
+
+  public static String makeModelClassPath (Config config) {
+    StringBuilder buf = new StringBuilder(256);
+    String ps = File.pathSeparator;
+    String v;
+
+    for (File f : config.getPathArray("boot_classpath")){
+      buf.append(f.getAbsolutePath());
+      buf.append(ps);
+    }
+
+    for (File f : config.getPathArray("classpath")){
+      buf.append(f.getAbsolutePath());
+      buf.append(ps);
+    }
+
+    // finally, we load from the standard Java libraries
+    v = System.getProperty("sun.boot.class.path");
+    if (v != null) {
+      buf.append(v);
+    }
+    
+    return buf.toString();
+  }
+  
+  protected static String[] loadArrayInterfaces () {
+    return new String[] {"java.lang.Cloneable", "java.io.Serializable"};
+  }
+
+  protected static String[] loadBuiltinInterfaces (String type) {
+    return EMPTY_STRING_ARRAY;
+  }
+
+
+  /**
+   * Loads the ClassInfo for named class.
+   */
+  void loadInterfaceRec (Set<ClassInfo> set, String[] interfaces) throws ClassInfoException {
+    if (interfaces != null) {
+      for (String iname : interfaces) {
+
+        ClassInfo ci = classLoader.getResolvedClassInfo(iname);
+
+        if (set != null){
+          set.add(ci);
+        }
+
+        loadInterfaceRec(set, ci.interfaceNames);
+      }
+    }
+  }
+
+  int computeInstanceDataOffset () {
+    if (superClass == null) {
+      return 0;
+    } else {
+      return superClass.getInstanceDataSize();
+    }
+  }
+
+  int getInstanceDataOffset () {
+    return instanceDataOffset;
+  }
+
+  ClassInfo getClassBase (String clsBase) {
+    if ((clsBase == null) || (name.equals(clsBase))) return this;
+
+    if (superClass != null) {
+      return superClass.getClassBase(clsBase);
+    }
+
+    return null; // Eeek - somebody asked for a class that isn't in the base list
+  }
+
+  int computeInstanceDataSize () {
+    int n = getDataSize( iFields);
+
+    for (ClassInfo c=superClass; c!= null; c=c.superClass) {
+      n += c.getDataSize(c.iFields);
+    }
+
+    return n;
+  }
+
+  public int getInstanceDataSize () {
+    return instanceDataSize;
+  }
+
+  int getDataSize (FieldInfo[] fields) {
+    int n=0;
+    for (int i=0; i<fields.length; i++) {
+      n += fields[i].getStorageSize();
+    }
+
+    return n;
+  }
+
+  public int getNumberOfDeclaredInstanceFields () {
+    return iFields.length;
+  }
+
+  public FieldInfo getDeclaredInstanceField (int i) {
+    return iFields[i];
+  }
+
+  public int getNumberOfInstanceFields () {
+    return nInstanceFields;
+  }
+
+  public FieldInfo getInstanceField (int i) {
+    int idx = i - (nInstanceFields - iFields.length);
+    if (idx >= 0) {
+      return ((idx < iFields.length) ? iFields[idx] : null);
+    } else {
+      return ((superClass != null) ? superClass.getInstanceField(i) : null);
+    }
+  }
+
+  public FieldInfo[] getInstanceFields(){
+    FieldInfo[] fields = new FieldInfo[nInstanceFields];
+    
+    for (int i=0; i<fields.length; i++){
+      fields[i] = getInstanceField(i);
+    }
+    
+    return fields;
+  }
+
+  public int getStaticDataSize () {
+    return staticDataSize;
+  }
+
+  int computeStaticDataSize () {
+    return getDataSize(sFields);
+  }
+
+  public int getNumberOfStaticFields () {
+    return sFields.length;
+  }
+
+  protected Source loadSource () {
+    return Source.getSource(sourceFileName);
+  }
+
+  public static boolean isBuiltinClass (String cname) {
+    char c = cname.charAt(0);
+
+    // array class
+    if ((c == '[') || cname.endsWith("[]")) {
+      return true;
+    }
+
+    // primitive type class
+    if (Character.isLowerCase(c)) {
+      if ("int".equals(cname) || "byte".equals(cname) ||
+          "boolean".equals(cname) || "double".equals(cname) ||
+          "long".equals(cname) || "char".equals(cname) ||
+          "short".equals(cname) || "float".equals(cname) || "void".equals(cname)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * set the locations where we look up sources
+   */
+  static void setSourceRoots (Config config) {
+    Source.init(config);
+  }
+
+  /**
+   * get names of all interfaceNames (transitive, idx.e. incl. bases and super-interfaceNames)
+   * @return a Set of String interface names
+   */
+  public Set<ClassInfo> getAllInterfaces () {
+    if (allInterfaces == null) {
+      HashSet<ClassInfo> set = new HashSet<ClassInfo>();
+
+      for (ClassInfo ci=this; ci != null; ci=ci.superClass) {
+        loadInterfaceRec(set, ci.interfaceNames);
+      }
+
+      allInterfaces = Collections.unmodifiableSet(set);
+    }
+
+    return allInterfaces;
+  }
+
+  /**
+   * get names of directly implemented interfaceNames
+   */
+  public String[] getDirectInterfaceNames () {
+    return interfaceNames;
+  }
+
+  public Set<ClassInfo> getInterfaceClassInfos() {
+    return interfaces;
+  }
+
+  public Set<ClassInfo> getAllInterfaceClassInfos() {
+    return getAllInterfaces();
+  }
+
+  
+  /**
+   * get names of direct inner classes
+   */
+  public String[] getInnerClasses(){
+    return innerClassNames;
+  }
+  
+  public ClassInfo[] getInnerClassInfos(){
+    ClassInfo[] innerClassInfos = new ClassInfo[innerClassNames.length];
+    
+    for (int i=0; i< innerClassNames.length; i++){
+      innerClassInfos[i] = classLoader.getResolvedClassInfo(innerClassNames[i]); // ? is this supposed to use the same classloader
+    }
+    
+    return innerClassInfos;
+  }
+  
+  public BootstrapMethodInfo getBootstrapMethodInfo(int index) {
+    return bootstrapMethods[index];
+  }
+
+  public ClassInfo getComponentClassInfo () {
+    if (isArray()) {
+      String cn = name.substring(1);
+
+      if (cn.charAt(0) != '[') {
+        cn = Types.getTypeName(cn);
+      }
+
+      ClassInfo cci = classLoader.getResolvedClassInfo(cn);
+
+      return cci;
+    }
+
+    return null;
+  }
+
+  /**
+   * most definitely not a public method, but handy for the NativePeer
+   */
+  protected Map<String, MethodInfo> getDeclaredMethods () {
+    return methods;
+  }
+
+  /**
+   * be careful, this replaces or adds MethodInfos dynamically
+   */
+  public MethodInfo putDeclaredMethod (MethodInfo mi){
+    return methods.put(mi.getUniqueName(), mi);
+  }
+
+  public MethodInfo[] getDeclaredMethodInfos() {
+    MethodInfo[] a = new MethodInfo[methods.size()];
+    methods.values().toArray(a);
+    return a;
+  }
+
+  public Instruction[] getMatchingInstructions (LocationSpec lspec){
+    Instruction[] insns = null;
+
+    if (lspec.matchesFile(sourceFileName)){
+      for (MethodInfo mi : methods.values()) {
+        Instruction[] a = mi.getMatchingInstructions(lspec);
+        if (a != null){
+          if (insns != null) {
+            // not very efficient but probably rare
+            insns = Misc.appendArray(insns, a);
+          } else {
+            insns = a;
+          }
+
+          // little optimization
+          if (!lspec.isLineInterval()) {
+            break;
+          }
+        }
+      }
+    }
+
+    return insns;
+  }
+
+  public List<MethodInfo> getMatchingMethodInfos (MethodSpec mspec){
+    ArrayList<MethodInfo> list = null;
+    if (mspec.matchesClass(name)) {
+      for (MethodInfo mi : methods.values()) {
+        if (mspec.matches(mi)) {
+          if (list == null) {
+            list = new ArrayList<MethodInfo>();
+          }
+          list.add(mi);
+        }
+      }
+    }
+    return list;
+  }
+
+  public MethodInfo getFinalizer () {
+    return finalizer;
+  }
+
+  public MethodInfo getClinit() {
+    // <2do> braindead - cache
+    for (MethodInfo mi : methods.values()) {
+      if ("<clinit>".equals(mi.getName())) {
+        return mi;
+      }
+    }
+    return null;
+  }
+
+  public boolean hasCtors() {
+    // <2do> braindead - cache
+    for (MethodInfo mi : methods.values()) {
+      if ("<init>".equals(mi.getName())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * see getInitializedClassInfo() for restrictions.
+   */
+  public static ClassInfo getInitializedSystemClassInfo (String clsName, ThreadInfo ti){
+    ClassLoaderInfo systemLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
+    ClassInfo ci = systemLoader.getResolvedClassInfo(clsName);
+    ci.initializeClassAtomic(ti);
+
+    return ci;
+  }
+
+  /**
+   * this one is for clients that need to synchronously get an initialized classinfo.
+   * NOTE: we don't handle clinits here. If there is one, this will throw
+   * an exception. NO STATIC BLOCKS / FIELDS ALLOWED
+   */
+  public static ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
+    ClassLoaderInfo cl = ClassLoaderInfo.getCurrentClassLoader();
+    ClassInfo ci = cl.getResolvedClassInfo(clsName);
+    ci.initializeClassAtomic(ti);
+
+    return ci;
+  }
+
+  public boolean isRegistered () {
+    //return (id != -1);
+    return getStaticElementInfo() != null;
+  }
+  
+  /**
+   * this registers a ClassInfo in the corresponding ClassLoader statics so that we can cross-link from
+   * SUT code and access static fields.
+   */
+  public StaticElementInfo registerClass (ThreadInfo ti){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei == null) {
+      // do this recursively for superclasses and interfaceNames
+      // respective classes might be defined by another classloader, so we have to call their ClassInfo.registerClass()
+      
+      if (superClass != null) {
+        superClass.registerClass(ti);
+      }
+
+      for (ClassInfo ifc : interfaces) {
+        ifc.registerClass(ti);
+      }
+      
+      ClassInfo.logger.finer("registering class: ", name);
+      
+      ElementInfo ei = createClassObject( ti);
+      sei = createAndLinkStaticElementInfo( ti, ei);
+      
+      // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
+      ti.getVM().notifyClassLoaded(this);
+    }
+    
+    return sei;
+  }
+
+  ElementInfo createClassObject (ThreadInfo ti){
+    Heap heap = VM.getVM().getHeap(); // ti can be null (during main thread initialization)
+
+    int anchor = name.hashCode(); // 2do - this should also take the ClassLoader ref into account
+
+    SystemClassLoaderInfo systemClassLoader = ti.getSystemClassLoaderInfo();
+
+    ClassInfo classClassInfo = systemClassLoader.getClassClassInfo();    
+    ElementInfo ei = heap.newSystemObject(classClassInfo, ti, anchor);
+    int clsObjRef = ei.getObjectRef();
+    
+    ElementInfo eiClsName = heap.newSystemString(name, ti, clsObjRef);
+    ei.setReferenceField("name", eiClsName.getObjectRef());
+
+    ei.setBooleanField("isPrimitive", isPrimitive());
+    
+    // setting the ID_FIELD is done in registerClass once we have a StaticElementInfo
+
+    // link the SUT class object to the classloader 
+    ei.setReferenceField("classLoader", classLoader.getClassLoaderObjectRef());
+    
+    return ei;
+  }
+  
+  StaticElementInfo createAndLinkStaticElementInfo (ThreadInfo ti, ElementInfo eiClsObj) {
+    Statics statics = classLoader.getStatics();
+    StaticElementInfo sei = statics.newClass(this, ti, eiClsObj);
+    
+    id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
+    uniqueId = ((long)classLoader.getId() << 32) | id;
+    
+    eiClsObj.setIntField( ID_FIELD, id);      
+    
+    return sei;
+  }
+
+  
+  // for startup classes, the order of initialization is reversed since we can't create
+  // heap objects before we have a minimal set of registered classes
+  
+  void registerStartupClass(ThreadInfo ti, List<ClassInfo> list) {
+    if (!isRegistered()) {
+      // do this recursively for superclasses and interfaceNames
+      // respective classes might be defined by another classloader, so we have
+      // to call their ClassInfo.registerClass()
+
+      if (superClass != null) {
+        superClass.registerStartupClass(ti, list);
+      }
+
+      for (ClassInfo ifc : interfaces) {
+        ifc.registerStartupClass(ti, list);
+      }
+    }
+
+    if (!list.contains(this)) {
+      list.add(this);
+      ClassInfo.logger.finer("registering startup class: ", name);
+      createStartupStaticElementInfo(ti);
+    }
+    
+      // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
+      ti.getVM().notifyClassLoaded(this);
+  }
+  
+  StaticElementInfo createStartupStaticElementInfo (ThreadInfo ti) {
+    Statics statics = classLoader.getStatics();
+    StaticElementInfo sei = statics.newStartupClass(this, ti);
+    
+    id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
+    uniqueId = ((long)classLoader.getId() << 32) | id;
+    
+    return sei;
+  }
+  
+  ElementInfo createAndLinkStartupClassObject (ThreadInfo ti) {
+    StaticElementInfo sei = getStaticElementInfo();
+    ElementInfo ei = createClassObject(ti);
+    
+    sei.setClassObjectRef(ei.getObjectRef());
+    ei.setIntField( ID_FIELD, id);      
+    
+    return ei;
+  }
+  
+  boolean checkIfValidClassClassInfo() {
+    return getDeclaredInstanceField( ID_FIELD) != null;
+  }
+  
+  public boolean isInitializing () {
+    StaticElementInfo sei = getStaticElementInfo();
+    return ((sei != null) && (sei.getStatus() >= 0));
+  }
+
+  /**
+   * note - this works recursively upwards since there might
+   * be a superclass with a clinit that is still executing
+   */
+  public boolean isInitialized () {
+    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      if (sei == null || sei.getStatus() != INITIALIZED){
+        return false;
+      }
+    }
+    
+    return true;
+  }
+
+  public boolean isResolved () {
+    return (!isObjectClassInfo() && superClass != null);
+  }
+
+  public boolean needsInitialization (ThreadInfo ti){
+    StaticElementInfo sei = getStaticElementInfo();
+    if (sei != null){
+      int status = sei.getStatus();
+      if (status == INITIALIZED || status == ti.getId()){
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public void setInitializing(ThreadInfo ti) {
+    StaticElementInfo sei = getModifiableStaticElementInfo();
+    sei.setStatus(ti.getId());
+  }
+  
+  /**
+   * 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 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 (pushedFrames > 0);
+  }
+
+  /**
+   * use this with care since it will throw a JPFException if we encounter a choice point
+   * during execution of clinits
+   * Use this mostly for wrapper exceptions and other system classes that are guaranteed to load
+   */
+  public void initializeClassAtomic (ThreadInfo ti){
+    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 && status != ti.getId()){
+          MethodInfo mi = ci.getMethod("<clinit>()V", false);
+          if (mi != null) {
+            DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
+            ti.executeMethodAtomic(frame);
+          } else {
+            ci.setInitialized();
+          }
+      } else {
+        break; // if this class is initialized, so are its superclasses
+      }
+    }
+  }
+
+  public void setInitialized() {
+    StaticElementInfo sei = getStaticElementInfo();
+    if (sei != null && sei.getStatus() != INITIALIZED){
+      sei = getModifiableStaticElementInfo();
+      sei.setStatus(INITIALIZED);
+
+      // we don't emit classLoaded() notifications for non-builtin classes
+      // here anymore because it would be confusing to get instructionExecuted()
+      // notifications from the <clinit> execution before the classLoaded()
+    }
+  }
+
+  public StaticElementInfo getStaticElementInfo() {
+    if (id != -1) {
+      return classLoader.getStatics().get( id);
+    } else {
+      return null;
+    }
+  }
+
+  public StaticElementInfo getModifiableStaticElementInfo() {
+    if (id != -1) {
+      return classLoader.getStatics().getModifiable( id);
+    } else {
+      return null;      
+    }
+  }
+
+  Fields createArrayFields (String type, int nElements, int typeSize, boolean isReferenceArray) {
+    return fieldsFactory.createArrayFields( type, this,
+                                            nElements, typeSize, isReferenceArray);
+  }
+
+  /**
+   * Creates the fields for a class.  This gets called during registration of a ClassInfo
+   */
+  Fields createStaticFields () {
+    return fieldsFactory.createStaticFields(this);
+  }
+
+  void initializeStaticData (ElementInfo ei, ThreadInfo ti) {
+    for (int i=0; i<sFields.length; i++) {
+      FieldInfo fi = sFields[i];
+      fi.initialize(ei, ti);
+    }
+  }
+
+  /**
+   * Creates the fields for an object.
+   */
+  public Fields createInstanceFields () {
+    return fieldsFactory.createInstanceFields(this);
+  }
+
+  void initializeInstanceData (ElementInfo ei, ThreadInfo ti) {
+    // Note this is only used for field inits, and array elements are not fields!
+    // Since Java has only limited element init requirements (either 0 or null),
+    // we do this ad hoc in the ArrayFields ctor
+
+    // the order of inits should not matter, since this is only
+    // for constant inits. In case of a "class X { int a=42; int b=a; ..}"
+    // we have a explicit "GETFIELD a, PUTFIELD b" in the ctor, but to play it
+    // safely we init top down
+
+    if (superClass != null) { // do superclasses first
+      superClass.initializeInstanceData(ei, ti);
+    }
+
+    for (int i=0; i<iFields.length; i++) {
+      FieldInfo fi = iFields[i];
+      fi.initialize(ei, ti);
+    }
+  }
+
+  Map<String, MethodInfo> loadArrayMethods () {
+    return new HashMap<String, MethodInfo>(0);
+  }
+
+  Map<String, MethodInfo> loadBuiltinMethods (String type) {
+    type = null;  // Get rid of IDE warning 
+     
+    return new HashMap<String, MethodInfo>(0);
+  }
+
+  protected ClassInfo loadSuperClass (String superName) throws ClassInfoException {
+    if (isObjectClassInfo()) {
+      return null;
+    }
+
+    logger.finer("resolving superclass: ", superName, " of ", name);
+
+    // resolve the superclass
+    ClassInfo sci = resolveReferencedClass(superName);
+
+    return sci;
+  }
+
+  protected Set<ClassInfo> loadInterfaces (String[] ifcNames) throws ClassInfoException {
+    if (ifcNames == null || ifcNames.length == 0){
+      return NO_INTERFACES;
+      
+    } else {
+      Set<ClassInfo> set = new HashSet<ClassInfo>();
+
+      for (String ifcName : ifcNames) {
+        ClassInfo.logger.finer("resolving interface: ", ifcName, " of ", name);
+        ClassInfo ifc = resolveReferencedClass(ifcName);
+        set.add(ifc);
+      }
+
+      return set;
+    }
+  }
+  
+  /**
+   * loads superclass and direct interfaces, and computes information
+   * that depends on them
+   */
+  protected void resolveClass() {
+    if (!isObjectClassInfo){
+      superClass = loadSuperClass(superClassName);
+      releaseActions = superClass.releaseActions;
+    }
+    interfaces = loadInterfaces(interfaceNames);
+
+    //computeInheritedAnnotations(superClass);
+
+    isWeakReference = isWeakReference0();
+    isEnum = isEnum0();
+  }
+
+  /**
+   * get a ClassInfo for a referenced type that is resolved with the same classLoader, but make
+   * sure we only do this once per path
+   * 
+   * This method is called by the following bytecode instructions:
+   * anewarray, checkcast, getstatic, instanceof, invokespecial, 
+   * invokestatic, ldc, ldc_w, multianewarray, new, and putstatic
+   * 
+   * It loads the class referenced by these instructions and adds it to the 
+   * resolvedClasses map of the classLoader
+   */
+  public ClassInfo resolveReferencedClass(String cname) {
+    if(name.equals(cname)) {
+      return this;
+    }
+
+    // if the class has been already resolved just return it
+    ClassInfo ci = classLoader.getAlreadyResolvedClassInfo(cname);
+    if(ci != null) {
+      return ci;
+    }
+    // The defining class loader of the class initiate the load of referenced classes
+    ci = classLoader.loadClass(cname);
+    classLoader.addResolvedClass(ci);
+
+    return ci;
+  }
+
+  protected int linkFields (FieldInfo[] fields, int idx, int off){
+    for (FieldInfo fi: fields) {      
+      fi.linkToClass(this, idx, off);
+      
+      int storageSize = fi.getStorageSize();      
+      off += storageSize;
+      idx++;
+    }
+    
+    return off;
+  }
+  
+  protected void linkFields() {
+    //--- instance fields
+    if(superClass != null) {
+      int superDataSize = superClass.instanceDataSize;
+      instanceDataSize = linkFields( iFields,  superClass.nInstanceFields, superDataSize);
+      nInstanceFields = superClass.nInstanceFields + iFields.length;
+      instanceDataOffset = superClass.instanceDataSize;
+      
+    } else {
+      instanceDataSize = linkFields( iFields, 0, 0);
+      nInstanceFields = iFields.length;
+      instanceDataOffset = 0;
+    }
+    
+    //--- static fields
+    staticDataSize = linkFields( sFields, 0, 0);
+  }
+
+  // this resolves all annotations in this class hierarchy, which sets inherited attributes
+  protected void checkInheritedAnnotations (){
+    
+  }
+  
+  @Override
+  public String toString() {
+    return "ClassInfo[name=" + name + "]";
+  }
+
+  protected MethodInfo getFinalizer0 () {
+    MethodInfo mi = getMethod("finalize()V", true);
+
+    // we are only interested in non-empty method bodies, Object.finalize()
+    // is a dummy
+    if ((mi != null) && (!mi.getClassInfo().isObjectClassInfo())) {
+      return mi;
+    }
+
+    return null;
+  }
+
+  protected boolean isObjectClassInfo0 () {
+       if (name.equals("java.lang.Object")) {
+         return true;
+       }
+       return false;
+  }
+
+  protected boolean isStringClassInfo0 () {
+    if(name.equals("java.lang.String")) {
+      return true;
+    }
+    return false;
+  }
+
+  protected boolean isRefClassInfo0 () {
+    if(name.equals("java.lang.ref.Reference")) {
+      return true;
+    }
+    return false;
+  }
+
+  protected boolean isWeakReference0 () {
+       if(name.equals("java.lang.ref.WeakReference")) {
+      return true;
+       }
+
+    for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
+      if (ci.isWeakReference()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean isEnum0 () {
+       if(name.equals("java.lang.Enum")) {
+      return true;
+       }
+
+    for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
+      if (ci.isEnum()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean isThreadClassInfo0 () {
+    if(name.equals("java.lang.Thread")) {
+      return true;
+    }
+    return false;
+  }
+
+
+  /**
+   * It creates an instance from a original ClassInfo instance. It doesn't copy sei & 
+   * uniqueId.
+   * 
+   * It is used for the cases where cl tries to load a class that the original version 
+   * of which has been loaded by some other classloader.
+   */
+  public ClassInfo cloneFor (ClassLoaderInfo cl) {
+    ClassInfo ci;
+
+    try {
+      ci = (ClassInfo)clone();
+
+      ci.classLoader = cl;
+      ci.interfaces = new HashSet<ClassInfo>();
+      ci.resolveClass();
+
+      ci.id = -1;
+      ci.uniqueId = -1;
+
+      if (methods != Collections.EMPTY_MAP){
+        ci.methods = (Map<String, MethodInfo>)((HashMap<String, MethodInfo>) methods).clone();
+      }
+
+      for(Map.Entry<String, MethodInfo> e: ci.methods.entrySet()) {
+        MethodInfo mi = e.getValue();
+        e.setValue(mi.getInstanceFor(ci));
+      }
+
+      ci.iFields = new FieldInfo[iFields.length];
+      for(int i=0; i<iFields.length; i++) {
+        ci.iFields[i] = iFields[i].getInstanceFor(ci);
+      }
+
+      ci.sFields = new FieldInfo[sFields.length];
+      for(int i=0; i<sFields.length; i++) {
+        ci.sFields[i] = sFields[i].getInstanceFor(ci);
+      }
+
+      if(nativePeer != null) {
+        ci.nativePeer = NativePeer.getNativePeer(ci);
+      }
+
+      ci.setAssertionStatus();
+
+    } catch (CloneNotSupportedException cnsx){
+      cnsx.printStackTrace();
+      return null;
+    }
+
+    VM.getVM().notifyClassLoaded(ci);
+    return ci;
+  }
+  
+  // <2do> should be abstract
+  public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
+    return null;
+  }
+  
+  public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo callee, int nLocalSlots){
+    return null;
+  }
+  
+  public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
+    return null;
+  }
+}
+
+