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.
459 lines
12 KiB
459 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.editor;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.*;
|
|
import java.security.*;
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
|
|
/**
|
|
* <P>
|
|
* This class computes the serial version UID of a class modeled by a
|
|
* <code>ClassEditor</code>. Otherwise, we would have to load the class in
|
|
* order to compute its serial version UID. That would suck.
|
|
* </P>
|
|
*
|
|
* <P>
|
|
* The algorithm for computing the serial version UID can be found in the <A
|
|
* href="http://java.sun.com/j2se/1.3/docs/guide/serialization/spec">serialization
|
|
* spec</A>
|
|
* </P>
|
|
*/
|
|
public class SerialVersionUID {
|
|
|
|
/**
|
|
* Returns <code>true</code> if the class modeled by the given
|
|
* <code>ClassEditor</code> implements {@link java.io.Serializable
|
|
* Serializable}. It checks superclasses.
|
|
*/
|
|
public static boolean implementsSerializable(final ClassEditor ce) {
|
|
if (ce.type().equals(Type.OBJECT)) {
|
|
// Stop the recursion!
|
|
return (false);
|
|
}
|
|
|
|
final Type serializable = Type.getType("Ljava/io/Serializable;");
|
|
final Type[] interfaces = ce.interfaces();
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
if (interfaces[i].equals(serializable)) {
|
|
return (true);
|
|
}
|
|
}
|
|
|
|
// Does its superclass implement Serializable?
|
|
|
|
final Type superclass = ce.superclass();
|
|
final ClassInfoLoader loader = ce.classInfo().loader();
|
|
try {
|
|
final ClassInfo ci = loader.loadClass(superclass.className());
|
|
final ClassEditor sce = new ClassEditor(ce.context(), ci);
|
|
return (SerialVersionUID.implementsSerializable(sce));
|
|
|
|
} catch (final ClassNotFoundException ex) {
|
|
System.err.println("Could not load class: " + superclass
|
|
+ ", superclass of " + ce.name());
|
|
System.exit(1);
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
/**
|
|
* Returns the serial version UID of the class modeled by the given
|
|
* <code>ClassEditor</code>.
|
|
*
|
|
* @param ce
|
|
* The class must implement {@link java.io.Serializable
|
|
* Serializable}
|
|
*/
|
|
public static long serialVersionUID(final ClassEditor ce) {
|
|
// Make sure the class implements Serializable
|
|
if (!SerialVersionUID.implementsSerializable(ce)) {
|
|
final String s = "Class " + ce.name()
|
|
+ " does not implement java.io.Serializable";
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
|
|
// If the class already has a serialVersionUID, return that
|
|
final FieldInfo[] fields = ce.fields();
|
|
for (int i = 0; i < fields.length; i++) {
|
|
final FieldEditor fe = new FieldEditor(ce, fields[i]);
|
|
if (fe.name().equals("serialVersionUID")) {
|
|
final Object value = fe.constantValue();
|
|
if (value != null) {
|
|
if (value instanceof Long) {
|
|
return (((Long) value).longValue());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now, compute the digest of the bytes using SHA
|
|
MessageDigest algorithm = null;
|
|
try {
|
|
algorithm = MessageDigest.getInstance("SHA");
|
|
|
|
} catch (final NoSuchAlgorithmException ex) {
|
|
final String s = "Can't use SHA-1 message digest algorith!";
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
|
|
final DataOutputStream dos = new DataOutputStream(
|
|
new DigestOutputStream(new ByteArrayOutputStream(), algorithm));
|
|
|
|
try {
|
|
// Write a bunch of information about the class to the output
|
|
// stream
|
|
SerialVersionUID.writeClassName(ce, dos);
|
|
SerialVersionUID.writeClassModifiers(ce, dos);
|
|
SerialVersionUID.writeInterfaceNames(ce, dos);
|
|
SerialVersionUID.writeFields(ce, dos);
|
|
SerialVersionUID.writeStaticInitializer(ce, dos);
|
|
SerialVersionUID.writeConstructors(ce, dos);
|
|
SerialVersionUID.writeMethods(ce, dos);
|
|
|
|
dos.flush();
|
|
dos.close();
|
|
|
|
} catch (final IOException ex) {
|
|
final String s = ("While computing serial version UID: " + ex);
|
|
throw new IllegalArgumentException(s);
|
|
}
|
|
|
|
// Compute the hash value from the first 64 bites of the digest
|
|
final byte[] digest = algorithm.digest();
|
|
long uid = 0;
|
|
for (int i = 0; i < Math.min(8, digest.length); i++) {
|
|
uid += (long) (digest[i] & 255) << (i * 8);
|
|
}
|
|
return (uid);
|
|
|
|
}
|
|
|
|
/**
|
|
* Writes the name of the class to the data output stream
|
|
*/
|
|
private static void writeClassName(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
dos.writeUTF(ce.name().replace('/', '.'));
|
|
}
|
|
|
|
/**
|
|
* Returns the Java reflection modifiers for a given class
|
|
*/
|
|
static int getModifiers(final ClassEditor ce) {
|
|
// Translate BLOAT's class modifiers into Java's reflection
|
|
// modifiers
|
|
int modifiers = 0;
|
|
|
|
if (ce.isPublic()) {
|
|
modifiers |= Modifier.PUBLIC;
|
|
}
|
|
|
|
if (ce.isPrivate()) {
|
|
modifiers |= Modifier.PRIVATE;
|
|
}
|
|
|
|
if (ce.isProtected()) {
|
|
modifiers |= Modifier.PROTECTED;
|
|
}
|
|
|
|
if (ce.isStatic()) {
|
|
modifiers |= Modifier.STATIC;
|
|
}
|
|
|
|
if (ce.isFinal()) {
|
|
modifiers |= Modifier.FINAL;
|
|
}
|
|
|
|
if (ce.isAbstract()) {
|
|
modifiers |= Modifier.ABSTRACT;
|
|
}
|
|
|
|
if (ce.isInterface()) {
|
|
modifiers |= Modifier.INTERFACE;
|
|
}
|
|
|
|
return (modifiers);
|
|
}
|
|
|
|
/**
|
|
* Writes the class's modifiers to the output stream
|
|
*/
|
|
private static void writeClassModifiers(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
dos.writeInt(SerialVersionUID.getModifiers(ce));
|
|
}
|
|
|
|
/**
|
|
* Writes the names of the interfaces implemented by the class to the output
|
|
* stream
|
|
*/
|
|
private static void writeInterfaceNames(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
|
|
// Sort interfaces by name
|
|
final SortedSet sorted = new TreeSet();
|
|
|
|
final Type[] interfaces = ce.interfaces();
|
|
for (int i = 0; i < interfaces.length; i++) {
|
|
sorted.add(interfaces[i].className().replace('/', '.'));
|
|
}
|
|
|
|
final Iterator iter = sorted.iterator();
|
|
while (iter.hasNext()) {
|
|
final String name = (String) iter.next();
|
|
dos.writeUTF(name);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the Java reflection modifiers for a field
|
|
*/
|
|
static int getModifiers(final FieldEditor fe) {
|
|
int modifiers = 0;
|
|
|
|
if (fe.isPublic()) {
|
|
modifiers |= Modifier.PUBLIC;
|
|
}
|
|
|
|
if (fe.isPrivate()) {
|
|
modifiers |= Modifier.PRIVATE;
|
|
}
|
|
|
|
if (fe.isProtected()) {
|
|
modifiers |= Modifier.PROTECTED;
|
|
}
|
|
|
|
if (fe.isPackage()) {
|
|
// Nothing
|
|
}
|
|
|
|
if (fe.isStatic()) {
|
|
modifiers |= Modifier.STATIC;
|
|
}
|
|
|
|
if (fe.isFinal()) {
|
|
modifiers |= Modifier.FINAL;
|
|
}
|
|
|
|
if (fe.isVolatile()) {
|
|
modifiers |= Modifier.VOLATILE;
|
|
}
|
|
|
|
if (fe.isTransient()) {
|
|
// Kind of a moot point
|
|
modifiers |= Modifier.TRANSIENT;
|
|
}
|
|
|
|
return (modifiers);
|
|
}
|
|
|
|
/**
|
|
* Writes information about the class's fields to the output stream
|
|
*/
|
|
private static void writeFields(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
|
|
// Sort the fields by their names
|
|
final SortedSet sorted = new TreeSet(new Comparator() {
|
|
public int compare(Object o1, Object o2) {
|
|
FieldEditor fe1 = (FieldEditor) o1;
|
|
FieldEditor fe2 = (FieldEditor) o2;
|
|
return (fe1.name().compareTo(fe2.name()));
|
|
}
|
|
});
|
|
|
|
final FieldInfo[] infos = ce.fields();
|
|
for (int i = 0; i < infos.length; i++) {
|
|
final FieldEditor fe = new FieldEditor(ce, infos[i]);
|
|
// Ignore private static and private transient fields
|
|
if (fe.isPrivate() && fe.isStatic()) {
|
|
break;
|
|
|
|
} else if (fe.isPrivate() && fe.isTransient()) {
|
|
break;
|
|
|
|
} else {
|
|
sorted.add(fe);
|
|
}
|
|
}
|
|
|
|
final Iterator iter = sorted.iterator();
|
|
while (iter.hasNext()) {
|
|
final FieldEditor fe = (FieldEditor) iter.next();
|
|
dos.writeUTF(fe.name());
|
|
dos.writeInt(SerialVersionUID.getModifiers(fe));
|
|
dos.writeUTF(fe.type().descriptor());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the Java reflection descriptors for a method
|
|
*/
|
|
static int getModifiers(final MethodEditor me) {
|
|
int modifiers = 0;
|
|
|
|
if (me.isPublic()) {
|
|
modifiers |= Modifier.PUBLIC;
|
|
}
|
|
|
|
if (me.isPrivate()) {
|
|
modifiers |= Modifier.PRIVATE;
|
|
}
|
|
|
|
if (me.isProtected()) {
|
|
modifiers |= Modifier.PROTECTED;
|
|
}
|
|
|
|
if (me.isPackage()) {
|
|
// Nothing
|
|
}
|
|
|
|
if (me.isStatic()) {
|
|
modifiers |= Modifier.STATIC;
|
|
}
|
|
|
|
if (me.isFinal()) {
|
|
modifiers |= Modifier.FINAL;
|
|
}
|
|
|
|
if (me.isSynchronized()) {
|
|
modifiers |= Modifier.SYNCHRONIZED;
|
|
}
|
|
|
|
if (me.isNative()) {
|
|
modifiers |= Modifier.NATIVE;
|
|
}
|
|
|
|
if (me.isAbstract()) {
|
|
modifiers |= Modifier.ABSTRACT;
|
|
}
|
|
|
|
if (me.isInterface()) {
|
|
modifiers |= Modifier.INTERFACE;
|
|
}
|
|
|
|
return (modifiers);
|
|
}
|
|
|
|
/**
|
|
* Writes information about the classes static initializer if it has one
|
|
*/
|
|
private static void writeStaticInitializer(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
|
|
MethodEditor clinit = null;
|
|
|
|
final MethodInfo[] methods = ce.methods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
final MethodEditor me = new MethodEditor(ce, methods[i]);
|
|
if (me.name().equals("<clinit>")) {
|
|
clinit = me;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (clinit != null) {
|
|
dos.writeUTF("<clinit>");
|
|
dos.writeInt(Modifier.STATIC);
|
|
dos.writeUTF("()V");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes information about the class's constructors
|
|
*/
|
|
private static void writeConstructors(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
|
|
// Sort constructors by their signatures
|
|
final SortedSet sorted = new TreeSet(new Comparator() {
|
|
public int compare(Object o1, Object o2) {
|
|
MethodEditor me1 = (MethodEditor) o1;
|
|
MethodEditor me2 = (MethodEditor) o2;
|
|
return (me1.type().descriptor().compareTo(me2.type()
|
|
.descriptor()));
|
|
}
|
|
});
|
|
|
|
final MethodInfo[] methods = ce.methods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
final MethodEditor me = new MethodEditor(ce, methods[i]);
|
|
if (me.name().equals("<init>")) {
|
|
if (!me.isPrivate()) {
|
|
// Ignore private constructors
|
|
sorted.add(me);
|
|
}
|
|
}
|
|
}
|
|
|
|
final Iterator iter = sorted.iterator();
|
|
while (iter.hasNext()) {
|
|
final MethodEditor init = (MethodEditor) iter.next();
|
|
dos.writeUTF("<init>");
|
|
dos.writeInt(SerialVersionUID.getModifiers(init));
|
|
dos.writeUTF(init.type().descriptor());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write information about the class's methods
|
|
*/
|
|
private static void writeMethods(final ClassEditor ce,
|
|
final DataOutputStream dos) throws IOException {
|
|
|
|
// Sort constructors by their names and signatures
|
|
final SortedSet sorted = new TreeSet(new Comparator() {
|
|
public int compare(Object o1, Object o2) {
|
|
MethodEditor me1 = (MethodEditor) o1;
|
|
MethodEditor me2 = (MethodEditor) o2;
|
|
|
|
String d1 = me1.name() + me1.type().descriptor();
|
|
String d2 = me2.name() + me2.type().descriptor();
|
|
return (d1.compareTo(d2));
|
|
}
|
|
});
|
|
|
|
final MethodInfo[] methods = ce.methods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
final MethodEditor me = new MethodEditor(ce, methods[i]);
|
|
if (!me.isPrivate() && !me.isConstructor()
|
|
&& !me.name().equals("<clinit>")) {
|
|
// Ignore private methods
|
|
sorted.add(me);
|
|
}
|
|
}
|
|
|
|
final Iterator iter = sorted.iterator();
|
|
while (iter.hasNext()) {
|
|
final MethodEditor me = (MethodEditor) iter.next();
|
|
dos.writeUTF(me.name());
|
|
dos.writeInt(SerialVersionUID.getModifiers(me));
|
|
dos.writeUTF(me.type().descriptor());
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|