1. /*
  2. * @(#)ObjectStreamClassUtil_1_3.java 1.7 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.corba.se.impl.orbutil;
  8. // for computing the structural UID
  9. import java.security.MessageDigest;
  10. import java.security.NoSuchAlgorithmException;
  11. import java.security.DigestOutputStream;
  12. import java.security.AccessController;
  13. import java.security.PrivilegedExceptionAction;
  14. import java.security.PrivilegedActionException;
  15. import java.security.PrivilegedAction;
  16. import java.io.DataOutputStream;
  17. import java.io.ByteArrayOutputStream;
  18. import java.io.IOException;
  19. import java.util.Arrays;
  20. import java.util.Comparator;
  21. import java.lang.reflect.Field;
  22. import java.lang.reflect.Modifier;
  23. import java.lang.reflect.Array;
  24. import java.lang.reflect.Member;
  25. import java.lang.reflect.Method;
  26. import java.lang.reflect.Constructor;
  27. import com.sun.corba.se.impl.io.ObjectStreamClass;
  28. public final class ObjectStreamClassUtil_1_3 {
  29. // maintained here for backward compatability with JDK 1.3, where
  30. // writeObject method was not being checked at all, so there is
  31. // no need to lookup the ObjectStreamClass
  32. public static long computeSerialVersionUID(final Class cl) {
  33. long csuid = ObjectStreamClass.getSerialVersionUID(cl);
  34. if (csuid == 0)
  35. return csuid; // for non-serializable/proxy classes
  36. csuid = (ObjectStreamClassUtil_1_3.getSerialVersion(csuid, cl).longValue());
  37. return csuid;
  38. }
  39. // to maintain same suid as the JDK 1.3, we pick
  40. // up suid only for classes with private,static,final
  41. // declarations, and compute it for all others
  42. private static Long getSerialVersion(final long csuid, final Class cl)
  43. {
  44. return (Long) AccessController.doPrivileged(new PrivilegedAction() {
  45. public Object run() {
  46. long suid;
  47. try {
  48. final Field f = cl.getDeclaredField("serialVersionUID");
  49. int mods = f.getModifiers();
  50. if (Modifier.isStatic(mods) &&
  51. Modifier.isFinal(mods) && Modifier.isPrivate(mods)) {
  52. suid = csuid;
  53. } else {
  54. suid = _computeSerialVersionUID(cl);
  55. }
  56. } catch (NoSuchFieldException ex) {
  57. suid = _computeSerialVersionUID(cl);
  58. //} catch (IllegalAccessException ex) {
  59. // suid = _computeSerialVersionUID(cl);
  60. }
  61. return new Long(suid);
  62. }
  63. });
  64. }
  65. public static long computeStructuralUID(boolean hasWriteObject, Class cl) {
  66. ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
  67. long h = 0;
  68. try {
  69. if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
  70. (cl.isInterface())){
  71. return 0;
  72. }
  73. if (java.io.Externalizable.class.isAssignableFrom(cl)) {
  74. return 1;
  75. }
  76. MessageDigest md = MessageDigest.getInstance("SHA");
  77. DigestOutputStream mdo = new DigestOutputStream(devnull, md);
  78. DataOutputStream data = new DataOutputStream(mdo);
  79. //In the old case, for the caller class, the write Method wasn't considered
  80. // for rep-id calculations correctly, but for parent classes it was taken
  81. // into account. That is the reason there is the klude of getting the write
  82. // Object method in there
  83. // Get SUID of parent
  84. Class parent = cl.getSuperclass();
  85. if ((parent != null) && (parent != java.lang.Object.class)) {
  86. boolean hasWriteObjectFlag = false;
  87. Class [] args = {java.io.ObjectOutputStream.class};
  88. Method hasWriteObjectMethod = ObjectStreamClassUtil_1_3.getDeclaredMethod(parent, "writeObject", args,
  89. Modifier.PRIVATE, Modifier.STATIC);
  90. if (hasWriteObjectMethod != null)
  91. hasWriteObjectFlag = true;
  92. data.writeLong(ObjectStreamClassUtil_1_3.computeStructuralUID(hasWriteObjectFlag, parent));
  93. }
  94. if (hasWriteObject)
  95. data.writeInt(2);
  96. else
  97. data.writeInt(1);
  98. /* Sort the field names to get a deterministic order */
  99. Field[] field = ObjectStreamClassUtil_1_3.getDeclaredFields(cl);
  100. Arrays.sort(field, compareMemberByName);
  101. for (int i = 0; i < field.length; i++) {
  102. Field f = field[i];
  103. /* Include in the hash all fields except those that are
  104. * transient or static.
  105. */
  106. int m = f.getModifiers();
  107. if (Modifier.isTransient(m) || Modifier.isStatic(m))
  108. continue;
  109. data.writeUTF(f.getName());
  110. data.writeUTF(getSignature(f.getType()));
  111. }
  112. /* Compute the hash value for this class.
  113. * Use only the first 64 bits of the hash.
  114. */
  115. data.flush();
  116. byte hasharray[] = md.digest();
  117. int minimum = Math.min(8, hasharray.length);
  118. for (int i = minimum; i > 0; i--) {
  119. h += (long)(hasharray[i] & 255) << (i * 8);
  120. }
  121. } catch (IOException ignore) {
  122. /* can't happen, but be deterministic anyway. */
  123. h = -1;
  124. } catch (NoSuchAlgorithmException complain) {
  125. throw new SecurityException(complain.getMessage());
  126. }
  127. return h;
  128. }
  129. /*
  130. * Compute a hash for the specified class. Incrementally add
  131. * items to the hash accumulating in the digest stream.
  132. * Fold the hash into a long. Use the SHA secure hash function.
  133. */
  134. private static long _computeSerialVersionUID(Class cl) {
  135. ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
  136. long h = 0;
  137. try {
  138. MessageDigest md = MessageDigest.getInstance("SHA");
  139. DigestOutputStream mdo = new DigestOutputStream(devnull, md);
  140. DataOutputStream data = new DataOutputStream(mdo);
  141. data.writeUTF(cl.getName());
  142. int classaccess = cl.getModifiers();
  143. classaccess &= (Modifier.PUBLIC | Modifier.FINAL |
  144. Modifier.INTERFACE | Modifier.ABSTRACT);
  145. /* Workaround for javac bug that only set ABSTRACT for
  146. * interfaces if the interface had some methods.
  147. * The ABSTRACT bit reflects that the number of methods > 0.
  148. * This is required so correct hashes can be computed
  149. * for existing class files.
  150. * Previously this hack was previously present in the VM.
  151. */
  152. Method[] method = cl.getDeclaredMethods();
  153. if ((classaccess & Modifier.INTERFACE) != 0) {
  154. classaccess &= (~Modifier.ABSTRACT);
  155. if (method.length > 0) {
  156. classaccess |= Modifier.ABSTRACT;
  157. }
  158. }
  159. data.writeInt(classaccess);
  160. /*
  161. * Get the list of interfaces supported,
  162. * Accumulate their names their names in Lexical order
  163. * and add them to the hash
  164. */
  165. if (!cl.isArray()) {
  166. /* In 1.2fcs, getInterfaces() was modified to return
  167. * {java.lang.Cloneable, java.io.Serializable} when
  168. * called on array classes. These values would upset
  169. * the computation of the hash, so we explicitly omit
  170. * them from its computation.
  171. */
  172. Class interfaces[] = cl.getInterfaces();
  173. Arrays.sort(interfaces, compareClassByName);
  174. for (int i = 0; i < interfaces.length; i++) {
  175. data.writeUTF(interfaces[i].getName());
  176. }
  177. }
  178. /* Sort the field names to get a deterministic order */
  179. Field[] field = cl.getDeclaredFields();
  180. Arrays.sort(field, compareMemberByName);
  181. for (int i = 0; i < field.length; i++) {
  182. Field f = field[i];
  183. /* Include in the hash all fields except those that are
  184. * private transient and private static.
  185. */
  186. int m = f.getModifiers();
  187. if (Modifier.isPrivate(m) &&
  188. (Modifier.isTransient(m) || Modifier.isStatic(m)))
  189. continue;
  190. data.writeUTF(f.getName());
  191. data.writeInt(m);
  192. data.writeUTF(getSignature(f.getType()));
  193. }
  194. // need to find the java replacement for hasStaticInitializer
  195. if (hasStaticInitializer(cl)) {
  196. data.writeUTF("<clinit>");
  197. data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have
  198. data.writeUTF("()V");
  199. }
  200. /*
  201. * Get the list of constructors including name and signature
  202. * Sort lexically, add all except the private constructors
  203. * to the hash with their access flags
  204. */
  205. MethodSignature[] constructors =
  206. MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors());
  207. for (int i = 0; i < constructors.length; i++) {
  208. MethodSignature c = constructors[i];
  209. String mname = "<init>";
  210. String desc = c.signature;
  211. desc = desc.replace('/', '.');
  212. data.writeUTF(mname);
  213. data.writeInt(c.member.getModifiers());
  214. data.writeUTF(desc);
  215. }
  216. /* Include in the hash all methods except those that are
  217. * private transient and private static.
  218. */
  219. MethodSignature[] methods =
  220. MethodSignature.removePrivateAndSort(method);
  221. for (int i = 0; i < methods.length; i++ ) {
  222. MethodSignature m = methods[i];
  223. String desc = m.signature;
  224. desc = desc.replace('/', '.');
  225. data.writeUTF(m.member.getName());
  226. data.writeInt(m.member.getModifiers());
  227. data.writeUTF(desc);
  228. }
  229. /* Compute the hash value for this class.
  230. * Use only the first 64 bits of the hash.
  231. */
  232. data.flush();
  233. byte hasharray[] = md.digest();
  234. for (int i = 0; i < Math.min(8, hasharray.length); i++) {
  235. h += (long)(hasharray[i] & 255) << (i * 8);
  236. }
  237. } catch (IOException ignore) {
  238. /* can't happen, but be deterministic anyway. */
  239. h = -1;
  240. } catch (NoSuchAlgorithmException complain) {
  241. throw new SecurityException(complain.getMessage());
  242. }
  243. return h;
  244. }
  245. /*
  246. * Comparator object for Classes and Interfaces
  247. */
  248. private static Comparator compareClassByName =
  249. new CompareClassByName();
  250. private static class CompareClassByName implements Comparator {
  251. public int compare(Object o1, Object o2) {
  252. Class c1 = (Class)o1;
  253. Class c2 = (Class)o2;
  254. return (c1.getName()).compareTo(c2.getName());
  255. }
  256. }
  257. /*
  258. * Comparator object for Members, Fields, and Methods
  259. */
  260. private static Comparator compareMemberByName =
  261. new CompareMemberByName();
  262. private static class CompareMemberByName implements Comparator {
  263. public int compare(Object o1, Object o2) {
  264. String s1 = ((Member)o1).getName();
  265. String s2 = ((Member)o2).getName();
  266. if (o1 instanceof Method) {
  267. s1 += getSignature((Method)o1);
  268. s2 += getSignature((Method)o2);
  269. } else if (o1 instanceof Constructor) {
  270. s1 += getSignature((Constructor)o1);
  271. s2 += getSignature((Constructor)o2);
  272. }
  273. return s1.compareTo(s2);
  274. }
  275. }
  276. /**
  277. * Compute the JVM signature for the class.
  278. */
  279. private static String getSignature(Class clazz) {
  280. String type = null;
  281. if (clazz.isArray()) {
  282. Class cl = clazz;
  283. int dimensions = 0;
  284. while (cl.isArray()) {
  285. dimensions++;
  286. cl = cl.getComponentType();
  287. }
  288. StringBuffer sb = new StringBuffer();
  289. for (int i = 0; i < dimensions; i++) {
  290. sb.append("[");
  291. }
  292. sb.append(getSignature(cl));
  293. type = sb.toString();
  294. } else if (clazz.isPrimitive()) {
  295. if (clazz == Integer.TYPE) {
  296. type = "I";
  297. } else if (clazz == Byte.TYPE) {
  298. type = "B";
  299. } else if (clazz == Long.TYPE) {
  300. type = "J";
  301. } else if (clazz == Float.TYPE) {
  302. type = "F";
  303. } else if (clazz == Double.TYPE) {
  304. type = "D";
  305. } else if (clazz == Short.TYPE) {
  306. type = "S";
  307. } else if (clazz == Character.TYPE) {
  308. type = "C";
  309. } else if (clazz == Boolean.TYPE) {
  310. type = "Z";
  311. } else if (clazz == Void.TYPE) {
  312. type = "V";
  313. }
  314. } else {
  315. type = "L" + clazz.getName().replace('.', '/') + ";";
  316. }
  317. return type;
  318. }
  319. /*
  320. * Compute the JVM method descriptor for the method.
  321. */
  322. private static String getSignature(Method meth) {
  323. StringBuffer sb = new StringBuffer();
  324. sb.append("(");
  325. Class[] params = meth.getParameterTypes(); // avoid clone
  326. for (int j = 0; j < params.length; j++) {
  327. sb.append(getSignature(params[j]));
  328. }
  329. sb.append(")");
  330. sb.append(getSignature(meth.getReturnType()));
  331. return sb.toString();
  332. }
  333. /*
  334. * Compute the JVM constructor descriptor for the constructor.
  335. */
  336. private static String getSignature(Constructor cons) {
  337. StringBuffer sb = new StringBuffer();
  338. sb.append("(");
  339. Class[] params = cons.getParameterTypes(); // avoid clone
  340. for (int j = 0; j < params.length; j++) {
  341. sb.append(getSignature(params[j]));
  342. }
  343. sb.append(")V");
  344. return sb.toString();
  345. }
  346. private static Field[] getDeclaredFields(final Class clz) {
  347. return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
  348. public Object run() {
  349. return clz.getDeclaredFields();
  350. }
  351. });
  352. }
  353. private static class MethodSignature implements Comparator {
  354. Member member;
  355. String signature; // cached parameter signature
  356. /* Given an array of Method or Constructor members,
  357. return a sorted array of the non-private members.*/
  358. /* A better implementation would be to implement the returned data
  359. structure as an insertion sorted link list.*/
  360. static MethodSignature[] removePrivateAndSort(Member[] m) {
  361. int numNonPrivate = 0;
  362. for (int i = 0; i < m.length; i++) {
  363. if (! Modifier.isPrivate(m[i].getModifiers())) {
  364. numNonPrivate++;
  365. }
  366. }
  367. MethodSignature[] cm = new MethodSignature[numNonPrivate];
  368. int cmi = 0;
  369. for (int i = 0; i < m.length; i++) {
  370. if (! Modifier.isPrivate(m[i].getModifiers())) {
  371. cm[cmi] = new MethodSignature(m[i]);
  372. cmi++;
  373. }
  374. }
  375. if (cmi > 0)
  376. Arrays.sort(cm, cm[0]);
  377. return cm;
  378. }
  379. /* Assumes that o1 and o2 are either both methods
  380. or both constructors.*/
  381. public int compare(Object o1, Object o2) {
  382. /* Arrays.sort calls compare when o1 and o2 are equal.*/
  383. if (o1 == o2)
  384. return 0;
  385. MethodSignature c1 = (MethodSignature)o1;
  386. MethodSignature c2 = (MethodSignature)o2;
  387. int result;
  388. if (isConstructor()) {
  389. result = c1.signature.compareTo(c2.signature);
  390. } else { // is a Method.
  391. result = c1.member.getName().compareTo(c2.member.getName());
  392. if (result == 0)
  393. result = c1.signature.compareTo(c2.signature);
  394. }
  395. return result;
  396. }
  397. final private boolean isConstructor() {
  398. return member instanceof Constructor;
  399. }
  400. private MethodSignature(Member m) {
  401. member = m;
  402. if (isConstructor()) {
  403. signature = ObjectStreamClassUtil_1_3.getSignature((Constructor)m);
  404. } else {
  405. signature = ObjectStreamClassUtil_1_3.getSignature((Method)m);
  406. }
  407. }
  408. }
  409. /* Find out if the class has a static class initializer <clinit> */
  410. // use java.io.ObjectStream's hasStaticInitializer method
  411. // private static native boolean hasStaticInitializer(Class cl);
  412. private static Method hasStaticInitializerMethod = null;
  413. /**
  414. * Returns true if the given class defines a static initializer method,
  415. * false otherwise.
  416. */
  417. private static boolean hasStaticInitializer(Class cl) {
  418. if (hasStaticInitializerMethod == null) {
  419. Class classWithThisMethod = null;
  420. try {
  421. try {
  422. // When using rip-int with Merlin or when this is a Merlin
  423. // workspace, the method we want is in sun.misc.ClassReflector
  424. // and absent from java.io.ObjectStreamClass.
  425. //
  426. // When compiling rip-int with JDK 1.3.x, we have to get it
  427. // from java.io.ObjectStreamClass.
  428. classWithThisMethod = Class.forName("sun.misc.ClassReflector");
  429. } catch (ClassNotFoundException cnfe) {
  430. // Do nothing. This is either not a Merlin workspace,
  431. // or rip-int is being compiled with something other than
  432. // Merlin, probably JDK 1.3. Fall back on java.io.ObjectStreaClass.
  433. }
  434. if (classWithThisMethod == null)
  435. classWithThisMethod = java.io.ObjectStreamClass.class;
  436. hasStaticInitializerMethod =
  437. classWithThisMethod.getDeclaredMethod("hasStaticInitializer",
  438. new Class[] { Class.class });
  439. } catch (NoSuchMethodException ex) {
  440. }
  441. if (hasStaticInitializerMethod == null) {
  442. throw new InternalError("Can't find hasStaticInitializer method on "
  443. + classWithThisMethod.getName());
  444. }
  445. hasStaticInitializerMethod.setAccessible(true);
  446. }
  447. try {
  448. Boolean retval = (Boolean)
  449. hasStaticInitializerMethod.invoke(null, new Object[] { cl });
  450. return retval.booleanValue();
  451. } catch (Exception ex) {
  452. throw new InternalError("Error invoking hasStaticInitializer: "
  453. + ex);
  454. }
  455. }
  456. private static Method getDeclaredMethod(final Class cl, final String methodName, final Class[] args,
  457. final int requiredModifierMask,
  458. final int disallowedModifierMask) {
  459. return (Method) AccessController.doPrivileged(new PrivilegedAction() {
  460. public Object run() {
  461. Method method = null;
  462. try {
  463. method =
  464. cl.getDeclaredMethod(methodName, args);
  465. int mods = method.getModifiers();
  466. if ((mods & disallowedModifierMask) != 0 ||
  467. (mods & requiredModifierMask) != requiredModifierMask) {
  468. method = null;
  469. }
  470. //if (!Modifier.isPrivate(mods) ||
  471. // Modifier.isStatic(mods)) {
  472. // method = null;
  473. //}
  474. } catch (NoSuchMethodException e) {
  475. // Since it is alright if methodName does not exist,
  476. // no need to do anything special here.
  477. }
  478. return method;
  479. }
  480. });
  481. }
  482. }