org.evosuite.testcarver.capture.FieldRegistry Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see .
*/
package org.evosuite.testcarver.capture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class FieldRegistry {
private static final Map> classRefQueueMapping = new LinkedHashMap>();
private static final Map>> classInstanceMapping = new LinkedHashMap>>();
private static final Map>> instanceRecentFieldValuesMapping = new LinkedHashMap>>();
private static final Map> classFieldsMapping = new LinkedHashMap>();
private static final Set> CLASSES = new LinkedHashSet>();
private static final Set registeredObjects = new LinkedHashSet();
private static final Logger logger = LoggerFactory.getLogger(FieldRegistry.class);
private static int captureId = Integer.MAX_VALUE;
public static ClassLoader carvingClassLoader = null;
private FieldRegistry() {
}
synchronized public static void register(final Object instance) {
if (!Capturer.isCapturing()) {
return;
}
try {
final Class> clazz;
if (instance instanceof Class) {
clazz = (Class>) instance;
} else {
clazz = instance.getClass();
}
final String internalClassName = clazz.getName().replace('.', '/');
registeredObjects.add(System.identityHashCode(instance));
cleanUpReferences(internalClassName);
Map observedFields = classFieldsMapping.get(internalClassName);
if (observedFields == null) {
// determine observable fields
observedFields = new LinkedHashMap();
collectAccessibleFields(observedFields, clazz, null);
//if (observedFields.isEmpty()) {
// logger.debug("Class {} has no observable fields", clazz);
// classFieldsMapping.put(internalClassName, Collections.EMPTY_MAP);
//} else {
// classFieldsMapping.put(internalClassName, observedFields);
//}
}
} catch(Throwable t) {
logger.debug("ARgh");
}
// if (!observedFields.isEmpty()) {
// List> instances = classInstanceMapping.get(internalClassName);
// ReferenceQueue> refQueue = classRefQueueMapping.get(internalClassName);
// if (instances == null) {
// instances = new ArrayList>();
// refQueue = new ReferenceQueue();
// classInstanceMapping.put(internalClassName, instances);
// classRefQueueMapping.put(internalClassName, refQueue);
// }
// instances.add(new MyWeakRef(instance, refQueue));
// determine current field values
// final Map> fieldValues = new LinkedHashMap>();
//
// Field f;
// Object v;
// for (Map.Entry entry : observedFields.entrySet()) {
// try {
// f = entry.getValue();
// if (Modifier.isStatic(f.getModifiers())) {
// v = f.get(null);
// fieldValues.put(entry.getKey(), new WeakReference(v));
//
// // TODO remove final fields from map of observed fields
// if (v != null) {
// // as PUTFIELD only access public (and protected) fields we can also add a corresponding GETFIELD entry to the log
// // to know the instances stored in the static fields
//
// final Object receiver = instance instanceof Class ? instance
// : instance.getClass();
//
// Capturer.capture(captureId, receiver, CaptureLog.GETSTATIC,
// Type.getDescriptor(f.getType()),
// new Object[] { f.getName() });
// Capturer.enable(captureId, receiver, v);
//
// CLASSES.add((Class>) receiver);
//
// // TODO proper capture id handling
// captureId--;
// }
// } else {
// // we can't collect instance field values from the class itself
// if (!(instance instanceof Class)) {
// fieldValues.put(entry.getKey(),
// new WeakReference(f.get(instance)));
// }
// }
// } catch (final Exception e) {
// logger.error("class={} field={} fieldOwner={} instance={}",
// new Object[] { internalClassName, entry.getKey(),
// entry.getValue().getDeclaringClass().getName(),
// instance });
//
// logger.error("an error occurred while determining current field values",
// e);
// throw new RuntimeException(e); // TODO better exception type
// }
// }
//
// instanceRecentFieldValuesMapping.put(System.identityHashCode(instance),
// fieldValues);
// }
}
private static Map collectAccessibleFields(Map accessibleFields,
final Class> clazz, final Package childPackage) {
logger.debug("Collecting accessible fields for {}", clazz.getCanonicalName());
if (clazz == null || Object.class.equals(clazz)) {
logger.debug("Cannot get fields for null class");
return new LinkedHashMap<>();
}
Map currentAccessibleFields = new LinkedHashMap<>();
try {
for (Field f : clazz.getDeclaredFields()) {
try {
int modifier = f.getModifiers();
if (Modifier.isPublic(modifier)
|| (Modifier.isProtected(modifier) && (childPackage == null || childPackage.equals(clazz.getPackage())))) {
f.setAccessible(true);
currentAccessibleFields.put(f.getName(), f);
logger.debug("Field {} is accessible", f.getName());
} else {
logger.debug("Field {} is NOT accessible", f.getName());
}
} catch (Throwable t) {
logger.error("Exception caught while looking at field {}: {}", f.getName(), t.toString());
}
// if(! Modifier.isPrivate(modifier) )
// {
// f.setAccessible(true);
// accessibleFields.put(f.getName(), f);
// }
}
} catch (Throwable t) {
logger.error("Exception caught while collecting fields from class {}: {}", clazz.getCanonicalName(), t.toString());
}
logger.debug("Looking at fields of superclass {}", clazz.getSuperclass().getCanonicalName());
Map superFieldMap = collectAccessibleFields(accessibleFields, clazz.getSuperclass(),
clazz.getPackage());
currentAccessibleFields.putAll(superFieldMap);
classFieldsMapping.put(clazz.getName().replace('.', '/'), currentAccessibleFields);
logger.debug("Storing {} field(s) for {}: {}", currentAccessibleFields.size(),
clazz.getCanonicalName(), currentAccessibleFields);
return currentAccessibleFields;
}
private static void cleanUpReferences(final String internalClassName) {
final List> instances = classInstanceMapping.get(internalClassName);
if (instances != null) {
final ReferenceQueue> refQueue = classRefQueueMapping.get(internalClassName);
// clean list of instances from garbagge collected references
Reference> ref;
while ((ref = refQueue.poll()) != null) {
instances.remove(ref);
instanceRecentFieldValuesMapping.remove(((MyWeakRef>) ref).oid);
}
if (instances.isEmpty()) {
classRefQueueMapping.remove(internalClassName);
classInstanceMapping.remove(internalClassName);
classFieldsMapping.remove(internalClassName);
}
}
}
synchronized public static void notifyModification(Object receiver, final int captureId,
final String internalClassName, final String fieldName, final String desc) {
cleanUpReferences(internalClassName);
if (!Capturer.isCapturing()) {
return;
}
Map observedFields = classFieldsMapping.get(internalClassName);
if (observedFields == null) {
// determine observable fields
populateFieldMap(internalClassName, fieldName);
}
try {
final Map fields = classFieldsMapping.get(internalClassName);
if (fields == null) {
logger.error("Fields map for class {} should not be null",
internalClassName);
throw new IllegalStateException("Fields map for class "
+ internalClassName + " should not be null");
}
if (fields.isEmpty()) {
logger.debug(classFieldsMapping.toString());
logger.debug("Done modify - no fields");
return;
}
final Field targetField = fields.get(fieldName);
if (targetField == null) {
// happens if field is private
logger.debug("Could not find field {} for class {}", fieldName,
internalClassName);
} else {
final Object currentValue;
if (Modifier.isStatic(targetField.getModifiers())) {
currentValue = targetField.get(null);
} else {
// we can't get instance field values from the class itself
if (receiver instanceof Class) {
return;
}
if(!registeredObjects.contains(System.identityHashCode(receiver))) {
return;
}
currentValue = targetField.get(receiver);
}
logger.debug("Notify modification of field {} on class {}", fieldName, internalClassName);
if(Modifier.isStatic(targetField.getModifiers())) {
Capturer.capture(captureId, receiver,
CaptureLog.PUTSTATIC, desc,
new Object[] { fieldName,
currentValue });
Capturer.enable(captureId, receiver,
CaptureLog.RETURN_TYPE_VOID);
} else {
Capturer.capture(captureId, receiver,
CaptureLog.PUTFIELD, desc,
new Object[] { fieldName,
currentValue });
Capturer.enable(captureId, receiver,
CaptureLog.RETURN_TYPE_VOID);
}
//
// if (instance instanceof Class) {
// // TODO error?
// final WeakReference> recentRef = recentFieldValues.get(fieldName);
// final Object recentValue = recentRef.get();
//
// if ((recentValue != currentValue)
// || (recentValue != null && !recentValue.equals(currentValue))) {
// Capturer.capture(captureId, instance,
// CaptureLog.PUTSTATIC, desc,
// new Object[] { fieldName,
// currentValue });
// Capturer.enable(captureId, instance,
// CaptureLog.RETURN_TYPE_VOID);
//
// // as PUTFIELD only access public fields we can also add a corresponding GETFIELD entry to the log
// Capturer.capture(captureId + 1, instance,
// CaptureLog.GETSTATIC, desc,
// new Object[] { fieldName });
// Capturer.enable(captureId + 1, instance,
// currentValue);
//
// break; // there can only be on field access at a time
// }
// } else {
// final WeakReference> recentRef = recentFieldValues.get(fieldName);
// final Object recentValue = recentRef.get();
//
// if (recentValue != currentValue) //|| (recentValue != null && ! recentValue.equals(currentValue)))
// {
// Capturer.capture(captureId, instance,
// CaptureLog.PUTFIELD, desc,
// new Object[] { fieldName,
// currentValue });
// Capturer.enable(captureId, instance,
// CaptureLog.RETURN_TYPE_VOID);
//
// // as PUTFIELD only access public fields we can also add a corresponding GETFIELD entry to the log
// Capturer.capture(captureId + 1, instance,
// CaptureLog.GETFIELD, desc,
// new Object[] { fieldName });
// Capturer.enable(captureId + 1, instance,
// currentValue);
//
// break; // there can only be on field access at a time
// }
// }
//
// } catch (final Exception e) {
// logger.error("an error occurred while comparing field values for class {}",
// internalClassName, e);
// throw new RuntimeException(e); // TODO better exception type
// }
//
// }
//}
//}
// }
// } else {
// logger.debug("No observed fields for class {} [MODIFY]", internalClassName);
// }
}
} catch (final Throwable e) {
logger.error("an error occurred while comparing field values for class {}",
internalClassName, e);
throw new RuntimeException(e); // TODO better exception type
}
logger.debug("Done field write");
}
private static void populateFieldMap(String internalClassName, String fieldName) {
Map observedFields = new LinkedHashMap<>();
try {
Class> clazz = Class.forName(internalClassName.replace('/', '.'), true, carvingClassLoader);
collectAccessibleFields(observedFields, clazz, null);
if(!observedFields.containsKey(fieldName)) {
logger.debug("Field {} not observed", fieldName);
return;
}
logger.debug("Trying to get field {} for class {}", fieldName, internalClassName);
if(Modifier.isStatic(observedFields.get(fieldName).getModifiers())) {
register(clazz);
}
// if (observedFields.isEmpty()) {
// logger.debug("Class {} has no observable fields", internalClassName);
// classFieldsMapping.put(internalClassName, Collections.EMPTY_MAP);
// } else {
// logger.debug("Setting field map for class "+internalClassName +" to "+observedFields);
// classFieldsMapping.put(internalClassName, observedFields);
// }
} catch(ClassNotFoundException e) {
logger.info("Error loading class "+internalClassName+": "+e);
} catch(Throwable e) {
logger.debug("Carving classloader: "+carvingClassLoader);
logger.info("TODO Error loading class "+internalClassName+": "+e);
logger.info("TODO Error loading class "+internalClassName+": "+e.getCause());
for(StackTraceElement elem : e.getStackTrace()) {
logger.debug(elem.toString());
}
if(e.getCause() != null)
for(StackTraceElement elem : e.getCause().getStackTrace()) {
logger.debug(elem.toString());
}
}
}
synchronized public static void notifyReadAccess(Object receiver, final int captureId,
final String internalClassName, final String fieldName, final String desc) {
cleanUpReferences(internalClassName);
if (!Capturer.isCapturing()) {
return;
}
Map observedFields = classFieldsMapping.get(internalClassName);
if (observedFields == null) {
// determine observable fields
logger.debug("Haven't seen {} {} yet", internalClassName, fieldName);
populateFieldMap(internalClassName, fieldName);
}
try {
final Map fields = classFieldsMapping.get(internalClassName);
if (fields == null) {
logger.error("Fields map for class {} should not be null",
internalClassName);
throw new IllegalStateException("Fields map for class "
+ internalClassName + " should not be null");
}
if (fields.isEmpty()) {
logger.debug("Done read - no fields");
return;
}
final Field targetField = fields.get(fieldName);
if (targetField == null) {
// happens if field is private
logger.debug("Could not find field {} for class {}", fieldName,
internalClassName);
return;
}
final Object currentValue;
if (Modifier.isStatic(targetField.getModifiers())) {
currentValue = targetField.get(null);
} else {
// we can't get instance field values from the class itself
if (receiver instanceof Class) {
logger.debug("WTF read");
return;
}
if(!registeredObjects.contains(System.identityHashCode(receiver))) {
return;
}
currentValue = targetField.get(receiver);
}
logger.debug("Notify read access {}, {}, {}", internalClassName, fieldName, receiver == null ? "null" : receiver.getClass());
if (receiver instanceof Class) {
Capturer.capture(captureId, receiver,
CaptureLog.GETSTATIC, desc,
new Object[] { fieldName });
Capturer.enable(captureId, receiver, currentValue);
} else if(receiver == null) {
Capturer.capture(captureId, targetField.getDeclaringClass(),
CaptureLog.GETSTATIC, desc,
new Object[] { fieldName });
Capturer.enable(captureId, targetField.getDeclaringClass(), currentValue);
} else {
Capturer.capture(captureId, receiver,
CaptureLog.GETFIELD, desc,
new Object[] { fieldName });
Capturer.enable(captureId, receiver, currentValue);
}
logger.debug("Done field read");
} catch (final Throwable e) {
logger.error("an error occurred while comparing field values for class {}",
internalClassName, e);
throw new RuntimeException(e); // TODO better exception type
}
}
synchronized public static void clear() {
classInstanceMapping.clear();
classFieldsMapping.clear();
instanceRecentFieldValuesMapping.clear();
classRefQueueMapping.clear();
registeredObjects.clear();
captureId = Integer.MAX_VALUE;
}
synchronized public static void restoreForegoingGETSTATIC() {
for (Class> c : CLASSES) {
register(c);
}
}
public static String classFieldsMappinString() {
final StringBuilder builder = new StringBuilder();
String c;
Map fieldMap;
int fieldModifiers;
for (Map.Entry> entry : classFieldsMapping.entrySet()) {
c = entry.getKey();
fieldMap = entry.getValue();
for (Map.Entry entry2 : fieldMap.entrySet()) {
fieldModifiers = entry2.getValue().getModifiers();
builder.append(c).append('.').append(entry2.getKey()).append(" public="
+ Modifier.isPublic(fieldModifiers)).append(" private="
+ Modifier.isPrivate(fieldModifiers)).append(" protected="
+ Modifier.isProtected(fieldModifiers)).append('\n');
}
}
return builder.toString();
}
public static boolean isKnownObject(Object obj) {
return registeredObjects.contains(obj);
}
public static class MyWeakRef extends WeakReference {
public final int oid;
public MyWeakRef(T referent, ReferenceQueue super T> q) {
super(referent, q);
oid = System.identityHashCode(referent);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy