X-Git-Url: http://plrg.eecs.uci.edu/git/?p=jpf-core.git;a=blobdiff_plain;f=src%2Fmain%2Fgov%2Fnasa%2Fjpf%2Fvm%2FClassInfo.java;fp=src%2Fmain%2Fgov%2Fnasa%2Fjpf%2Fvm%2FClassInfo.java;h=d26abeda060b94d8053569a4a392188c26245035;hp=0000000000000000000000000000000000000000;hb=a9bc9081ebda74eb6ee5451d2b719405db3a955c;hpb=eb7cfaa7d9ce99087e9678a61f6840d3cd48f2b2 diff --git a/src/main/gov/nasa/jpf/vm/ClassInfo.java b/src/main/gov/nasa/jpf/vm/ClassInfo.java new file mode 100644 index 0000000..d26abed --- /dev/null +++ b/src/main/gov/nasa/jpf/vm/ClassInfo.java @@ -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, 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 NO_METHODS = Collections.emptyMap(); + protected static final Set NO_INTERFACES = new HashSet(); + + /** + * support to auto-load listeners from annotations + */ + protected static HashSet autoloadAnnotations; + protected static HashSet 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 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 interfaces = new HashSet(); + + /** cache of all interfaceNames (parent interfaceNames and interface parents) - lazy eval */ + protected Set 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 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(); + + 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(); + + 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 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(); + 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 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( 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.Entrye : 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.Entrye : 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.Entrye : 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 iterator() { + return new Iterator() { + ClassInfo ci = ClassInfo.this; + Iterator 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 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 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 - 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 type){ + for (int i=0; i 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= 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 getAllInterfaces () { + if (allInterfaces == null) { + HashSet set = new HashSet(); + + 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 getInterfaceClassInfos() { + return interfaces; + } + + public Set 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 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 getMatchingMethodInfos (MethodSpec mspec){ + ArrayList list = null; + if (mspec.matchesClass(name)) { + for (MethodInfo mi : methods.values()) { + if (mspec.matches(mi)) { + if (list == null) { + list = new ArrayList(); + } + list.add(mi); + } + } + } + return list; + } + + public MethodInfo getFinalizer () { + return finalizer; + } + + public MethodInfo getClinit() { + // <2do> braindead - cache + for (MethodInfo mi : methods.values()) { + if ("".equals(mi.getName())) { + return mi; + } + } + return null; + } + + public boolean hasCtors() { + // <2do> braindead - cache + for (MethodInfo mi : methods.values()) { + if ("".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 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("()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("()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 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 loadArrayMethods () { + return new HashMap(0); + } + + Map loadBuiltinMethods (String type) { + type = null; // Get rid of IDE warning + + return new HashMap(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 loadInterfaces (String[] ifcNames) throws ClassInfoException { + if (ifcNames == null || ifcNames.length == 0){ + return NO_INTERFACES; + + } else { + Set set = new HashSet(); + + 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(); + ci.resolveClass(); + + ci.id = -1; + ci.uniqueId = -1; + + if (methods != Collections.EMPTY_MAP){ + ci.methods = (Map)((HashMap) methods).clone(); + } + + for(Map.Entry e: ci.methods.entrySet()) { + MethodInfo mi = e.getValue(); + e.setValue(mi.getInstanceFor(ci)); + } + + ci.iFields = new FieldInfo[iFields.length]; + for(int i=0; i 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; + } +} + +