Mirror of the BLOAT repository https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/context/CachingBloatContext.java

461 lines
12 KiB

/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.context;
import java.io.*;
import java.util.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* Does a lot of the same stuff as <tt>PersistentBloatContext</tt> except that
* it manages the chaches of BLOAT objects. For example, when a
* <tt>MethodEditor</tt> is no longer needed, it is removed from the cache if
* it is not dirty. This context is meant to used in volatile memory.
*/
public class CachingBloatContext extends PersistentBloatContext {
// Keep track of reference counts in a manner reminiscent of the old
// Editor class.
protected Map classRC;
protected Map methodRC;
protected Map fieldRC;
/**
* Constructor.
*
* @param loader
* Used to load classes
* @param classes
* Some initial classes in the context
* @param closure
* Do we look for the maximum number of classes?
*/
public CachingBloatContext(final ClassInfoLoader loader,
final Collection classes, final boolean closure) {
super(loader, closure);
classRC = new HashMap();
methodRC = new HashMap();
fieldRC = new HashMap();
addClasses(classes);
}
public ClassEditor newClass(final int modifiers, final String className,
final Type superType, final Type[] interfaces) {
final ClassEditor ce = super.newClass(modifiers, className, superType,
interfaces);
final ClassInfo info = ce.classInfo();
classRC.put(info, new Integer(1));
return ce;
}
public ClassEditor editClass(final ClassInfo info) {
// Check the cache
ClassEditor ce = (ClassEditor) classEditors.get(info);
if (ce == null) {
ce = new ClassEditor(this, info);
classEditors.put(info, ce);
classRC.put(info, new Integer(1));
if (!classInfos.containsValue(info)) {
final String className = ce.name().intern();
BloatContext.db("editClass(ClassInfo): " + className + " -> "
+ info);
classInfos.put(className, info);
}
} else {
final Integer rc = (Integer) classRC.get(info);
classRC.put(info, new Integer(rc.intValue() + 1));
}
return (ce);
}
public MethodEditor editMethod(final MemberRef method)
throws NoSuchMethodException {
// Check the MethodInfo cache
final MethodInfo info = (MethodInfo) methodInfos.get(method);
if (info == null) {
// Groan, we have to do this the HARD way.
BloatContext.db("Creating a new MethodEditor for " + method);
final NameAndType nat = method.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(method.declaringClass());
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
final MethodEditor me = editMethod(methods[i]);
if (me.name().equals(name) && me.type().equals(type)) {
// The call to editMethod should have already handled
// the
// methodEditors mapping, but we still need to do
// methodInfos.
methodInfos.put(method, methods[i]);
release(ce.classInfo());
return (me);
}
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
throw new NoSuchMethodException(method.toString() + "("
+ ex1.getMessage() + ")");
} catch (final ClassFormatException ex2) {
throw new NoSuchMethodException(method.toString() + "("
+ ex2.getMessage() + ")");
}
throw new NoSuchMethodException(method.toString());
}
return (editMethod(info));
}
public MethodEditor editMethod(final MethodInfo info) {
// Check methodEditors cache
MethodEditor me = (MethodEditor) methodEditors.get(info);
if (me == null) {
final ClassInfo classInfo = info.declaringClass();
me = new MethodEditor(editClass(classInfo), info);
release(classInfo);
methodEditors.put(info, me);
methodRC.put(info, new Integer(1));
BloatContext
.db("Creating a new MethodEditor for " + me.memberRef());
} else {
final Integer rc = (Integer) methodRC.get(info);
methodRC.put(info, new Integer(rc.intValue() + 1));
}
return (me);
}
public FieldEditor editField(final MemberRef field)
throws NoSuchFieldException {
// Just like we had to do with methods
final FieldInfo info = (FieldInfo) fieldInfos.get(field);
if (info == null) {
final NameAndType nat = field.nameAndType();
final String name = nat.name();
final Type type = nat.type();
try {
final ClassEditor ce = editClass(field.declaringClass());
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = editField(fields[i]);
if (fe.name().equals(name) && fe.type().equals(type)) {
fieldInfos.put(field, fields[i]);
release(ce.classInfo());
return (fe);
}
release(fields[i]);
}
release(ce.classInfo());
} catch (final ClassNotFoundException ex1) {
} catch (final ClassFormatException ex2) {
}
throw new NoSuchFieldException(field.toString());
}
return (editField(info));
}
public FieldEditor editField(final FieldInfo info) {
// Check the cache
FieldEditor fe = (FieldEditor) fieldEditors.get(info);
BloatContext.db("Editing " + info);
if (fe == null) {
final ClassInfo classInfo = info.declaringClass();
fe = new FieldEditor(editClass(classInfo), info);
release(classInfo);
fieldEditors.put(info, fe);
fieldRC.put(info, new Integer(0));
BloatContext.db("Creating a new FieldEditor for "
+ fe.nameAndType());
} else {
final Integer rc = (Integer) fieldRC.get(info);
fieldRC.put(info, new Integer(rc.intValue() + 1));
}
return (fe);
}
public void release(final ClassInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
// Not done yet;
classRC.put(info, new Integer(rc.intValue() - 1));
return;
}
ClassEditor ce = (ClassEditor) classEditors.get(info);
if ((ce != null) && ce.isDirty()) {
return;
}
// We're done with this class, remove all traces of it
ce = (ClassEditor) classEditors.remove(info);
classRC.remove(info);
classEditors.remove(info);
final Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final String name = (String) iter.next();
final ClassInfo info2 = (ClassInfo) classInfos.get(name);
if (info2 == info) {
BloatContext.db("Removing ClassInfo: " + name + " -> " + info2);
classInfos.remove(name);
break;
}
}
if (ce != null) {
// Remove all of the class's fields and methods also
final MethodInfo[] methods = ce.methods();
for (int i = 0; i < methods.length; i++) {
release(methods[i]);
}
final FieldInfo[] fields = ce.fields();
for (int i = 0; i < fields.length; i++) {
release(fields[i]);
}
}
}
public void release(final MethodInfo info) {
final Integer rc = (Integer) classRC.get(info);
if ((rc != null) && (rc.intValue() > 1)) {
methodRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final MethodEditor me = (MethodEditor) methodEditors.get(info);
// We should keep dirty methods around. My original thought was
// that if we committed dirty methods when they were released, we
// risk having MethodEditors editing different versions of the
// same method. So, if we don't release dirty methods, we'll only
// have ONE MethodEditor.
if ((me != null) && me.isDirty()) {
return;
}
// We're done with this method, remove all traces of it
methodRC.remove(info);
methodEditors.remove(info);
final Iterator iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final MethodInfo info2 = (MethodInfo) methodInfos.get(ref);
if (info2 == info) {
methodInfos.remove(ref);
break;
}
}
}
public void release(final FieldInfo info) {
final Integer rc = (Integer) fieldRC.get(info);
BloatContext.db("Releasing " + info);
if ((rc != null) && (rc.intValue() > 1)) {
fieldRC.put(info, new Integer(rc.intValue() - 1));
return;
}
final FieldEditor fe = (FieldEditor) fieldEditors.get(info);
if ((fe != null) && fe.isDirty()) {
return;
}
// We're done with this field, remove all traces of it
fieldRC.remove(info);
fieldEditors.remove(info);
final Iterator iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final MemberRef ref = (MemberRef) iter.next();
final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref);
if (info2 == info) {
fieldInfos.remove(ref);
break;
}
}
}
public void commit(final ClassInfo info) {
super.commit(info);
classEditors.remove(info);
classRC.remove(info);
}
public void commit(final MethodInfo info) {
super.commit(info);
methodEditors.remove(info);
methodRC.remove(info);
}
public void commit(final FieldInfo info) {
super.commit(info);
fieldEditors.remove(info);
fieldRC.remove(info);
}
public void commit() {
Iterator iter = fieldEditors.values().iterator();
while (iter.hasNext()) {
final FieldEditor fe = (FieldEditor) iter.next();
commit(fe.fieldInfo());
}
iter = methodEditors.values().iterator();
while (iter.hasNext()) {
final MethodEditor me = (MethodEditor) iter.next();
commit(me.methodInfo());
}
iter = classEditors.values().iterator();
while (iter.hasNext()) {
final ClassEditor ce = (ClassEditor) iter.next();
commit(ce.classInfo());
}
}
/**
* Return a textual description of all of the caches. Useful if we run out
* of memory.
*/
public String toString() {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw, true);
pw.println("Context of caches in CachingBloatContext...");
pw.println(" Class Infos");
Iterator iter = classInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classInfos.get(key));
}
pw.println(" Class Editors");
iter = classEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classEditors.get(key));
}
pw.println(" Class RC");
iter = classRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + classRC.get(key));
}
pw.println(" Method Infos");
iter = methodInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodInfos.get(key));
}
pw.println(" Method Editors");
iter = methodEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodEditors.get(key));
}
pw.println(" Method RC");
iter = methodRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + methodRC.get(key));
}
pw.println(" Field Infos");
iter = fieldInfos.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldInfos.get(key));
}
pw.println(" Field Editors");
iter = fieldEditors.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldEditors.get(key));
}
pw.println(" Field RC");
iter = fieldRC.keySet().iterator();
while (iter.hasNext()) {
final Object key = iter.next();
pw.println(" " + key + " -> " + fieldRC.get(key));
}
return (sw.toString());
}
}