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