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.
438 lines
12 KiB
438 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.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.editor.*;
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
|
|
/**
|
|
* Maintains all BLOAT data structures as if they were meant to reside in a
|
|
* persistent store. As a result, it keeps every piece of BLOAT data around
|
|
* because it might be needed in the future. No fancing cache maintainence is
|
|
* performed. Because we are going for maximum information we take the closure
|
|
* of classes when working with the class hierarchy.
|
|
*/
|
|
public class PersistentBloatContext extends BloatContext {
|
|
|
|
protected final ClassHierarchy hierarchy;
|
|
|
|
protected Map classInfos; // Maps Strings to ClassInfos
|
|
|
|
protected Map methodInfos; // Maps MemberRefs to MethodInfos
|
|
|
|
protected Map fieldInfos; // Maps MemberRefs to FieldInfos
|
|
|
|
protected Map classEditors; // Maps ClassInfos to ClassEditors
|
|
|
|
protected Map methodEditors; // Maps MethodInfos to MethodEditors
|
|
|
|
protected Map fieldEditors; // Maps MethodInfos to FieldEditors
|
|
|
|
public static boolean DB_COMMIT = false;
|
|
|
|
protected static void comm(final String s) {
|
|
if (PersistentBloatContext.DB_COMMIT || BloatContext.DEBUG) {
|
|
System.out.println(s);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor. Each <tt>BloatContext</tt> stems from a
|
|
* <tt>ClassInfoLoader</tt>. Using the loader it can create an
|
|
* <tt>Editor</tt> and such. Initially, no classes are loaded.
|
|
*/
|
|
public PersistentBloatContext(final ClassInfoLoader loader) {
|
|
this(loader, true);
|
|
}
|
|
|
|
/**
|
|
* Constructor. It is the responsibility of the subclasses to add classes to
|
|
* the hierarchy by calling <tt>addClasses</tt>.
|
|
*
|
|
* @param loader
|
|
* Used to load classes
|
|
* @param closure
|
|
* Do we look for the maximum number of classes?
|
|
*/
|
|
protected PersistentBloatContext(final ClassInfoLoader loader,
|
|
final boolean closure) {
|
|
super(loader);
|
|
BloatContext.db("Creating a new BloatContext");
|
|
|
|
// Create a bunch of the mappings we maintain. Make sure to do
|
|
// this before anything else!
|
|
classInfos = new HashMap();
|
|
methodInfos = new HashMap();
|
|
fieldInfos = new HashMap();
|
|
|
|
classEditors = new HashMap();
|
|
methodEditors = new HashMap();
|
|
fieldEditors = new HashMap();
|
|
|
|
// Have to create an empty class hierarchy then add the classes.
|
|
// There is a strange circular dependence between the hierarchy
|
|
// and the context.
|
|
this.hierarchy = new ClassHierarchy(this, new ArrayList(), closure);
|
|
|
|
}
|
|
|
|
/**
|
|
* Adds a bunch of (names of) classes to the hierarchy.
|
|
*/
|
|
protected void addClasses(final Collection classes) {
|
|
final Iterator iter = classes.iterator();
|
|
while (iter.hasNext()) {
|
|
final String className = (String) iter.next();
|
|
this.hierarchy.addClassNamed(className);
|
|
}
|
|
}
|
|
|
|
public ClassInfo loadClass(String className) throws ClassNotFoundException {
|
|
// Lots of interesting stuff to do here. For the moment, just
|
|
// load the class from the ClassInfoLoader and add it to the
|
|
// hierarchy.
|
|
|
|
className = className.replace('.', '/').intern();
|
|
|
|
// Check the cache of ClassInfos
|
|
ClassInfo info = (ClassInfo) classInfos.get(className);
|
|
|
|
if (info == null) {
|
|
BloatContext.db("BloatContext: Loading class " + className);
|
|
info = loader.loadClass(className);
|
|
hierarchy.addClassNamed(className);
|
|
BloatContext.db("loadClass: " + className + " -> " + info);
|
|
classInfos.put(className, info);
|
|
}
|
|
|
|
return (info);
|
|
}
|
|
|
|
public ClassInfo newClassInfo(final int modifiers, final int classIndex,
|
|
final int superClassIndex, final int[] interfaceIndexes,
|
|
final List constants) {
|
|
|
|
return this.loader.newClass(modifiers, classIndex, superClassIndex,
|
|
interfaceIndexes, constants);
|
|
}
|
|
|
|
public ClassHierarchy getHierarchy() {
|
|
return (this.hierarchy);
|
|
}
|
|
|
|
public ClassEditor newClass(final int modifiers, String className,
|
|
final Type superType, final Type[] interfaces) {
|
|
|
|
final ClassEditor ce = new ClassEditor(this, modifiers, className,
|
|
superType, interfaces);
|
|
final ClassInfo info = ce.classInfo();
|
|
|
|
className = ce.name().intern();
|
|
|
|
BloatContext.db("editClass(ClassInfo): " + className + " -> " + info);
|
|
|
|
classInfos.put(className, info);
|
|
classEditors.put(info, ce);
|
|
|
|
return ce;
|
|
}
|
|
|
|
public ClassEditor editClass(String className)
|
|
throws ClassNotFoundException, ClassFormatException {
|
|
// Only make the name -> classInfo mapping if we edit the class,
|
|
// this way the mapping will be deleted when the ClassEditor is
|
|
// released.
|
|
|
|
className = className.intern();
|
|
|
|
ClassInfo info = (ClassInfo) classInfos.get(className);
|
|
|
|
if (info == null) {
|
|
info = loadClass(className);
|
|
// db("editClass(String): " + className + " -> " + info);
|
|
// classInfos.put(className, info);
|
|
}
|
|
|
|
return (editClass(info));
|
|
}
|
|
|
|
public ClassEditor editClass(final Type classType)
|
|
throws ClassNotFoundException, ClassFormatException {
|
|
return (editClass(classType.className()));
|
|
}
|
|
|
|
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);
|
|
|
|
if (!classInfos.containsValue(info)) {
|
|
final String className = ce.name().intern();
|
|
BloatContext.db("editClass(ClassInfo): " + className + " -> "
|
|
+ info);
|
|
classInfos.put(className, info);
|
|
}
|
|
}
|
|
|
|
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(methods[i]);
|
|
}
|
|
|
|
} catch (final ClassNotFoundException ex1) {
|
|
} catch (final ClassFormatException ex2) {
|
|
}
|
|
|
|
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) {
|
|
me = new MethodEditor(editClass(info.declaringClass()), info);
|
|
methodEditors.put(info, me);
|
|
BloatContext
|
|
.db("Creating a new MethodEditor for " + me.memberRef());
|
|
}
|
|
|
|
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]);
|
|
}
|
|
} 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);
|
|
|
|
if (fe == null) {
|
|
fe = new FieldEditor(editClass(info.declaringClass()), info);
|
|
fieldEditors.put(info, fe);
|
|
BloatContext.db("Creating a new FieldEditor for "
|
|
+ fe.nameAndType());
|
|
}
|
|
|
|
return (fe);
|
|
}
|
|
|
|
public void release(final ClassInfo info) {
|
|
// Since we keep around all data, do nothing
|
|
}
|
|
|
|
public void release(final ClassEditor ce) {
|
|
// Since we keep around all data, do nothing
|
|
}
|
|
|
|
public void release(final MethodInfo info) {
|
|
// Since we keep around all data, do nothing
|
|
}
|
|
|
|
public void release(final FieldInfo info) {
|
|
// Since we keep around all data, do nothing
|
|
}
|
|
|
|
/**
|
|
* Classes that are ignored are not committed.
|
|
*
|
|
* @see #ignoreClass(Type)
|
|
*/
|
|
public void commit(final ClassInfo info) {
|
|
final Type type = Type.getType("L" + info.name() + ";");
|
|
if (ignoreClass(type)) {
|
|
return;
|
|
}
|
|
|
|
final ClassEditor ce = editClass(info);
|
|
|
|
// Commit all of the class's methods and fields
|
|
final MethodInfo[] methods = ce.methods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
commit(methods[i]);
|
|
}
|
|
|
|
final FieldInfo[] fields = ce.fields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
commit(fields[i]);
|
|
}
|
|
|
|
ce.commit();
|
|
|
|
ce.setDirty(false);
|
|
release(info);
|
|
}
|
|
|
|
public void commit(final MethodInfo info) {
|
|
final MethodEditor me = editMethod(info);
|
|
me.commit();
|
|
|
|
// We make the method's class dirty so it, too, will be committed
|
|
me.declaringClass().setDirty(true);
|
|
me.setDirty(false);
|
|
release(info);
|
|
}
|
|
|
|
public void commit(final FieldInfo info) {
|
|
final FieldEditor fe = editField(info);
|
|
fe.commit();
|
|
|
|
// We make the method's class dirty so it, too, will be committed
|
|
fe.declaringClass().setDirty(true);
|
|
fe.setDirty(false);
|
|
release(info);
|
|
}
|
|
|
|
public void commit() {
|
|
Object[] array = fieldEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final FieldEditor fe = (FieldEditor) array[i];
|
|
if (!ignoreField(fe.memberRef())) {
|
|
commit(fe.fieldInfo());
|
|
}
|
|
}
|
|
|
|
array = methodEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final MethodEditor me = (MethodEditor) array[i];
|
|
if (!ignoreMethod(me.memberRef())) {
|
|
commit(me.methodInfo());
|
|
}
|
|
}
|
|
|
|
array = classEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final ClassEditor ce = (ClassEditor) array[i];
|
|
if (!ignoreClass(ce.type())) {
|
|
commit(ce.classInfo());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void commitDirty() {
|
|
PersistentBloatContext.comm("Committing dirty data");
|
|
|
|
// Commit all dirty fields
|
|
Object[] array = this.fieldEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final FieldEditor fe = (FieldEditor) array[i];
|
|
if (fe.isDirty() && !ignoreField(fe.memberRef())) {
|
|
PersistentBloatContext.comm(" Committing field: "
|
|
+ fe.declaringClass().name() + "." + fe.name());
|
|
commit(fe.fieldInfo());
|
|
}
|
|
}
|
|
|
|
// Commit all dirty methods
|
|
array = this.methodEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final MethodEditor me = (MethodEditor) array[i];
|
|
if (me.isDirty() && !ignoreMethod(me.memberRef())) {
|
|
PersistentBloatContext.comm(" Committing method: "
|
|
+ me.declaringClass().name() + "." + me.name()
|
|
+ me.type());
|
|
commit(me.methodInfo());
|
|
}
|
|
}
|
|
|
|
// Commit all dirty classes
|
|
array = this.classEditors.values().toArray();
|
|
for (int i = 0; i < array.length; i++) {
|
|
final ClassEditor ce = (ClassEditor) array[i];
|
|
if (ce.isDirty() && !ignoreClass(ce.type())) {
|
|
PersistentBloatContext.comm(" Committing class: " + ce.name());
|
|
commit(ce.classInfo());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|