Merge branch 'master' into gradle
[jpf-core.git] / src / main / gov / nasa / jpf / vm / ClassInfo.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.JPFConfigException;
23 import gov.nasa.jpf.JPFListener;
24 import gov.nasa.jpf.util.ImmutableList;
25 import gov.nasa.jpf.util.JPFLogger;
26 import gov.nasa.jpf.util.LocationSpec;
27 import gov.nasa.jpf.util.MethodSpec;
28 import gov.nasa.jpf.util.Misc;
29 import gov.nasa.jpf.util.OATHash;
30 import gov.nasa.jpf.util.Source;
31
32 import java.io.File;
33 import java.lang.reflect.Modifier;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Iterator;
39 import java.util.LinkedHashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.NoSuchElementException;
43 import java.util.Set;
44 import java.util.logging.Level;
45
46
47 /**
48  * Describes the VM's view of a java class.  Contains descriptions of the
49  * static and dynamic fields, declaredMethods, and information relevant to the
50  * class.
51  * 
52  * Note that ClassInfos / classes have three different construction/initialization steps:
53  * (1) construction : recursively via ClassLoaderInfo.getResolvedClassInfo -> ClassFileContainer.createClassInfo
54  *     -> ClassInfo ctor -> resolveClass
55  *     this only creates the ClassInfo object, but it is not visible/usable from SUT code yet and hence not
56  *     observable from classLoaded() listeners
57  * (2) registration : create StaticElementInfo and add it to the respective ClassLoaderInfo statics, then create
58  *     the java.lang.Class companion object in the SUT
59  *     this makes the ClassInfo usable from SUT code
60  * (3) initialization : execute clinit (if the class has one)
61  * 
62  * Note that id/uniqueId are NOT set before registration takes place, and registration is not automatically performed since
63  * listeners/peers might create ClassInfos internally (e.g. for inspection), which should not be visible from the SUT or observable
64  * by other listeners.
65  * 
66  * Automatic registration from the ClassInfo ctors would require to pass a ThreadInfo context throughout the whole ClassLoaderInfo/
67  * ClassFileContainer/ClassInfo chain and could lead to false positives for sharedness based POR, which would record this
68  * thread as referencing even if this is a listener/peer internal request
69  */
70 public class ClassInfo extends InfoObject implements Iterable<MethodInfo>, GenericSignatureHolder {
71
72   //--- ClassInfo states, in chronological order
73   // note the somewhat strange, decreasing values - >= 0 (=thread-id) means 
74   // we are in clinit
75   // ideally, we would have a separate RESOLVED state, but (a) this is somewhat
76   // orthogonal to REGISTERED, and - more importantly - (b) we need the
77   // superClass instance when initializing our Fields (instance field offsets).
78   // Doing the field initialization during resolveReferencedClass() seems awkward and
79   // error prone (there is not much you can do with an unresolved class then)
80   
81   // not registered or clinit'ed (but cached in loadedClasses)
82   public static final int UNINITIALIZED = -1;
83   // 'REGISTERED' simply means 'sei' is set (we have a StaticElementInfo)
84   // 'INITIALIZING' is any number >=0, which is the thread objRef that executes the clinit
85   public static final int INITIALIZED = -2;
86
87   protected static final String ID_FIELD = "nativeId"; 
88
89   protected static JPFLogger logger = JPF.getLogger("class");
90
91   protected static int nClassInfos; // for statistics
92   
93   protected static Config config;
94
95   /**
96    * ClassLoader that loaded this class.
97    */
98   protected static final ClassLoader thisClassLoader = ClassInfo.class.getClassLoader();  
99   
100   /**
101    * our abstract factory to createAndInitialize object and class fields
102    */
103   protected static FieldsFactory fieldsFactory;
104
105   
106   protected static final FieldInfo[] EMPTY_FIELDINFO_ARRAY = new FieldInfo[0];
107   protected static final String[] EMPTY_STRING_ARRAY = new String[0];
108   protected static final String UNINITIALIZED_STRING = "UNINITIALIZED"; 
109   protected static final Map<String,MethodInfo> NO_METHODS = Collections.emptyMap();
110   protected static final Set<ClassInfo> NO_INTERFACES = new HashSet<ClassInfo>();
111   
112   /**
113    * support to auto-load listeners from annotations
114    */
115   protected static HashSet<String> autoloadAnnotations;
116   protected static HashSet<String> autoloaded;
117
118   /**
119    * Name of the class. e.g. "java.lang.String"
120    * NOTE - this is the expanded name for builtin types, e.g. "int", but NOT
121    * for arrays, which are for some reason in Ldot notation, e.g. "[Ljava.lang.String;" or "[I"
122    */
123   protected String name;
124   
125   /** type erased signature of the class. e.g. "Ljava/lang/String;" */
126   protected String signature;
127
128   /** Generic type signatures of the class as per para. 4.4.4 of the revised VM spec */
129   protected String genericSignature;
130
131   /** The classloader that defined (directly loaded) this class */
132   protected ClassLoaderInfo classLoader;
133   
134   // various class attributes
135   protected boolean      isClass = true;
136   protected boolean      isWeakReference = false;
137   protected boolean      isObjectClassInfo = false;
138   protected boolean      isStringClassInfo = false;
139   protected boolean      isThreadClassInfo = false;
140   protected boolean      isRefClassInfo = false;
141   protected boolean      isArray = false;
142   protected boolean      isEnum = false;
143   protected boolean      isReferenceArray = false;
144   protected boolean      isAbstract = false;
145   protected boolean      isBuiltin = false;
146
147   // that's ultimately where we keep the attributes
148   // <2do> this is currently quite redundant, but these are used in reflection
149   protected int modifiers;
150
151   protected MethodInfo   finalizer = null;
152
153   /** type based object attributes (for GC, partial order reduction and
154    * property checks)
155    */
156   protected int elementInfoAttrs = 0;
157
158   /**
159    * all our declared declaredMethods (we don't flatten, this is not
160    * a high-performance VM)
161    */
162   protected Map<String, MethodInfo> methods;
163
164   /**
165    * our instance fields.
166    * Note these are NOT flattened, idx.e. only contain the declared ones
167    */
168   protected FieldInfo[] iFields;
169
170   /** the storage size of instances of this class (stored as an int[]) */
171   protected int instanceDataSize;
172
173   /** where in the instance data array (int[]) do our declared fields start */
174   protected int instanceDataOffset;
175
176   /** total number of instance fields (flattened, not only declared ones) */
177   protected int nInstanceFields;
178
179   /**
180    * our static fields. Again, not flattened
181    */
182   protected FieldInfo[] sFields;
183
184   /** the storage size of static fields of this class (stored as an int[]) */
185   protected int staticDataSize;
186
187   /**
188    * we only set the superClassName upon creation, it is instantiated into
189    * a ClassInfo by resolveReferencedClass(), which is required to be called before
190    * we can createAndInitialize objects of this type
191    */
192   protected ClassInfo  superClass;
193   protected String superClassName;
194
195   protected String enclosingClassName;
196   protected String enclosingMethodName;
197
198   protected String[] innerClassNames = EMPTY_STRING_ARRAY;
199   protected BootstrapMethodInfo[] bootstrapMethods;
200     
201   /** direct ifcs implemented by this class */
202   protected String[] interfaceNames;
203
204   protected Set<ClassInfo> interfaces = new HashSet<ClassInfo>();
205   
206   /** cache of all interfaceNames (parent interfaceNames and interface parents) - lazy eval */
207   protected Set<ClassInfo> allInterfaces;
208   
209   /** Name of the package. */
210   protected String packageName;
211
212   /** this is only set if the classfile has a SourceFile class attribute */
213   protected String sourceFileName;
214
215   /** 
216    * Uniform resource locater for the class file. NOTE: since for builtin classes
217    * there is no class file assigned is set to the typeName 
218    */ 
219   protected String classFileUrl;
220
221   /** from where the corresponding classfile was loaded (if this is not a builtin) */
222   protected gov.nasa.jpf.vm.ClassFileContainer container;
223
224   
225   /**
226    *  a search global numeric id that is only unique within this ClassLoader namespace. Ids are
227    *  computed by the ClassLoaderInfo/Statics implementation during ClassInfo registration
228    */
229   protected int  id = -1;
230
231   /**
232    * A search global unique id associate with this class, which is comprised of the classLoader id
233    * and the (loader-specific) ClassInfo id. This is just a quick way to do search global checks for equality
234    * 
235    * NOTE - since this is based on the classloader-specific id, it can't be used before the ClassInfo is registered
236    */
237   protected long uniqueId = -1;
238
239   /**
240    * this is the object we use to enter declaredMethods in the underlying VM
241    * (it replaces Reflection)
242    */
243   protected NativePeer nativePeer;
244
245   /** Source file associated with the class.*/
246   protected Source source;
247
248   protected boolean enableAssertions;
249
250   /** actions to be taken when an object of this type is gc'ed */
251   protected ImmutableList<ReleaseAction> releaseActions; 
252           
253   
254   static boolean init (Config config) {
255
256     ClassInfo.config = config;
257     
258     setSourceRoots(config);
259     //buildBCELModelClassPath(config);
260
261     fieldsFactory = config.getEssentialInstance("vm.fields_factory.class",
262                                                 FieldsFactory.class);
263
264     autoloadAnnotations = config.getNonEmptyStringSet("listener.autoload");
265     if (autoloadAnnotations != null) {
266       autoloaded = new HashSet<String>();
267
268       if (logger.isLoggable(Level.INFO)) {
269         for (String s : autoloadAnnotations){
270           logger.info("watching for autoload annotation @" + s);
271         }
272       }
273     }
274
275     return true;
276   }
277
278   public static boolean isObjectClassInfo (ClassInfo ci){
279     return ci.isObjectClassInfo();
280   }
281
282   public static boolean isStringClassInfo (ClassInfo ci){
283     return ci.isStringClassInfo();
284   }
285
286   
287    //--- initialization interface towards parsers (which might reside in other packages)
288     
289   protected void setClass(String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
290     String parsedName = Types.getClassNameFromTypeName(clsName);
291
292     if (name != null && !name.equals(parsedName)){
293       throw new ClassParseException("wrong class name (expected: " + name + ", found: " + parsedName + ')');
294     }
295     name = parsedName;
296     
297     // the enclosingClassName is set on demand since it requires loading enclosing class candidates
298     // to verify their innerClass attributes
299
300     int i = name.lastIndexOf('.');
301     packageName = (i > 0) ? name.substring(0, i) : "";
302
303     modifiers = flags;
304     
305     // annotations are interfaces too (not exposed by Modifier)
306     isClass = ((flags & Modifier.INTERFACE) == 0);
307
308     superClassName = superClsName;
309   }
310
311   public void setInnerClassNames(String[] clsNames) {
312     innerClassNames = clsNames;
313   }
314
315   public void setEnclosingClass (String clsName) {
316     enclosingClassName = clsName;
317   }
318   
319   public void setEnclosingMethod (String mthName){
320     enclosingMethodName = mthName;    
321   }
322
323   public void setInterfaceNames(String[] ifcNames) {
324     interfaceNames = ifcNames;
325   }
326   
327   public void setSourceFile (String fileName){
328     // prepend if we already know the package
329     if (packageName.length() > 0) {
330       // Source will take care of proper separator chars later
331       sourceFileName = packageName.replace('.', '/') + '/' + fileName;
332     } else {
333       sourceFileName = fileName;
334     }
335   }
336
337   public void setFields(FieldInfo[] fields) {
338     if (fields == null){
339       iFields = EMPTY_FIELDINFO_ARRAY;
340       sFields = EMPTY_FIELDINFO_ARRAY;
341       
342     } else { // there are fields, we have to tell them apart
343       int nInstance = 0, nStatic = 0;
344       for (int i = 0; i < fields.length; i++) {
345         if (fields[i].isStatic()) {
346           nStatic++;
347         } else {
348           nInstance++;
349         }
350       }
351
352       FieldInfo[] instanceFields = (nInstance > 0) ? new FieldInfo[nInstance] : EMPTY_FIELDINFO_ARRAY;
353       FieldInfo[] staticFields = (nStatic > 0) ? new FieldInfo[nStatic] : EMPTY_FIELDINFO_ARRAY;
354
355       int iInstance = 0;
356       int iStatic = 0;
357       for (int i = 0; i < fields.length; i++) {
358         FieldInfo fi = fields[i];
359
360         if (fi.isStatic()) {
361           staticFields[iStatic++] = fi;
362         } else {
363           instanceFields[iInstance++] = fi;
364         }
365         
366         processJPFAnnotations(fi);
367       }
368
369       iFields = instanceFields;
370       sFields = staticFields;
371
372       // we can't link the fields yet because we need the superclasses to be resolved
373     }
374   }
375
376   protected void setMethod (MethodInfo mi){
377     mi.linkToClass(this);
378     methods.put( mi.getUniqueName(), mi);
379     processJPFAnnotations(mi);
380   }
381   
382   public void setMethods (MethodInfo[] newMethods) {
383     if (newMethods != null && newMethods.length > 0) {
384       methods = new LinkedHashMap<String, MethodInfo>();
385
386       for (int i = 0; i < newMethods.length; i++) {
387         setMethod( newMethods[i]);
388       }
389     }
390   }
391  
392   protected void processJPFAttrAnnotation(InfoObject infoObj){
393     AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.JPFAttribute");
394     if (ai != null){
395       String[] attrTypes = ai.getValueAsStringArray();
396       if (attrTypes != null){
397         ClassLoader loader = config.getClassLoader();
398
399         for (String clsName : attrTypes){
400           try {
401             Class<?> attrCls = loader.loadClass(clsName);
402             Object attr = attrCls.newInstance(); // needs to have a default ctor
403             infoObj.addAttr(attr);
404             
405           } catch (ClassNotFoundException cnfx){
406             logger.warning("attribute class not found: " + clsName);
407             
408           } catch (IllegalAccessException iax){
409             logger.warning("attribute class has no public default ctor: " + clsName);            
410             
411           } catch (InstantiationException ix){
412             logger.warning("attribute class has no default ctor: " + clsName);            
413           }
414         }
415       }
416     }    
417   }
418
419   protected void processNoJPFExecutionAnnotation(InfoObject infoObj) {
420     AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.NoJPFExecution");
421     if (ai != null) {
422       infoObj.addAttr(NoJPFExec.SINGLETON);
423     }
424   }
425
426   protected void processJPFAnnotations(InfoObject infoObj) {
427     processJPFAttrAnnotation(infoObj);
428     processNoJPFExecutionAnnotation(infoObj);
429   }
430
431     public AnnotationInfo getResolvedAnnotationInfo (String typeName){
432     return classLoader.getResolvedAnnotationInfo( typeName);
433   }
434   
435   @Override
436   public void setAnnotations(AnnotationInfo[] annotations) {
437     this.annotations = annotations;
438   }
439   
440   //--- end initialization interface
441  
442   //--- the overridden annotation accessors (we need these because of inherited superclass annotations)
443   // note that we don't flatten annotations anymore, assuming the prevalent query will be getAnnotation(name)
444   
445   @Override
446   public boolean hasAnnotations(){
447     if (annotations.length > 0){
448       return true;
449     }
450     
451     for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
452       AnnotationInfo[] a = ci.annotations;
453       for (int j=0; j<a.length; j++){
454         if (a[j].isInherited()){
455           return true;
456         }
457       }
458     }
459     
460     return false;
461   }
462   
463   /**
464    * return all annotations, which includes the ones inherited from our superclasses
465    * NOTE - this is not very efficient
466    */
467   @Override
468   public AnnotationInfo[] getAnnotations() {
469     int nAnnotations = annotations.length;
470     for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
471       AnnotationInfo[] a = ci.annotations;
472       for (int i=0; i<a.length; i++){
473         if (a[i].isInherited()){
474           nAnnotations++;
475         }
476       }
477     }
478     
479     AnnotationInfo[] allAnnotations = new AnnotationInfo[nAnnotations];
480     System.arraycopy(annotations, 0, allAnnotations, 0, annotations.length);
481     int idx=annotations.length;
482     for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
483       AnnotationInfo[] a = ci.annotations;
484       for (int i=0; i<a.length; i++){
485         if (a[i].isInherited()){
486           allAnnotations[idx++] = a[i];
487         }
488       }
489     }
490     
491     return allAnnotations;
492   }
493     
494   @Override
495   public AnnotationInfo getAnnotation (String annotationName){
496     AnnotationInfo[] a = annotations;
497     for (int i=0; i<a.length; i++){
498       if (a[i].getName().equals(annotationName)){
499         return a[i];
500       }
501     }
502     
503     for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
504       a = ci.annotations;
505       for (int i=0; i<a.length; i++){
506         AnnotationInfo ai = a[i];
507         if (ai.getName().equals(annotationName) && ai.isInherited()){
508           return ai;
509         }
510       }
511     }
512     
513     return null;
514   }
515   
516   protected ClassInfo (String name, ClassLoaderInfo cli, String classFileUrl){
517     nClassInfos++;
518     
519     this.name = name;
520     this.classLoader = cli;
521     this.classFileUrl = classFileUrl;
522     
523     this.methods = NO_METHODS;  // yet
524
525     // rest has to be initialized by concrete ctor, which should call resolveAndLink(parser)
526   }
527   
528   /**
529    * the initialization part that has to happen once we have super, fields, methods and annotations
530    * NOTE - this has to be called by concrete ctors after parsing class files
531    */
532   protected void resolveAndLink () throws ClassParseException {
533     
534     //--- these might get streamlined
535     isStringClassInfo = isStringClassInfo0();
536     isThreadClassInfo = isThreadClassInfo0();
537     isObjectClassInfo = isObjectClassInfo0();
538     isRefClassInfo = isRefClassInfo0();
539    // isWeakReference = isWeakReference0();
540     isAbstract = (modifiers & Modifier.ABSTRACT) != 0;
541    // isEnum = isEnum0();
542     
543     finalizer = getFinalizer0();
544
545     resolveClass(); // takes care of super classes and interfaces
546
547     // Used to enter native methods (in the host VM).
548     // This needs to be initialized AFTER we get our  MethodInfos, since it does a reverse lookup to determine which
549     // ones are handled by the peer (by means of setting MethodInfo attributes)
550     nativePeer = loadNativePeer();
551     checkUnresolvedNativeMethods();
552
553     linkFields(); // computes field offsets
554     
555     setAssertionStatus();
556     processJPFConfigAnnotation();
557     processJPFAnnotations(this);
558     loadAnnotationListeners();    
559   }
560   
561   protected ClassInfo(){
562     nClassInfos++;
563     
564     // for explicit subclass initialization
565   }
566   
567   /**
568    * ClassInfo ctor used for builtin types (arrays and primitive types)
569    * idx.e. classes we don't have class files for
570    */
571   protected ClassInfo (String builtinClassName, ClassLoaderInfo classLoader) {
572     nClassInfos++;
573
574     this.classLoader = classLoader;
575
576     isArray = (builtinClassName.charAt(0) == '[');
577     isReferenceArray = isArray && (builtinClassName.endsWith(";") || builtinClassName.charAt(1) == '[');
578     isBuiltin = true;
579
580     name = builtinClassName;
581
582     logger.log(Level.FINE, "generating builtin class: %1$s", name);
583
584     packageName = ""; // builtin classes don't reside in java.lang !
585     sourceFileName = null;
586     source = null;
587     genericSignature = "";
588
589     // no fields
590     iFields = EMPTY_FIELDINFO_ARRAY;
591     sFields = EMPTY_FIELDINFO_ARRAY;
592
593     if (isArray) {
594       if(classLoader.isSystemClassLoader()) {
595         superClass = ((SystemClassLoaderInfo)classLoader).getObjectClassInfo();
596       } else {
597         superClass = ClassLoaderInfo.getCurrentSystemClassLoader().getObjectClassInfo();
598       }
599       interfaceNames = loadArrayInterfaces();
600       methods = loadArrayMethods();
601     } else {
602       superClass = null; // strange, but true, a 'no object' class
603       interfaceNames = loadBuiltinInterfaces(name);
604       methods = loadBuiltinMethods(name);
605     }
606
607     enableAssertions = true; // doesn't really matter - no code associated
608
609     classFileUrl = name;
610     
611     // no fields or declaredMethods, so we don't have to link/resolve anything
612   }
613   
614   public static int getNumberOfLoadedClasses(){
615     return nClassInfos;
616   }
617   
618   //--- the VM type specific methods
619   // <2do> those should be abstract
620   
621   protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
622     // to be overridden by VM specific class
623   }
624   
625   protected void setDirectCallCode (MethodInfo miCallee, MethodInfo miStub){
626     // to be overridden by VM specific class
627   }
628   
629   protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod){
630     // to be overridden by VM specific class
631   }
632   
633   protected void setNativeCallCode (NativeMethodInfo miNative){
634     // to be overridden by VM specific class
635   }
636   
637   protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
638     // to be overridden by VM specific class
639   }
640   
641   /**
642    * createAndInitialize a fully synthetic implementation of an Annotation proxy
643    */
644   protected ClassInfo (ClassInfo annotationCls, String name, ClassLoaderInfo classLoader, String url) {
645     this.classLoader = classLoader;
646     
647     this.name = name;
648     isClass = true;
649
650     //superClass = objectClassInfo;
651     superClass = ClassLoaderInfo.getSystemResolvedClassInfo("gov.nasa.jpf.AnnotationProxyBase");
652
653     interfaceNames = new String[]{ annotationCls.name };    
654     packageName = annotationCls.packageName;
655     sourceFileName = annotationCls.sourceFileName;
656     genericSignature = annotationCls.genericSignature;
657
658     sFields = new FieldInfo[0]; // none
659     staticDataSize = 0;
660
661     methods = new HashMap<String, MethodInfo>();
662     iFields = new FieldInfo[annotationCls.methods.size()];
663     nInstanceFields = iFields.length;
664
665     // all accessor declaredMethods of ours make it into iField/method combinations
666     int idx = 0;
667     int off = 0;  // no super class
668     for (MethodInfo mi : annotationCls.getDeclaredMethodInfos()) {
669       String mname = mi.getName();
670       String mtype = mi.getReturnType();
671       String genericSignature = mi.getGenericSignature();
672
673       // create and initialize an instance field for it
674       FieldInfo fi = FieldInfo.create(mname, mtype, 0);
675       fi.linkToClass(this, idx, off);
676       fi.setGenericSignature(genericSignature);
677       iFields[idx++] = fi;
678       off += fi.getStorageSize();
679
680       MethodInfo pmi = new MethodInfo(this, mname, mi.getSignature(), Modifier.PUBLIC, 1, 2);
681       pmi.setGenericSignature(genericSignature);
682       
683       setAnnotationValueGetterCode( pmi, fi);
684       methods.put(pmi.getUniqueName(), pmi);
685     }
686
687     instanceDataSize = computeInstanceDataSize();
688     instanceDataOffset = 0;
689
690     classFileUrl = url;
691     linkFields();
692   }
693   
694   
695   //used to create synthetic classes that implement functional interfaces
696   protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
697    return null;
698  }
699  
700  protected ClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String[] fieldTypesName) {
701    ClassInfo enclosingClass = bootstrapMethod.enclosingClass;
702    this.classLoader = enclosingClass.classLoader;
703
704    this.name = name;
705    isClass = true;
706
707    superClassName = "java.lang.Object";
708
709    interfaceNames = new String[]{ funcInterface.name };    
710    packageName = enclosingClass.getPackageName();
711
712    // creating fields used to capture free variables
713    int n = fieldTypesName.length;
714    
715    iFields = new FieldInfo[n];
716    nInstanceFields = n;
717    
718    sFields = new FieldInfo[0];
719    staticDataSize = 0;
720    
721    int idx = 0;
722    int off = 0;  // no super class
723    
724    int i = 0;
725    for(String type: fieldTypesName) {
726      FieldInfo fi = FieldInfo.create("arg" + i++, type, 0);
727      fi.linkToClass(this, idx, off);
728      iFields[idx++] = fi;
729      off += fi.getStorageSize();
730    }
731    
732    linkFields();
733  }
734   
735   // since id and hence uniqueId are not set before this class is registered, we can't use them
736   
737   @Override
738   public int hashCode() {
739     return OATHash.hash(name.hashCode(), classLoader.hashCode());
740   }
741   
742   @Override
743   public boolean equals (Object o) {
744     if (o instanceof ClassInfo) {
745       ClassInfo other = (ClassInfo)o;
746       if (classLoader == other.classLoader) {
747         // beware of ClassInfos that are not registered yet - in this case we have to equals names
748         if (name.equals(other.name)) {
749           return true;
750         }
751       }
752     }
753     
754     return false;
755   }
756
757   protected String computeSourceFileName(){
758     return name.replace('.', '/') + ".java";
759   }
760
761   protected void checkUnresolvedNativeMethods(){
762     for (MethodInfo mi : methods.values()){
763       if (mi.isUnresolvedNativeMethod()){
764         NativeMethodInfo nmi = new NativeMethodInfo(mi, null, nativePeer);
765         nmi.replace(mi);
766       }
767     }
768   }
769
770   protected void processJPFConfigAnnotation() {
771     AnnotationInfo ai = getAnnotation("gov.nasa.jpf.annotation.JPFConfig");
772     if (ai != null) {
773       for (String s : ai.getValueAsStringArray()) {
774         config.parse(s);
775       }
776     }
777   }
778
779   protected void loadAnnotationListeners () {
780     if (autoloadAnnotations != null) {
781       autoloadListeners(annotations); // class annotations
782
783       for (int i=0; i<sFields.length; i++) {
784         autoloadListeners(sFields[i].getAnnotations());
785       }
786
787       for (int i=0; i<iFields.length; i++) {
788         autoloadListeners(iFields[i].getAnnotations());
789       }
790
791       // method annotations are checked during method loading
792       // (to avoid extra iteration)
793     }
794   }
795
796   void autoloadListeners(AnnotationInfo[] annos) {
797     if ((annos != null) && (autoloadAnnotations != null)) {
798       for (AnnotationInfo ai : annos) {
799         String aName = ai.getName();
800         if (autoloadAnnotations.contains(aName)) {
801           if (!autoloaded.contains(aName)) {
802             autoloaded.add(aName);
803             String key = "listener." + aName;
804             String defClsName = aName + "Checker";
805             try {
806               JPFListener listener = config.getInstance(key, JPFListener.class, defClsName);
807               
808               JPF jpf = VM.getVM().getJPF(); // <2do> that's a BAD access path
809               jpf.addUniqueTypeListener(listener);
810
811               if (logger.isLoggable(Level.INFO)){
812                 logger.info("autoload annotation listener: @", aName, " => ", listener.getClass().getName());
813               }
814
815             } catch (JPFConfigException cx) {
816               logger.warning("no autoload listener class for annotation " + aName +
817                              " : " + cx.getMessage());
818               autoloadAnnotations.remove(aName);
819             }
820           }
821         }
822       }
823
824       if (autoloadAnnotations.isEmpty()) {
825         autoloadAnnotations = null;
826       }
827     }
828   }
829
830   protected NativePeer loadNativePeer(){
831     return NativePeer.getNativePeer(this);
832   }
833   
834   /**
835    * Returns the class loader that 
836    */
837   public ClassLoaderInfo getClassLoaderInfo() {
838     return classLoader;
839   }
840
841   /**
842    * the container this is stored in
843    */
844   public Statics getStatics() {
845     return classLoader.getStatics();
846   }
847   
848   /**
849    * required by InfoObject interface
850    */
851   public ClassInfo getClassInfo() {
852     return this;
853   }
854
855   protected void setAssertionStatus() {
856     if(isInitialized()) {
857       return;
858     } else {
859       enableAssertions = classLoader.desiredAssertionStatus(name);
860     }
861   }
862
863   boolean getAssertionStatus () {
864     return enableAssertions;
865   }
866
867   public boolean desiredAssertionStatus() {
868     return classLoader.desiredAssertionStatus(name);
869   }
870
871   @Override
872   public String getGenericSignature() {
873     return genericSignature;
874   }
875
876   @Override
877   public void setGenericSignature(String sig){
878     genericSignature = sig;
879   }
880   
881   public boolean isArray () {
882     return isArray;
883   }
884
885   public boolean isEnum () {
886     return isEnum;
887   }
888
889   public boolean isAbstract() {
890     return isAbstract;
891   }
892
893   public boolean isBuiltin(){
894     return isBuiltin;
895   }
896   
897   public boolean isInterface() {
898     return ((modifiers & Modifier.INTERFACE) != 0);
899   }
900
901   public boolean isReferenceArray () {
902     return isReferenceArray;
903   }
904
905   public boolean isObjectClassInfo() {
906     return isObjectClassInfo;
907   }
908
909   public boolean isStringClassInfo() {
910     return isStringClassInfo;
911   }
912
913   public boolean isThreadClassInfo() {
914     return isThreadClassInfo;
915   }
916
917   protected void checkNoClinitInitialization(){
918     if (!isInitialized()){
919       ThreadInfo ti = ThreadInfo.getCurrentThread();
920       registerClass(ti);
921       setInitialized(); // we might want to check if there is a clinit
922     }
923   }
924   
925   protected ClassInfo createAnnotationProxy (String proxyName){
926     // to be overridden by VM specific ClassInfos
927     return null;
928   }
929   
930   public ClassInfo getAnnotationProxy (){
931     // <2do> test if this is a annotation ClassInfo
932     
933     checkNoClinitInitialization(); // annotation classes don't have clinits
934     
935     ClassInfo ciProxy = classLoader.getResolvedAnnotationProxy(this);
936     ciProxy.checkNoClinitInitialization();
937     
938     return ciProxy;
939   }
940   
941 /**
942   public static ClassInfo getAnnotationProxy (ClassInfo ciAnnotation){
943     ThreadInfo ti = ThreadInfo.getCurrentThread();
944
945     // make sure the annotationCls is initialized (no code there)
946     if (!ciAnnotation.isInitialized()) {
947       ciAnnotation.registerClass(ti);
948       ciAnnotation.setInitialized(); // no clinit
949     }
950
951     String url = computeProxyUrl(ciAnnotation);
952     ClassInfo ci = null; // getOriginalClassInfo(url);
953
954     if (ci == null){
955       String cname = ciAnnotation.getName() + "$Proxy";
956       ci = new ClassInfo(ciAnnotation, cname, ciAnnotation.classLoader, url);
957       ciAnnotation.classLoader.addResolvedClass(ci);
958       if (!ci.isInitialized()){
959         ci.registerClass(ti);
960         ci.setInitialized();
961       }
962     }
963
964     return ci;
965   }
966 **/
967
968   public boolean areAssertionsEnabled() {
969     return enableAssertions;
970   }
971
972   public boolean hasInstanceFields () {
973     return (instanceDataSize > 0);
974   }
975
976   public ElementInfo getClassObject(){
977     StaticElementInfo sei = getStaticElementInfo();
978     
979     if (sei != null){
980       int objref = sei.getClassObjectRef();
981       return VM.getVM().getElementInfo(objref);
982     }
983
984     return null;
985   }
986   
987   public ElementInfo getModifiableClassObject(){
988     StaticElementInfo sei = getStaticElementInfo();
989     
990     if (sei != null){
991       int objref = sei.getClassObjectRef();
992       return VM.getVM().getModifiableElementInfo(objref);
993     }
994
995     return null;
996   }
997   
998
999   public int getClassObjectRef () {
1000     StaticElementInfo sei = getStaticElementInfo();    
1001     return (sei != null) ? sei.getClassObjectRef() : MJIEnv.NULL;
1002   }
1003
1004   public gov.nasa.jpf.vm.ClassFileContainer getContainer(){
1005     return container;
1006   }
1007   
1008   public String getClassFileUrl (){
1009     return classFileUrl;
1010   }
1011
1012   //--- type based object release actions
1013   
1014   public boolean hasReleaseAction (ReleaseAction action){
1015     return (releaseActions != null) && releaseActions.contains(action);
1016   }
1017   
1018   /**
1019    * NOTE - this can only be set *before* subclasses are loaded (e.g. from classLoaded() notification) 
1020    */
1021   public void addReleaseAction (ReleaseAction action){
1022     // flattened in ctor to super releaseActions
1023     releaseActions = new ImmutableList<ReleaseAction>( action, releaseActions);
1024   }
1025   
1026   /**
1027    * recursively process release actions registered for this type or any of
1028    * its super types (only classes). The releaseAction list is flattened during
1029    * ClassInfo initialization, to reduce runtime overhead during GC sweep
1030    */
1031   public void processReleaseActions (ElementInfo ei){
1032     if (superClass != null){
1033       superClass.processReleaseActions(ei);
1034     }
1035     
1036     if (releaseActions != null) {
1037       for (ReleaseAction action : releaseActions) {
1038         action.release(ei);
1039       }
1040     }
1041   }
1042   
1043   public int getModifiers() {
1044     return modifiers;
1045   }
1046
1047   /**
1048    * Note that 'uniqueName' is the name plus the argument type part of the
1049    * signature, idx.e. everything that's relevant for overloading
1050    * (besides saving some const space, we also ease reverse lookup
1051    * of natives that way).
1052    * Note also that we don't have to make any difference between
1053    * class and instance declaredMethods, because that just matters in the
1054    * INVOKExx instruction, when looking up the relevant ClassInfo to start
1055    * searching in (either by means of the object type, or by means of the
1056    * constpool classname entry).
1057    */
1058   public MethodInfo getMethod (String uniqueName, boolean isRecursiveLookup) {
1059     MethodInfo mi = methods.get(uniqueName);
1060
1061     if ((mi == null) && isRecursiveLookup && (superClass != null)) {
1062       mi = superClass.getMethod(uniqueName, true);
1063     }
1064
1065     return mi;
1066   }
1067
1068   /**
1069    * if we don't know the return type
1070    * signature is in paren/dot notation
1071    */
1072   public MethodInfo getMethod (String name, String signature, boolean isRecursiveLookup) {
1073     MethodInfo mi = null;
1074     String matchName = name + signature;
1075
1076     for (Map.Entry<String, MethodInfo>e : methods.entrySet()) {
1077       if (e.getKey().startsWith(matchName)){
1078         mi = e.getValue();
1079         break;
1080       }
1081     }
1082
1083     if ((mi == null) && isRecursiveLookup && (superClass != null)) {
1084       mi = superClass.getMethod(name, signature, true);
1085     }
1086
1087     return mi;
1088   }
1089
1090   
1091   public MethodInfo getDefaultMethod (String uniqueName) {
1092     MethodInfo mi = null;
1093     
1094     for (ClassInfo ci = this; ci != null; ci = ci.superClass){
1095       for (ClassInfo ciIfc : ci.interfaces){
1096         MethodInfo miIfc = ciIfc.getMethod(uniqueName, true);
1097         if (miIfc != null && !miIfc.isAbstract()){
1098           if (mi != null && !mi.equals(miIfc)){
1099             // this has to throw a IncompatibleClassChangeError in the client since Java prohibits ambiguous default methods
1100             String msg = "Conflicting default methods: " + mi.getFullName() + ", " + miIfc.getFullName();
1101             throw new ClassChangeException(msg);
1102           } else {
1103             mi = miIfc;
1104           }
1105         }
1106       }
1107     }
1108     
1109     return mi;
1110   }
1111   
1112   /**
1113    * This retrieves the SAM from this functional interface. Note that this is only
1114    * called on functional interface expecting to have a SAM. This shouldn't expect 
1115    * this interface to have only one method which is abstract, since:
1116    *    1. functional interface can declare the abstract methods from the java.lang.Object 
1117    *       class.
1118    *    2. functional interface can extend another interface which is functional, but it 
1119    *       should not declare any new abstract methods.
1120    *    3. functional interface can have one abstract method and any number of default
1121    *       methods.
1122    * 
1123    * To retrieve the SAM, this method iterates over the methods of this interface and its 
1124    * superinterfaces, and it returns the first method which is abstract and it does not 
1125    * declare a method in java.lang.Object.
1126    */
1127   public MethodInfo getInterfaceAbstractMethod () {
1128     ClassInfo objCi = ClassLoaderInfo.getCurrentResolvedClassInfo("java.lang.Object");
1129     
1130     for(MethodInfo mi: this.methods.values()) {
1131       if(mi.isAbstract() && objCi.getMethod(mi.getUniqueName(), false)==null) {
1132         return mi;
1133       }
1134     }
1135     
1136     for (ClassInfo ifc : this.interfaces){
1137       MethodInfo mi = ifc.getInterfaceAbstractMethod();
1138       if(mi!=null) {
1139         return mi;
1140       }
1141     }
1142     
1143     return null;
1144   }
1145
1146   /**
1147    * method lookup for use by reflection methods (java.lang.Class.getXMethod)
1148    * 
1149    * note this doesn't specify the return type, which means covariant return 
1150    * types are not allowed in reflection lookup.
1151    * 
1152    * note also this includes interface methods, but only after the inheritance
1153    * hierarchy has been searched
1154    */
1155   public MethodInfo getReflectionMethod (String fullName, boolean isRecursiveLookup) {
1156         
1157     // first look for methods within the class hierarchy
1158     for (ClassInfo ci = this; ci != null; ci = ci.superClass){
1159       for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
1160         String name = e.getKey();
1161         if (name.startsWith(fullName)) {
1162           return e.getValue();
1163         }
1164       }
1165       if (!isRecursiveLookup){
1166         return null;
1167       }
1168     }
1169
1170     // this is the recursive case - if none found, look for interface methods
1171     for (ClassInfo ci : getAllInterfaces() ){
1172       for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
1173         String name = e.getKey();
1174         if (name.startsWith(fullName)) {
1175           return e.getValue();
1176         }
1177       }      
1178     }    
1179
1180     return null;
1181   }
1182   
1183   /**
1184    * iterate over all declaredMethods of this class (and it's superclasses), until
1185    * the provided MethodLocator tells us it's done
1186    */
1187   public void matchMethods (MethodLocator loc) {
1188     for (MethodInfo mi : methods.values()) {
1189       if (loc.match(mi)) {
1190         return;
1191       }
1192     }
1193     if (superClass != null) {
1194       superClass.matchMethods(loc);
1195     }
1196   }
1197
1198   /**
1199    * iterate over all declaredMethods declared in this class, until the provided
1200    * MethodLocator tells us it's done
1201    */
1202   public void matchDeclaredMethods (MethodLocator loc) {
1203     for (MethodInfo mi : methods.values()) {
1204       if (loc.match(mi)) {
1205         return;
1206       }
1207     }
1208   }
1209
1210   @Override
1211   public Iterator<MethodInfo> iterator() {
1212     return new Iterator<MethodInfo>() {
1213       ClassInfo ci = ClassInfo.this;
1214       Iterator<MethodInfo> it = ci.methods.values().iterator();
1215
1216       @Override
1217         public boolean hasNext() {
1218         if (it.hasNext()) {
1219           return true;
1220         } else {
1221           if (ci.superClass != null) {
1222             ci = ci.superClass;
1223             it = ci.methods.values().iterator();
1224             return it.hasNext();
1225           } else {
1226             return false;
1227           }
1228         }
1229       }
1230
1231       @Override
1232         public MethodInfo next() {
1233         if (hasNext()) {
1234           return it.next();
1235         } else {
1236           throw new NoSuchElementException();
1237         }
1238       }
1239
1240       @Override
1241         public void remove() {
1242         // not supported
1243         throw new UnsupportedOperationException("can't remove methods");
1244       }
1245     };
1246   }
1247   
1248   public Iterator<MethodInfo> declaredMethodIterator() {
1249     return methods.values().iterator();
1250   }
1251
1252   /**
1253    * Search up the class hierarchy to find a static field
1254    * @param fName name of field
1255    * @return null if field name not found (not declared)
1256    */
1257   public FieldInfo getStaticField (String fName) {
1258     FieldInfo fi;
1259     ClassInfo c = this;
1260
1261     while (c != null) {
1262       fi = c.getDeclaredStaticField(fName);
1263       if (fi != null) {
1264         return fi;
1265       }
1266       c = c.superClass;
1267     }
1268
1269     //interfaceNames can have static fields too
1270     // <2do> why would that not be already resolved here ?
1271     for (ClassInfo ci : getAllInterfaces()) {
1272       fi = ci.getDeclaredStaticField(fName);
1273       if (fi != null) {
1274         return fi;
1275       }
1276     }
1277
1278     return null;
1279   }
1280
1281   public Object getStaticFieldValueObject (String id){
1282     ClassInfo c = this;
1283     Object v;
1284
1285     while (c != null){
1286       ElementInfo sei = c.getStaticElementInfo();
1287       v = sei.getFieldValueObject(id);
1288       if (v != null){
1289         return v;
1290       }
1291       c = c.getSuperClass();
1292     }
1293
1294     return null;
1295   }
1296
1297   public FieldInfo[] getDeclaredStaticFields() {
1298     return sFields;
1299   }
1300
1301   public FieldInfo[] getDeclaredInstanceFields() {
1302     return iFields;
1303   }
1304
1305   /**
1306    * FieldInfo lookup in the static fields that are declared in this class
1307    * <2do> pcm - should employ a map at some point, but it's usually not that
1308    * important since we can cash the returned FieldInfo in the PUT/GET_STATIC insns
1309    */
1310   public FieldInfo getDeclaredStaticField (String fName) {
1311     for (int i=0; i<sFields.length; i++) {
1312       if (sFields[i].getName().equals(fName)) return sFields[i];
1313     }
1314
1315     return null;
1316   }
1317
1318   /**
1319    * base relative FieldInfo lookup - the workhorse
1320    * <2do> again, should eventually use Maps
1321    * @param fName the field name
1322    */
1323   public FieldInfo getInstanceField (String fName) {
1324     FieldInfo fi;
1325     ClassInfo c = this;
1326
1327     while (c != null) {
1328       fi = c.getDeclaredInstanceField(fName);
1329       if (fi != null) return fi;
1330       c = c.superClass;
1331     }
1332
1333     return null;
1334   }
1335
1336   /**
1337    * FieldInfo lookup in the fields that are declared in this class
1338    */
1339   public FieldInfo getDeclaredInstanceField (String fName) {
1340     for (int i=0; i<iFields.length; i++) {
1341       if (iFields[i].getName().equals(fName)) return iFields[i];
1342     }
1343
1344     return null;
1345   }
1346   
1347   public String getSignature() {
1348     if (signature == null) {
1349       signature = Types.getTypeSignature(name, false);
1350     }
1351     
1352     return signature;     
1353   }
1354
1355   /**
1356    * Returns the name of the class.  e.g. "java.lang.String".  similar to
1357    * java.lang.Class.getName().
1358    */
1359   public String getName () {
1360     return name;
1361   }
1362
1363   public String getSimpleName () {
1364     int i;
1365     String enclosingClassName = getEnclosingClassName();
1366     
1367     if(enclosingClassName!=null){
1368       i = enclosingClassName.length();      
1369     } else{
1370       i = name.lastIndexOf('.');
1371     }
1372     
1373     return name.substring(i+1);
1374   }
1375
1376   public String getPackageName () {
1377     return packageName;
1378   }
1379
1380   public int getId() {
1381     return id;
1382   }
1383
1384   public long getUniqueId() {
1385     return uniqueId;
1386   }
1387
1388   public int getFieldAttrs (int fieldIndex) {
1389     fieldIndex = 0; // Get rid of IDE warning
1390      
1391     return 0;
1392   }
1393
1394   public void setElementInfoAttrs (int attrs){
1395     elementInfoAttrs = attrs;
1396   }
1397
1398   public void addElementInfoAttr (int attr){
1399     elementInfoAttrs |= attr;
1400   }
1401
1402   public int getElementInfoAttrs () {
1403     return elementInfoAttrs;
1404   }
1405
1406   public Source getSource () {
1407     if (source == null) {
1408       source = loadSource();
1409     }
1410
1411     return source;
1412   }
1413
1414   public String getSourceFileName () {
1415     return sourceFileName;
1416   }
1417
1418   /**
1419    * Returns the information about a static field.
1420    */
1421   public FieldInfo getStaticField (int index) {
1422     return sFields[index];
1423   }
1424
1425   /**
1426    * Returns the name of a static field.
1427    */
1428   public String getStaticFieldName (int index) {
1429     return getStaticField(index).getName();
1430   }
1431
1432   /**
1433    * Checks if a static method call is deterministic, but only for
1434    * abtraction based determinism, due to Bandera.choose() calls
1435    */
1436   public boolean isStaticMethodAbstractionDeterministic (ThreadInfo th,
1437                                                          MethodInfo mi) {
1438     //    Reflection r = reflection.instantiate();
1439     //    return r.isStaticMethodAbstractionDeterministic(th, mi);
1440     // <2do> - still has to be implemented
1441      
1442     th = null;  // Get rid of IDE warning
1443     mi = null;
1444      
1445     return true;
1446   }
1447
1448   public String getSuperClassName() {
1449     return superClassName;
1450   }
1451
1452   /**
1453    * Return the super class.
1454    */
1455   public ClassInfo getSuperClass () {
1456     return superClass;
1457   }
1458
1459   /**
1460    * return the ClassInfo for the provided superclass name. If this is equals
1461    * to ourself, return this (a little bit strange if we hit it in the first place)
1462    */
1463   public ClassInfo getSuperClass (String clsName) {
1464     if (clsName.equals(name)) return this;
1465
1466     if (superClass != null) {
1467       return superClass.getSuperClass(clsName);
1468     } else {
1469       return null;
1470     }
1471   }
1472
1473   public int getNumberOfSuperClasses(){
1474     int n = 0;
1475     for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
1476       n++;
1477     }
1478     return n;
1479   }
1480   
1481   /**
1482    * beware - this loads (but not yet registers) the enclosing class
1483    */
1484   public String getEnclosingClassName(){
1485     return enclosingClassName;
1486   }
1487   
1488   /**
1489    * beware - this loads (but not yet registers) the enclosing class
1490    */
1491   public ClassInfo getEnclosingClassInfo() {
1492     String enclName = getEnclosingClassName();
1493     return (enclName == null ? null : classLoader.getResolvedClassInfo(enclName)); // ? is this supposed to use the same classloader
1494   }
1495
1496   public String getEnclosingMethodName(){
1497     return enclosingMethodName;
1498   }
1499
1500   /**
1501    * same restriction as getEnclosingClassInfo() - might not be registered/initialized
1502    */
1503   public MethodInfo getEnclosingMethodInfo(){
1504     MethodInfo miEncl = null;
1505     
1506     if (enclosingMethodName != null){
1507       ClassInfo ciIncl = getEnclosingClassInfo();
1508       miEncl = ciIncl.getMethod( enclosingMethodName, false);
1509     }
1510     
1511     return miEncl;
1512   }
1513   
1514   /**
1515    * Returns true if the class is a system class.
1516    */
1517   public boolean isSystemClass () {
1518     return name.startsWith("java.") || name.startsWith("javax.");
1519   }
1520
1521   /**
1522    * <2do> that's stupid - we should use subclasses for builtin and box types
1523    */
1524   public boolean isBoxClass () {
1525     if (name.startsWith("java.lang.")) {
1526       String rawType = name.substring(10);
1527       if (rawType.startsWith("Boolean") ||
1528           rawType.startsWith("Byte") ||
1529           rawType.startsWith("Character") ||
1530           rawType.startsWith("Integer") ||
1531           rawType.startsWith("Float") ||
1532           rawType.startsWith("Long") ||
1533           rawType.startsWith("Double")) {
1534         return true;
1535       }
1536     }
1537     return false;
1538   }
1539
1540   /**
1541    * Returns the type of a class.
1542    */
1543   public String getType () {
1544     if (!isArray) {
1545       return "L" + name.replace('.', '/') + ";";
1546     } else {
1547       return name;
1548     }
1549   }
1550
1551   /**
1552    * is this a (subclass of) WeakReference? this must be efficient, since it's
1553    * called in the mark phase on all live objects
1554    */
1555   public boolean isWeakReference () {
1556     return isWeakReference;
1557   }
1558
1559   /**
1560    * note this only returns true is this is really the java.lang.ref.Reference classInfo
1561    */
1562   public boolean isReferenceClassInfo () {
1563     return isRefClassInfo;
1564   }
1565
1566   /**
1567    * whether this refers to a primitive type.
1568    */
1569   public boolean isPrimitive() {
1570     return superClass == null && !isObjectClassInfo();
1571   }
1572
1573
1574   boolean hasRefField (int ref, Fields fv) {
1575     ClassInfo c = this;
1576
1577     do {
1578       FieldInfo[] fia = c.iFields;
1579       for (int i=0; i<fia.length; i++) {
1580         FieldInfo fi = c.iFields[i];
1581         if (fi.isReference() && (fv.getIntValue( fi.getStorageOffset()) == ref)) return true;
1582       }
1583       c = c.superClass;
1584     } while (c != null);
1585
1586     return false;
1587   }
1588
1589   boolean hasImmutableInstances () {
1590     return ((elementInfoAttrs & ElementInfo.ATTR_IMMUTABLE) != 0);
1591   }
1592
1593   public boolean hasInstanceFieldInfoAttr (Class<?> type){
1594     for (int i=0; i<nInstanceFields; i++){
1595       if (getInstanceField(i).hasAttr(type)){
1596         return true;
1597       }
1598     }
1599     
1600     return false;
1601   }
1602   
1603   public NativePeer getNativePeer () {
1604     return nativePeer;
1605   }
1606   
1607   /**
1608    * Returns true if the given class is an instance of the class
1609    * or interface specified.
1610    */
1611   public boolean isInstanceOf (String cname) {
1612     if (isPrimitive()) {
1613       return Types.getJNITypeCode(name).equals(cname);
1614
1615     } else {
1616       cname = Types.getClassNameFromTypeName(cname);
1617       ClassInfo ci = this.classLoader.getResolvedClassInfo(cname);
1618       return isInstanceOf(ci);
1619     }
1620   }
1621
1622   /**
1623    * Returns true if the given class is an instance of the class
1624    * or interface specified.
1625    */
1626   public boolean isInstanceOf (ClassInfo ci) {
1627     if (isPrimitive()) { // no inheritance for builtin types
1628       return (this==ci);
1629     } else {
1630       for (ClassInfo c = this; c != null; c = c.superClass) {
1631         if (c==ci) {
1632           return true;
1633         }
1634       }
1635
1636       return getAllInterfaces().contains(ci);
1637     }
1638   }
1639
1640   public boolean isInnerClassOf (String enclosingName){
1641     // don't register or initialize yet
1642     ClassInfo ciEncl = classLoader.tryGetResolvedClassInfo( enclosingName);
1643     if (ciEncl != null){
1644       return ciEncl.hasInnerClass(name);
1645     } else {
1646       return false;
1647     }
1648   }
1649   
1650   public boolean hasInnerClass (String innerName){
1651     for (int i=0; i<innerClassNames.length; i++){
1652       if (innerClassNames[i].equals(innerName)){
1653         return true;
1654       }
1655     }
1656     
1657     return false;
1658   }
1659
1660
1661   public static String makeModelClassPath (Config config) {
1662     StringBuilder buf = new StringBuilder(256);
1663     String ps = File.pathSeparator;
1664     String v;
1665
1666     for (File f : config.getPathArray("boot_classpath")){
1667       buf.append(f.getAbsolutePath());
1668       buf.append(ps);
1669     }
1670
1671     for (File f : config.getPathArray("classpath")){
1672       buf.append(f.getAbsolutePath());
1673       buf.append(ps);
1674     }
1675
1676     // finally, we load from the standard Java libraries
1677     v = System.getProperty("sun.boot.class.path");
1678     if (v != null) {
1679       buf.append(v);
1680     }
1681     
1682     return buf.toString();
1683   }
1684   
1685   protected static String[] loadArrayInterfaces () {
1686     return new String[] {"java.lang.Cloneable", "java.io.Serializable"};
1687   }
1688
1689   protected static String[] loadBuiltinInterfaces (String type) {
1690     return EMPTY_STRING_ARRAY;
1691   }
1692
1693
1694   /**
1695    * Loads the ClassInfo for named class.
1696    */
1697   void loadInterfaceRec (Set<ClassInfo> set, String[] interfaces) throws ClassInfoException {
1698     if (interfaces != null) {
1699       for (String iname : interfaces) {
1700
1701         ClassInfo ci = classLoader.getResolvedClassInfo(iname);
1702
1703         if (set != null){
1704           set.add(ci);
1705         }
1706
1707         loadInterfaceRec(set, ci.interfaceNames);
1708       }
1709     }
1710   }
1711
1712   int computeInstanceDataOffset () {
1713     if (superClass == null) {
1714       return 0;
1715     } else {
1716       return superClass.getInstanceDataSize();
1717     }
1718   }
1719
1720   int getInstanceDataOffset () {
1721     return instanceDataOffset;
1722   }
1723
1724   ClassInfo getClassBase (String clsBase) {
1725     if ((clsBase == null) || (name.equals(clsBase))) return this;
1726
1727     if (superClass != null) {
1728       return superClass.getClassBase(clsBase);
1729     }
1730
1731     return null; // Eeek - somebody asked for a class that isn't in the base list
1732   }
1733
1734   int computeInstanceDataSize () {
1735     int n = getDataSize( iFields);
1736
1737     for (ClassInfo c=superClass; c!= null; c=c.superClass) {
1738       n += c.getDataSize(c.iFields);
1739     }
1740
1741     return n;
1742   }
1743
1744   public int getInstanceDataSize () {
1745     return instanceDataSize;
1746   }
1747
1748   int getDataSize (FieldInfo[] fields) {
1749     int n=0;
1750     for (int i=0; i<fields.length; i++) {
1751       n += fields[i].getStorageSize();
1752     }
1753
1754     return n;
1755   }
1756
1757   public int getNumberOfDeclaredInstanceFields () {
1758     return iFields.length;
1759   }
1760
1761   public FieldInfo getDeclaredInstanceField (int i) {
1762     return iFields[i];
1763   }
1764
1765   public int getNumberOfInstanceFields () {
1766     return nInstanceFields;
1767   }
1768
1769   public FieldInfo getInstanceField (int i) {
1770     int idx = i - (nInstanceFields - iFields.length);
1771     if (idx >= 0) {
1772       return ((idx < iFields.length) ? iFields[idx] : null);
1773     } else {
1774       return ((superClass != null) ? superClass.getInstanceField(i) : null);
1775     }
1776   }
1777
1778   public FieldInfo[] getInstanceFields(){
1779     FieldInfo[] fields = new FieldInfo[nInstanceFields];
1780     
1781     for (int i=0; i<fields.length; i++){
1782       fields[i] = getInstanceField(i);
1783     }
1784     
1785     return fields;
1786   }
1787
1788   public int getStaticDataSize () {
1789     return staticDataSize;
1790   }
1791
1792   int computeStaticDataSize () {
1793     return getDataSize(sFields);
1794   }
1795
1796   public int getNumberOfStaticFields () {
1797     return sFields.length;
1798   }
1799
1800   protected Source loadSource () {
1801     return Source.getSource(sourceFileName);
1802   }
1803
1804   public static boolean isBuiltinClass (String cname) {
1805     char c = cname.charAt(0);
1806
1807     // array class
1808     if ((c == '[') || cname.endsWith("[]")) {
1809       return true;
1810     }
1811
1812     // primitive type class
1813     if (Character.isLowerCase(c)) {
1814       if ("int".equals(cname) || "byte".equals(cname) ||
1815           "boolean".equals(cname) || "double".equals(cname) ||
1816           "long".equals(cname) || "char".equals(cname) ||
1817           "short".equals(cname) || "float".equals(cname) || "void".equals(cname)) {
1818         return true;
1819       }
1820     }
1821
1822     return false;
1823   }
1824
1825   /**
1826    * set the locations where we look up sources
1827    */
1828   static void setSourceRoots (Config config) {
1829     Source.init(config);
1830   }
1831
1832   /**
1833    * get names of all interfaceNames (transitive, idx.e. incl. bases and super-interfaceNames)
1834    * @return a Set of String interface names
1835    */
1836   public Set<ClassInfo> getAllInterfaces () {
1837     if (allInterfaces == null) {
1838       HashSet<ClassInfo> set = new HashSet<ClassInfo>();
1839
1840       for (ClassInfo ci=this; ci != null; ci=ci.superClass) {
1841         loadInterfaceRec(set, ci.interfaceNames);
1842       }
1843
1844       allInterfaces = Collections.unmodifiableSet(set);
1845     }
1846
1847     return allInterfaces;
1848   }
1849
1850   /**
1851    * get names of directly implemented interfaceNames
1852    */
1853   public String[] getDirectInterfaceNames () {
1854     return interfaceNames;
1855   }
1856
1857   public Set<ClassInfo> getInterfaceClassInfos() {
1858     return interfaces;
1859   }
1860
1861   public Set<ClassInfo> getAllInterfaceClassInfos() {
1862     return getAllInterfaces();
1863   }
1864
1865   
1866   /**
1867    * get names of direct inner classes
1868    */
1869   public String[] getInnerClasses(){
1870     return innerClassNames;
1871   }
1872   
1873   public ClassInfo[] getInnerClassInfos(){
1874     ClassInfo[] innerClassInfos = new ClassInfo[innerClassNames.length];
1875     
1876     for (int i=0; i< innerClassNames.length; i++){
1877       innerClassInfos[i] = classLoader.getResolvedClassInfo(innerClassNames[i]); // ? is this supposed to use the same classloader
1878     }
1879     
1880     return innerClassInfos;
1881   }
1882   
1883   public BootstrapMethodInfo getBootstrapMethodInfo(int index) {
1884     return bootstrapMethods[index];
1885   }
1886
1887   public ClassInfo getComponentClassInfo () {
1888     if (isArray()) {
1889       String cn = name.substring(1);
1890
1891       if (cn.charAt(0) != '[') {
1892         cn = Types.getTypeName(cn);
1893       }
1894
1895       ClassInfo cci = classLoader.getResolvedClassInfo(cn);
1896
1897       return cci;
1898     }
1899
1900     return null;
1901   }
1902
1903   /**
1904    * most definitely not a public method, but handy for the NativePeer
1905    */
1906   protected Map<String, MethodInfo> getDeclaredMethods () {
1907     return methods;
1908   }
1909
1910   /**
1911    * be careful, this replaces or adds MethodInfos dynamically
1912    */
1913   public MethodInfo putDeclaredMethod (MethodInfo mi){
1914     return methods.put(mi.getUniqueName(), mi);
1915   }
1916
1917   public MethodInfo[] getDeclaredMethodInfos() {
1918     MethodInfo[] a = new MethodInfo[methods.size()];
1919     methods.values().toArray(a);
1920     return a;
1921   }
1922
1923   public Instruction[] getMatchingInstructions (LocationSpec lspec){
1924     Instruction[] insns = null;
1925
1926     if (lspec.matchesFile(sourceFileName)){
1927       for (MethodInfo mi : methods.values()) {
1928         Instruction[] a = mi.getMatchingInstructions(lspec);
1929         if (a != null){
1930           if (insns != null) {
1931             // not very efficient but probably rare
1932             insns = Misc.appendArray(insns, a);
1933           } else {
1934             insns = a;
1935           }
1936
1937           // little optimization
1938           if (!lspec.isLineInterval()) {
1939             break;
1940           }
1941         }
1942       }
1943     }
1944
1945     return insns;
1946   }
1947
1948   public List<MethodInfo> getMatchingMethodInfos (MethodSpec mspec){
1949     ArrayList<MethodInfo> list = null;
1950     if (mspec.matchesClass(name)) {
1951       for (MethodInfo mi : methods.values()) {
1952         if (mspec.matches(mi)) {
1953           if (list == null) {
1954             list = new ArrayList<MethodInfo>();
1955           }
1956           list.add(mi);
1957         }
1958       }
1959     }
1960     return list;
1961   }
1962
1963   public MethodInfo getFinalizer () {
1964     return finalizer;
1965   }
1966
1967   public MethodInfo getClinit() {
1968     // <2do> braindead - cache
1969     for (MethodInfo mi : methods.values()) {
1970       if ("<clinit>".equals(mi.getName())) {
1971         return mi;
1972       }
1973     }
1974     return null;
1975   }
1976
1977   public boolean hasCtors() {
1978     // <2do> braindead - cache
1979     for (MethodInfo mi : methods.values()) {
1980       if ("<init>".equals(mi.getName())) {
1981         return true;
1982       }
1983     }
1984     return false;
1985   }
1986
1987   /**
1988    * see getInitializedClassInfo() for restrictions.
1989    */
1990   public static ClassInfo getInitializedSystemClassInfo (String clsName, ThreadInfo ti){
1991     ClassLoaderInfo systemLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
1992     ClassInfo ci = systemLoader.getResolvedClassInfo(clsName);
1993     ci.initializeClassAtomic(ti);
1994
1995     return ci;
1996   }
1997
1998   /**
1999    * this one is for clients that need to synchronously get an initialized classinfo.
2000    * NOTE: we don't handle clinits here. If there is one, this will throw
2001    * an exception. NO STATIC BLOCKS / FIELDS ALLOWED
2002    */
2003   public static ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
2004     ClassLoaderInfo cl = ClassLoaderInfo.getCurrentClassLoader();
2005     ClassInfo ci = cl.getResolvedClassInfo(clsName);
2006     ci.initializeClassAtomic(ti);
2007
2008     return ci;
2009   }
2010
2011   public boolean isRegistered () {
2012     //return (id != -1);
2013     return getStaticElementInfo() != null;
2014   }
2015   
2016   /**
2017    * this registers a ClassInfo in the corresponding ClassLoader statics so that we can cross-link from
2018    * SUT code and access static fields.
2019    */
2020   public StaticElementInfo registerClass (ThreadInfo ti){
2021     StaticElementInfo sei = getStaticElementInfo();
2022     
2023     if (sei == null) {
2024       // do this recursively for superclasses and interfaceNames
2025       // respective classes might be defined by another classloader, so we have to call their ClassInfo.registerClass()
2026       
2027       if (superClass != null) {
2028         superClass.registerClass(ti);
2029       }
2030
2031       for (ClassInfo ifc : interfaces) {
2032         ifc.registerClass(ti);
2033       }
2034       
2035       ClassInfo.logger.finer("registering class: ", name);
2036       
2037       ElementInfo ei = createClassObject( ti);
2038       sei = createAndLinkStaticElementInfo( ti, ei);
2039       
2040       // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
2041       ti.getVM().notifyClassLoaded(this);
2042     }
2043     
2044     return sei;
2045   }
2046
2047   ElementInfo createClassObject (ThreadInfo ti){
2048     Heap heap = VM.getVM().getHeap(); // ti can be null (during main thread initialization)
2049
2050     int anchor = name.hashCode(); // 2do - this should also take the ClassLoader ref into account
2051
2052     SystemClassLoaderInfo systemClassLoader = ti.getSystemClassLoaderInfo();
2053
2054     ClassInfo classClassInfo = systemClassLoader.getClassClassInfo();    
2055     ElementInfo ei = heap.newSystemObject(classClassInfo, ti, anchor);
2056     int clsObjRef = ei.getObjectRef();
2057     
2058     ElementInfo eiClsName = heap.newSystemString(name, ti, clsObjRef);
2059     ei.setReferenceField("name", eiClsName.getObjectRef());
2060
2061     ei.setBooleanField("isPrimitive", isPrimitive());
2062     
2063     // setting the ID_FIELD is done in registerClass once we have a StaticElementInfo
2064
2065     // link the SUT class object to the classloader 
2066     ei.setReferenceField("classLoader", classLoader.getClassLoaderObjectRef());
2067     
2068     return ei;
2069   }
2070   
2071   StaticElementInfo createAndLinkStaticElementInfo (ThreadInfo ti, ElementInfo eiClsObj) {
2072     Statics statics = classLoader.getStatics();
2073     StaticElementInfo sei = statics.newClass(this, ti, eiClsObj);
2074     
2075     id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
2076     uniqueId = ((long)classLoader.getId() << 32) | id;
2077     
2078     eiClsObj.setIntField( ID_FIELD, id);      
2079     
2080     return sei;
2081   }
2082
2083   
2084   // for startup classes, the order of initialization is reversed since we can't create
2085   // heap objects before we have a minimal set of registered classes
2086   
2087   void registerStartupClass(ThreadInfo ti, List<ClassInfo> list) {
2088     if (!isRegistered()) {
2089       // do this recursively for superclasses and interfaceNames
2090       // respective classes might be defined by another classloader, so we have
2091       // to call their ClassInfo.registerClass()
2092
2093       if (superClass != null) {
2094         superClass.registerStartupClass(ti, list);
2095       }
2096
2097       for (ClassInfo ifc : interfaces) {
2098         ifc.registerStartupClass(ti, list);
2099       }
2100     }
2101
2102     if (!list.contains(this)) {
2103       list.add(this);
2104       ClassInfo.logger.finer("registering startup class: ", name);
2105       createStartupStaticElementInfo(ti);
2106     }
2107     
2108       // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
2109       ti.getVM().notifyClassLoaded(this);
2110   }
2111   
2112   StaticElementInfo createStartupStaticElementInfo (ThreadInfo ti) {
2113     Statics statics = classLoader.getStatics();
2114     StaticElementInfo sei = statics.newStartupClass(this, ti);
2115     
2116     id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
2117     uniqueId = ((long)classLoader.getId() << 32) | id;
2118     
2119     return sei;
2120   }
2121   
2122   ElementInfo createAndLinkStartupClassObject (ThreadInfo ti) {
2123     StaticElementInfo sei = getStaticElementInfo();
2124     ElementInfo ei = createClassObject(ti);
2125     
2126     sei.setClassObjectRef(ei.getObjectRef());
2127     ei.setIntField( ID_FIELD, id);      
2128     
2129     return ei;
2130   }
2131   
2132   boolean checkIfValidClassClassInfo() {
2133     return getDeclaredInstanceField( ID_FIELD) != null;
2134   }
2135   
2136   public boolean isInitializing () {
2137     StaticElementInfo sei = getStaticElementInfo();
2138     return ((sei != null) && (sei.getStatus() >= 0));
2139   }
2140
2141   /**
2142    * note - this works recursively upwards since there might
2143    * be a superclass with a clinit that is still executing
2144    */
2145   public boolean isInitialized () {
2146     for (ClassInfo ci = this; ci != null; ci = ci.superClass){
2147       StaticElementInfo sei = ci.getStaticElementInfo();
2148       if (sei == null || sei.getStatus() != INITIALIZED){
2149         return false;
2150       }
2151     }
2152     
2153     return true;
2154   }
2155
2156   public boolean isResolved () {
2157     return (!isObjectClassInfo() && superClass != null);
2158   }
2159
2160   public boolean needsInitialization (ThreadInfo ti){
2161     StaticElementInfo sei = getStaticElementInfo();
2162     if (sei != null){
2163       int status = sei.getStatus();
2164       if (status == INITIALIZED || status == ti.getId()){
2165         return false;
2166       }
2167     }
2168
2169     return true;
2170   }
2171
2172   public void setInitializing(ThreadInfo ti) {
2173     StaticElementInfo sei = getModifiableStaticElementInfo();
2174     sei.setStatus(ti.getId());
2175   }
2176   
2177   /**
2178    * initialize this class and its superclasses (but not interfaces)
2179    * this will cause execution of clinits of not-yet-initialized classes in this hierarchy
2180    *
2181    * note - we don't treat registration/initialization of a class as
2182    * a sharedness-changing operation since it is done automatically by
2183    * the VM and the triggering action in the SUT (e.g. static field access or method call)
2184    * is the one that should update sharedness and/or break the transition accordingly
2185    *
2186    * @return true - if initialization pushed DirectCallStackFrames and caller has to re-execute
2187    */
2188   public boolean initializeClass(ThreadInfo ti){
2189     int pushedFrames = 0;
2190
2191     // push clinits of class hierarchy (upwards, since call stack is LIFO)
2192     for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
2193       StaticElementInfo sei = ci.getStaticElementInfo();
2194       if (sei == null){
2195         sei = ci.registerClass(ti);
2196       }
2197
2198       int status = sei.getStatus();
2199       if (status != INITIALIZED){
2200         // we can't do setInitializing() yet because there is no global lock that
2201         // covers the whole clinit chain, and we might have a context switch before executing
2202         // a already pushed subclass clinit - there can be races as to which thread
2203         // does the static init first. Note this case is checked in INVOKECLINIT
2204         // (which is one of the reasons why we have it).
2205
2206         if (status != ti.getId()) {
2207           // even if it is already initializing - if it does not happen in the current thread
2208           // we have to sync, which we do by calling clinit
2209           MethodInfo mi = ci.getMethod("<clinit>()V", false);
2210           if (mi != null) {
2211             DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
2212             ti.pushFrame( frame);
2213             pushedFrames++;
2214
2215           } else {
2216             // it has no clinit, we can set it initialized
2217             ci.setInitialized();
2218           }
2219         } else {
2220           // ignore if it's already being initialized  by our own thread (recursive request)
2221         }
2222       } else {
2223         break; // if this class is initialized, so are its superclasses
2224       }
2225     }
2226
2227     return (pushedFrames > 0);
2228   }
2229
2230   /**
2231    * use this with care since it will throw a JPFException if we encounter a choice point
2232    * during execution of clinits
2233    * Use this mostly for wrapper exceptions and other system classes that are guaranteed to load
2234    */
2235   public void initializeClassAtomic (ThreadInfo ti){
2236     for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
2237       StaticElementInfo sei = ci.getStaticElementInfo();
2238       if (sei == null){
2239         sei = ci.registerClass(ti);
2240       }
2241
2242       int status = sei.getStatus();
2243       if (status != INITIALIZED && status != ti.getId()){
2244           MethodInfo mi = ci.getMethod("<clinit>()V", false);
2245           if (mi != null) {
2246             DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
2247             ti.executeMethodAtomic(frame);
2248           } else {
2249             ci.setInitialized();
2250           }
2251       } else {
2252         break; // if this class is initialized, so are its superclasses
2253       }
2254     }
2255   }
2256
2257   public void setInitialized() {
2258     StaticElementInfo sei = getStaticElementInfo();
2259     if (sei != null && sei.getStatus() != INITIALIZED){
2260       sei = getModifiableStaticElementInfo();
2261       sei.setStatus(INITIALIZED);
2262
2263       // we don't emit classLoaded() notifications for non-builtin classes
2264       // here anymore because it would be confusing to get instructionExecuted()
2265       // notifications from the <clinit> execution before the classLoaded()
2266     }
2267   }
2268
2269   public StaticElementInfo getStaticElementInfo() {
2270     if (id != -1) {
2271       return classLoader.getStatics().get( id);
2272     } else {
2273       return null;
2274     }
2275   }
2276
2277   public StaticElementInfo getModifiableStaticElementInfo() {
2278     if (id != -1) {
2279       return classLoader.getStatics().getModifiable( id);
2280     } else {
2281       return null;      
2282     }
2283   }
2284
2285   Fields createArrayFields (String type, int nElements, int typeSize, boolean isReferenceArray) {
2286     return fieldsFactory.createArrayFields( type, this,
2287                                             nElements, typeSize, isReferenceArray);
2288   }
2289
2290   /**
2291    * Creates the fields for a class.  This gets called during registration of a ClassInfo
2292    */
2293   Fields createStaticFields () {
2294     return fieldsFactory.createStaticFields(this);
2295   }
2296
2297   void initializeStaticData (ElementInfo ei, ThreadInfo ti) {
2298     for (int i=0; i<sFields.length; i++) {
2299       FieldInfo fi = sFields[i];
2300       fi.initialize(ei, ti);
2301     }
2302   }
2303
2304   /**
2305    * Creates the fields for an object.
2306    */
2307   public Fields createInstanceFields () {
2308     return fieldsFactory.createInstanceFields(this);
2309   }
2310
2311   void initializeInstanceData (ElementInfo ei, ThreadInfo ti) {
2312     // Note this is only used for field inits, and array elements are not fields!
2313     // Since Java has only limited element init requirements (either 0 or null),
2314     // we do this ad hoc in the ArrayFields ctor
2315
2316     // the order of inits should not matter, since this is only
2317     // for constant inits. In case of a "class X { int a=42; int b=a; ..}"
2318     // we have a explicit "GETFIELD a, PUTFIELD b" in the ctor, but to play it
2319     // safely we init top down
2320
2321     if (superClass != null) { // do superclasses first
2322       superClass.initializeInstanceData(ei, ti);
2323     }
2324
2325     for (int i=0; i<iFields.length; i++) {
2326       FieldInfo fi = iFields[i];
2327       fi.initialize(ei, ti);
2328     }
2329   }
2330
2331   Map<String, MethodInfo> loadArrayMethods () {
2332     return new HashMap<String, MethodInfo>(0);
2333   }
2334
2335   Map<String, MethodInfo> loadBuiltinMethods (String type) {
2336     type = null;  // Get rid of IDE warning 
2337      
2338     return new HashMap<String, MethodInfo>(0);
2339   }
2340
2341   protected ClassInfo loadSuperClass (String superName) throws ClassInfoException {
2342     if (isObjectClassInfo()) {
2343       return null;
2344     }
2345
2346     logger.finer("resolving superclass: ", superName, " of ", name);
2347
2348     // resolve the superclass
2349     ClassInfo sci = resolveReferencedClass(superName);
2350
2351     return sci;
2352   }
2353
2354   protected Set<ClassInfo> loadInterfaces (String[] ifcNames) throws ClassInfoException {
2355     if (ifcNames == null || ifcNames.length == 0){
2356       return NO_INTERFACES;
2357       
2358     } else {
2359       Set<ClassInfo> set = new HashSet<ClassInfo>();
2360
2361       for (String ifcName : ifcNames) {
2362         ClassInfo.logger.finer("resolving interface: ", ifcName, " of ", name);
2363         ClassInfo ifc = resolveReferencedClass(ifcName);
2364         set.add(ifc);
2365       }
2366
2367       return set;
2368     }
2369   }
2370   
2371   /**
2372    * loads superclass and direct interfaces, and computes information
2373    * that depends on them
2374    */
2375   protected void resolveClass() {
2376     if (!isObjectClassInfo){
2377       superClass = loadSuperClass(superClassName);
2378       releaseActions = superClass.releaseActions;
2379     }
2380     interfaces = loadInterfaces(interfaceNames);
2381
2382     //computeInheritedAnnotations(superClass);
2383
2384     isWeakReference = isWeakReference0();
2385     isEnum = isEnum0();
2386   }
2387
2388   /**
2389    * get a ClassInfo for a referenced type that is resolved with the same classLoader, but make
2390    * sure we only do this once per path
2391    * 
2392    * This method is called by the following bytecode instructions:
2393    * anewarray, checkcast, getstatic, instanceof, invokespecial, 
2394    * invokestatic, ldc, ldc_w, multianewarray, new, and putstatic
2395    * 
2396    * It loads the class referenced by these instructions and adds it to the 
2397    * resolvedClasses map of the classLoader
2398    */
2399   public ClassInfo resolveReferencedClass(String cname) {
2400     if(name.equals(cname)) {
2401       return this;
2402     }
2403
2404     // if the class has been already resolved just return it
2405     ClassInfo ci = classLoader.getAlreadyResolvedClassInfo(cname);
2406     if(ci != null) {
2407       return ci;
2408     }
2409  
2410     // The defining class loader of the class initiate the load of referenced classes
2411     ci = classLoader.loadClass(cname);
2412     classLoader.addResolvedClass(ci);
2413
2414     return ci;
2415   }
2416
2417   protected int linkFields (FieldInfo[] fields, int idx, int off){
2418     for (FieldInfo fi: fields) {      
2419       fi.linkToClass(this, idx, off);
2420       
2421       int storageSize = fi.getStorageSize();      
2422       off += storageSize;
2423       idx++;
2424     }
2425     
2426     return off;
2427   }
2428   
2429   protected void linkFields() {
2430     //--- instance fields
2431     if(superClass != null) {
2432       int superDataSize = superClass.instanceDataSize;
2433       instanceDataSize = linkFields( iFields,  superClass.nInstanceFields, superDataSize);
2434       nInstanceFields = superClass.nInstanceFields + iFields.length;
2435       instanceDataOffset = superClass.instanceDataSize;
2436       
2437     } else {
2438       instanceDataSize = linkFields( iFields, 0, 0);
2439       nInstanceFields = iFields.length;
2440       instanceDataOffset = 0;
2441     }
2442     
2443     //--- static fields
2444     staticDataSize = linkFields( sFields, 0, 0);
2445   }
2446
2447   // this resolves all annotations in this class hierarchy, which sets inherited attributes
2448   protected void checkInheritedAnnotations (){
2449     
2450   }
2451   
2452   @Override
2453   public String toString() {
2454     return "ClassInfo[name=" + name + "]";
2455   }
2456
2457   protected MethodInfo getFinalizer0 () {
2458     MethodInfo mi = getMethod("finalize()V", true);
2459
2460     // we are only interested in non-empty method bodies, Object.finalize()
2461     // is a dummy
2462     if ((mi != null) && (!mi.getClassInfo().isObjectClassInfo())) {
2463       return mi;
2464     }
2465
2466     return null;
2467   }
2468
2469   protected boolean isObjectClassInfo0 () {
2470         if (name.equals("java.lang.Object")) {
2471           return true;
2472         }
2473         return false;
2474   }
2475
2476   protected boolean isStringClassInfo0 () {
2477     if(name.equals("java.lang.String")) {
2478       return true;
2479     }
2480     return false;
2481   }
2482
2483   protected boolean isRefClassInfo0 () {
2484     if(name.equals("java.lang.ref.Reference")) {
2485       return true;
2486     }
2487     return false;
2488   }
2489
2490   protected boolean isWeakReference0 () {
2491         if(name.equals("java.lang.ref.WeakReference")) {
2492       return true;
2493         }
2494
2495     for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
2496       if (ci.isWeakReference()) {
2497         return true;
2498       }
2499     }
2500
2501     return false;
2502   }
2503
2504   protected boolean isEnum0 () {
2505         if(name.equals("java.lang.Enum")) {
2506       return true;
2507         }
2508
2509     for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
2510       if (ci.isEnum()) {
2511         return true;
2512       }
2513     }
2514
2515     return false;
2516   }
2517
2518   protected boolean isThreadClassInfo0 () {
2519     if(name.equals("java.lang.Thread")) {
2520       return true;
2521     }
2522     return false;
2523   }
2524
2525
2526   /**
2527    * It creates an instance from a original ClassInfo instance. It doesn't copy sei & 
2528    * uniqueId.
2529    * 
2530    * It is used for the cases where cl tries to load a class that the original version 
2531    * of which has been loaded by some other classloader.
2532    */
2533   public ClassInfo cloneFor (ClassLoaderInfo cl) {
2534     ClassInfo ci;
2535
2536     try {
2537       ci = (ClassInfo)clone();
2538
2539       ci.classLoader = cl;
2540       ci.interfaces = new HashSet<ClassInfo>();
2541       ci.resolveClass();
2542
2543       ci.id = -1;
2544       ci.uniqueId = -1;
2545
2546       if (methods != Collections.EMPTY_MAP){
2547         ci.methods = (Map<String, MethodInfo>)((HashMap<String, MethodInfo>) methods).clone();
2548       }
2549
2550       for(Map.Entry<String, MethodInfo> e: ci.methods.entrySet()) {
2551         MethodInfo mi = e.getValue();
2552         e.setValue(mi.getInstanceFor(ci));
2553       }
2554
2555       ci.iFields = new FieldInfo[iFields.length];
2556       for(int i=0; i<iFields.length; i++) {
2557         ci.iFields[i] = iFields[i].getInstanceFor(ci);
2558       }
2559
2560       ci.sFields = new FieldInfo[sFields.length];
2561       for(int i=0; i<sFields.length; i++) {
2562         ci.sFields[i] = sFields[i].getInstanceFor(ci);
2563       }
2564
2565       if(nativePeer != null) {
2566         ci.nativePeer = NativePeer.getNativePeer(ci);
2567       }
2568
2569       ci.setAssertionStatus();
2570
2571     } catch (CloneNotSupportedException cnsx){
2572       cnsx.printStackTrace();
2573       return null;
2574     }
2575
2576     VM.getVM().notifyClassLoaded(ci);
2577     return ci;
2578   }
2579   
2580   // <2do> should be abstract
2581   public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
2582     return null;
2583   }
2584   
2585   public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo callee, int nLocalSlots){
2586     return null;
2587   }
2588   
2589   public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
2590     return null;
2591   }
2592 }
2593
2594