001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2005, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba;
021:
022: import java.security.MessageDigest;
023: import java.security.NoSuchAlgorithmException;
024: import java.util.Arrays;
025:
026: import org.apache.bcel.classfile.Method;
027:
028: /**
029: * Compute a hash of the bytecode for given method.
030: * This can find methods which have not been changed other
031: * than accessing different constant pool entries.
032: *
033: * @author David Hovemeyer
034: */
035: public class MethodHash implements Comparable<MethodHash> {
036: public static final String METHOD_HASH_ELEMENT_NAME = "MethodHash";
037:
038: private byte[] hash;
039: private String methodName;
040: private String methodSig;
041: private boolean isStatic;
042:
043: /**
044: * Constructor.
045: * computeHash(Method) must be used to initialize the contents.
046: */
047: public MethodHash() {
048: }
049:
050: /**
051: * Constructor.
052: *
053: * @param methodName method name
054: * @param methodSig method signature
055: * @param isStatic true if the method is static, false if not
056: * @param hash the pre-computed hash
057: */
058: public MethodHash(String methodName, String methodSig,
059: boolean isStatic, byte[] hash) {
060: this .methodName = methodName;
061: this .methodSig = methodSig;
062: this .isStatic = isStatic;
063: this .hash = new byte[hash.length];
064: System.arraycopy(hash, 0, this .hash, 0, hash.length);
065: }
066:
067: /**
068: * @return Returns the method name.
069: */
070: public String getMethodName() {
071: return methodName;
072: }
073:
074: /**
075: * @return Returns the method signature.
076: */
077: public String getMethodSig() {
078: return methodSig;
079: }
080:
081: /**
082: * @return Returns whether the method is static.
083: */
084: public boolean isStatic() {
085: return isStatic;
086: }
087:
088: /**
089: * Get the computed method hash.
090: *
091: * @return the method hash
092: */
093: public byte[] getMethodHash() {
094: return hash;
095: }
096:
097: /**
098: * Compute hash on given method.
099: *
100: * @param method the method
101: * @return this object
102: */
103: public MethodHash computeHash(Method method) {
104: MessageDigest digest_;
105: try {
106: digest_ = MessageDigest.getInstance("MD5");
107: } catch (NoSuchAlgorithmException e) {
108: throw new IllegalStateException(
109: "No algorithm for computing method hash", e);
110: }
111: final MessageDigest digest = digest_;
112:
113: byte[] code;
114: if (method.getCode() == null
115: || method.getCode().getCode() == null) {
116: code = new byte[0];
117: } else {
118: code = method.getCode().getCode();
119: }
120:
121: BytecodeScanner.Callback callback = new BytecodeScanner.Callback() {
122: public void handleInstruction(int opcode, int index) {
123: digest.update((byte) opcode);
124: }
125: };
126:
127: BytecodeScanner bytecodeScanner = new BytecodeScanner();
128: bytecodeScanner.scan(code, callback);
129:
130: hash = digest.digest();
131:
132: return this ;
133: }
134:
135: /**
136: * Return whether or not this method hash has the same value as the one given.
137: *
138: * @param other another MethodHash
139: * @return true if the hash values are the same, false if not
140: */
141: public boolean isSameHash(MethodHash other) {
142: return Arrays.equals(this .hash, other.hash);
143: }
144:
145: /* (non-Javadoc)
146: * @see java.lang.Comparable#compareTo(T)
147: */
148: public int compareTo(MethodHash other) {
149: return MethodHash.compareHashes(this .hash, other.hash);
150: }
151:
152: @Override
153: public boolean equals(Object o) {
154: if (o instanceof MethodHash)
155: return isSameHash((MethodHash) o);
156: return false;
157: }
158:
159: @Override
160: public int hashCode() {
161: int result = 0;
162: for (byte b : hash)
163: result = result * 17 + b;
164: return result;
165: }
166:
167: public static int compareHashes(byte[] a, byte[] b) {
168: int pfxlen = Math.min(a.length, b.length);
169: for (int i = 0; i < pfxlen; ++i) {
170: int cmp = toUnsigned(a[i]) - toUnsigned(b[i]);
171: if (cmp != 0)
172: return cmp;
173: }
174: return a.length - b.length;
175: }
176:
177: /**
178: * Convert a byte to an unsigned int.
179: *
180: * @param b a byte value
181: * @return the unsigned integer value of the byte
182: */
183: private static int toUnsigned(byte b) {
184: int value = b & 0x7F;
185: if ((b & 0x80) != 0) {
186: value |= 0x80;
187: }
188: return value;
189: }
190:
191: }
|