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