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.
551 lines
14 KiB
551 lines
14 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.editor;
|
|
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
import EDU.purdue.cs.bloat.util.*;
|
|
|
|
/**
|
|
* A ClassEditor provides finer-grain access to a class than a CLassInfo object
|
|
* does. A ClassEditor takes a ClassInfo and extracts the class's constant pool,
|
|
* type, super class type, and the types of its interfaces. When editing is
|
|
* finished, changes are committed with the commit method.
|
|
*
|
|
* @see ClassInfo
|
|
* @see MethodEditor
|
|
*
|
|
* @author Nate Nystrom (<a
|
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
|
|
*/
|
|
public class ClassEditor {
|
|
public static boolean DEBUG = Boolean.getBoolean("ClassEditor.DEBUG");
|
|
|
|
private ConstantPool constants; // A copy of the constant pool of the class
|
|
|
|
// being edited.
|
|
private ClassInfo classInfo; // (A representation of) the class being
|
|
// edited
|
|
|
|
private Type type; // An index into constant pool (descriptors) that
|
|
|
|
private Type superclass; // specifies the class, superclass, and
|
|
// interfaces
|
|
|
|
private Type[] interfaces; // of the class being edited.
|
|
|
|
private EditorContext context; // Use to edit classes and methods
|
|
|
|
private boolean dirty; // Has the class been modified?
|
|
|
|
/**
|
|
* Constructor. Create a new ClassEditor based on information in a ClassInfo
|
|
* object. It extracts the class's constant pool, and the types of the
|
|
* class, its superclass, and any interfaces it implements.
|
|
*
|
|
* @param context
|
|
* The <tt>EditorContext</tt> used to edit fields and methods.
|
|
* @param classInfo
|
|
* The ClassInfo structure of the class to edit.
|
|
*
|
|
* @see EDU.purdue.cs.bloat.reflect.ClassInfo
|
|
* @see ConstantPool
|
|
* @see Type
|
|
*/
|
|
public ClassEditor(final EditorContext context, final ClassInfo classInfo) {
|
|
this.context = context;
|
|
this.classInfo = classInfo;
|
|
this.dirty = false;
|
|
|
|
// Extract the constant pool from the ClassInfo
|
|
constants = new ConstantPool(classInfo.constants());
|
|
|
|
int index;
|
|
|
|
// Load information (such as the indices of the class, superclass,
|
|
// and the interfaces) about the class being edited from its
|
|
// constant pool.
|
|
|
|
index = classInfo.classIndex();
|
|
type = (Type) constants.constantAt(index);
|
|
|
|
index = classInfo.superclassIndex();
|
|
superclass = (Type) constants.constantAt(index);
|
|
|
|
final int ifs[] = classInfo.interfaceIndices();
|
|
interfaces = new Type[ifs.length];
|
|
|
|
for (int i = 0; i < ifs.length; i++) {
|
|
interfaces[i] = (Type) constants.constantAt(ifs[i]);
|
|
}
|
|
|
|
if (ClassEditor.DEBUG) {
|
|
System.out.println("Editing class " + type);
|
|
}
|
|
|
|
this.setDirty(false);
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>ClassEditor</code> for editing a class (or
|
|
* interface) from scratch. This constructor should not be invoked direcly.
|
|
* Use {@link EditorContext#newClass(int, String, Type, Type[])} instead.
|
|
*/
|
|
public ClassEditor(final EditorContext context, final int modifiers,
|
|
final String className, Type superType, Type[] interfaces) {
|
|
|
|
if (className == null) {
|
|
final String s = "Cannot have a null class name";
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
|
|
if (superType == null) {
|
|
superType = Type.OBJECT;
|
|
}
|
|
|
|
if (interfaces == null) {
|
|
interfaces = new Type[0];
|
|
}
|
|
|
|
if (ClassEditor.DEBUG) {
|
|
System.out.println("Creating new class " + className + " extends "
|
|
+ superType.className());
|
|
}
|
|
|
|
this.context = context;
|
|
this.superclass = superType;
|
|
this.interfaces = interfaces;
|
|
|
|
final ConstantPool cp = new ConstantPool();
|
|
this.constants = cp;
|
|
|
|
this.type = Type.getType(Type.classDescriptor(className));
|
|
final int classNameIndex = cp.getClassIndex(this.type);
|
|
final int superTypeIndex = cp.getClassIndex(superType);
|
|
final int[] interfaceIndexes = new int[interfaces.length];
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
interfaceIndexes[i] = cp.getClassIndex(interfaces[i]);
|
|
}
|
|
|
|
this.classInfo = context.newClassInfo(modifiers, classNameIndex,
|
|
superTypeIndex, interfaceIndexes, cp.getConstantsList());
|
|
|
|
this.dirty = true;
|
|
}
|
|
|
|
/**
|
|
* Returns <tt>true</tt> if the class has been modified.
|
|
*/
|
|
public boolean isDirty() {
|
|
return (this.dirty);
|
|
}
|
|
|
|
/**
|
|
* Sets this class's dirty flag. The dirty flag is <tt>true</tt> if the
|
|
* class has been modified.
|
|
*/
|
|
public void setDirty(final boolean dirty) {
|
|
this.dirty = dirty;
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the class represented by this <tt>ClassEditor</tt>.
|
|
*/
|
|
public String name() {
|
|
return (this.classInfo().name());
|
|
}
|
|
|
|
/**
|
|
* Obtain the <tt>EditorContext</tt> for this ClassEditor.
|
|
*/
|
|
public EditorContext context() {
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Get the ClassInfo object representing the class that being edited.
|
|
*/
|
|
public ClassInfo classInfo() {
|
|
return classInfo;
|
|
}
|
|
|
|
public boolean isPublic() {
|
|
return (classInfo.modifiers() & Modifiers.PUBLIC) != 0;
|
|
}
|
|
|
|
public boolean isPrivate() {
|
|
return (classInfo.modifiers() & Modifiers.PRIVATE) != 0;
|
|
}
|
|
|
|
public boolean isProtected() {
|
|
return (classInfo.modifiers() & Modifiers.PROTECTED) != 0;
|
|
}
|
|
|
|
public boolean isStatic() {
|
|
return (classInfo.modifiers() & Modifiers.STATIC) != 0;
|
|
}
|
|
|
|
public boolean isFinal() {
|
|
return (classInfo.modifiers() & Modifiers.FINAL) != 0;
|
|
}
|
|
|
|
public boolean isSuper() {
|
|
return (classInfo.modifiers() & Modifiers.SUPER) != 0;
|
|
}
|
|
|
|
public boolean isAbstract() {
|
|
return (classInfo.modifiers() & Modifiers.ABSTRACT) != 0;
|
|
}
|
|
|
|
public boolean isInterface() {
|
|
return (classInfo.modifiers() & Modifiers.INTERFACE) != 0;
|
|
}
|
|
|
|
public void setPublic(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.PUBLIC;
|
|
} else {
|
|
modifiers &= ~Modifiers.PUBLIC;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setPrivate(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.PRIVATE;
|
|
} else {
|
|
modifiers &= ~Modifiers.PRIVATE;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setProtected(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.PROTECTED;
|
|
} else {
|
|
modifiers &= ~Modifiers.PROTECTED;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setStatic(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.STATIC;
|
|
} else {
|
|
modifiers &= ~Modifiers.STATIC;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setFinal(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.FINAL;
|
|
} else {
|
|
modifiers &= ~Modifiers.FINAL;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setSuper(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.SUPER;
|
|
} else {
|
|
modifiers &= ~Modifiers.SUPER;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setAbstract(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.ABSTRACT;
|
|
} else {
|
|
modifiers &= ~Modifiers.ABSTRACT;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
public void setInterface(final boolean flag) {
|
|
int modifiers = classInfo.modifiers();
|
|
|
|
if (flag) {
|
|
modifiers |= Modifiers.INTERFACE;
|
|
} else {
|
|
modifiers &= ~Modifiers.INTERFACE;
|
|
}
|
|
|
|
classInfo.setModifiers(modifiers);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Sets the Type (descriptor) object for the class.
|
|
*
|
|
* @param type
|
|
* A Type.
|
|
*/
|
|
public void setType(final Type type) {
|
|
this.type = type;
|
|
Assert.isTrue(type.isObject(), "Cannot set class type to " + type);
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Returns the Type (descriptor) for the class.
|
|
*/
|
|
public Type type() {
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* Returns a Type object for the class's superclass.
|
|
*/
|
|
public Type superclass() {
|
|
return superclass;
|
|
}
|
|
|
|
/**
|
|
* Adds an interface of the given class to the set of interfaces that the
|
|
* class implements.
|
|
*
|
|
* @throws IllegalArgumentException <code>interfaceClass</code> is not an
|
|
* interface
|
|
*/
|
|
public void addInterface(final Class interfaceClass) {
|
|
if (!interfaceClass.isInterface()) {
|
|
final String s = "Cannot add non-interface type: "
|
|
+ interfaceClass.getName();
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
|
|
addInterface(Type.getType(interfaceClass));
|
|
}
|
|
|
|
/**
|
|
* Adds an interface of a given Type to the set of interfaces that the class
|
|
* implements.
|
|
*/
|
|
public void addInterface(final Type interfaceType) {
|
|
// // The interface must have an index in the constant pool
|
|
// this.constants().getClassIndex(interfaceType);
|
|
|
|
final Type[] interfaces = new Type[this.interfaces.length + 1];
|
|
for (int i = 0; i < this.interfaces.length; i++) {
|
|
interfaces[i] = this.interfaces[i];
|
|
}
|
|
interfaces[interfaces.length - 1] = interfaceType;
|
|
this.setInterfaces(interfaces);
|
|
}
|
|
|
|
/**
|
|
* Returns the interfaces the class implements.
|
|
*
|
|
* @param interfaces
|
|
* An array of Types.
|
|
*/
|
|
public void setInterfaces(final Type[] interfaces) {
|
|
this.interfaces = interfaces;
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Returns the interfaces the class implements.
|
|
*/
|
|
public Type[] interfaces() {
|
|
return interfaces;
|
|
}
|
|
|
|
/**
|
|
* Returns the modifiers of the class. The values correspond to the
|
|
* constants in the <tt>Modifiers</tt> class.
|
|
*
|
|
* @return A bit vector of modifier flags for the class.
|
|
* @see Modifiers
|
|
*/
|
|
public int modifiers() {
|
|
return classInfo.modifiers();
|
|
}
|
|
|
|
/**
|
|
* Returns an array of <tt>FieldInfo</tt> structures for each field in the
|
|
* class.
|
|
*/
|
|
public FieldInfo[] fields() {
|
|
return classInfo.fields();
|
|
}
|
|
|
|
/**
|
|
* Returns an array of MethodInfo structures for each method in the class.
|
|
*/
|
|
public MethodInfo[] methods() {
|
|
return classInfo.methods();
|
|
}
|
|
|
|
/**
|
|
* Returns the constant pool for the class.
|
|
*/
|
|
public ConstantPool constants() {
|
|
return constants;
|
|
}
|
|
|
|
/**
|
|
* Commit any changes to the class since creation time. Note that committal
|
|
* will occur regardless of whether or not the class is dirty.
|
|
*/
|
|
public void commit() {
|
|
commitOnly(null, null);
|
|
}
|
|
|
|
/**
|
|
* Commits only certain methods and fields. Note that committal will occur
|
|
* regardless of whether or not the class is dirty.
|
|
*
|
|
* @param methods
|
|
* Methods (<tt>MethodInfo</tt>s) to commit. If <tt>null</tt>,
|
|
* all methods are committed.
|
|
* @param fields
|
|
* Fields (<tt>FieldInfo</tt>s) to commit. If <tt>null</tt>,
|
|
* all fields are committed.
|
|
*/
|
|
public void commitOnly(final Set methods, final Set fields) {
|
|
classInfo.setClassIndex(constants.addConstant(Constant.CLASS, type));
|
|
classInfo.setSuperclassIndex(constants.addConstant(Constant.CLASS,
|
|
superclass));
|
|
|
|
final int ifs[] = new int[interfaces.length];
|
|
|
|
for (int i = 0; i < ifs.length; i++) {
|
|
ifs[i] = constants.addConstant(Constant.CLASS, interfaces[i]);
|
|
}
|
|
|
|
classInfo.setInterfaceIndices(ifs);
|
|
|
|
classInfo.setConstants(constants.constants());
|
|
|
|
classInfo.commitOnly(methods, fields);
|
|
|
|
// This class is no longer dirty
|
|
this.setDirty(false);
|
|
}
|
|
|
|
/**
|
|
* This class is visited by an <tt>EditorVisitor</tt>. First, this
|
|
* <tt>ClassEditor</tt> itself is visited. Then, all of this class's
|
|
* fields (<tt>FieldEditor</tt>s) are visited. Finally, each of this
|
|
* class's methods (<tt>MethodEditor</tt>s) are visited. Constructors
|
|
* are visited before regular methods.
|
|
*/
|
|
public void visit(final EditorVisitor visitor) {
|
|
// First visit ourself
|
|
visitor.visitClassEditor(this);
|
|
|
|
final EditorContext context = this.context();
|
|
|
|
// Visit each field
|
|
final FieldInfo[] fields = this.fields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
final FieldEditor fieldEditor = context.editField(fields[i]);
|
|
visitor.visitFieldEditor(fieldEditor);
|
|
context.release(fields[i]);
|
|
}
|
|
|
|
// Visit each method
|
|
final ArrayList regularMethods = new ArrayList();
|
|
final MethodInfo[] methods = this.methods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
final MethodEditor methodEditor = context.editMethod(methods[i]);
|
|
|
|
if (methodEditor.name().charAt(0) != '<') {
|
|
regularMethods.add(methods[i]);
|
|
|
|
} else {
|
|
visitor.visitMethodEditor(methodEditor);
|
|
}
|
|
|
|
context.release(methods[i]);
|
|
}
|
|
|
|
final Iterator iter = regularMethods.iterator();
|
|
while (iter.hasNext()) {
|
|
final MethodInfo info = (MethodInfo) iter.next();
|
|
final MethodEditor me = context.editMethod(info);
|
|
visitor.visitMethodEditor(me);
|
|
context.release(info);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Two <tt>ClassEditor</tt>s are equal if they edit the same class.
|
|
*/
|
|
public boolean equals(final Object o) {
|
|
if (o instanceof ClassEditor) {
|
|
final ClassEditor other = (ClassEditor) o;
|
|
if (!other.type().equals(this.type())) {
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* A <tt>ClassEditor</tt>'s hash code is based upon the hash code of the
|
|
* name of the class it edits.
|
|
*/
|
|
public int hashCode() {
|
|
return (this.name().hashCode());
|
|
}
|
|
|
|
public String toString() {
|
|
return (this.type().toString());
|
|
}
|
|
|
|
}
|
|
|