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/editor/SerialVersionUID.java

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());
}
}
}