1 /* ObjectStreamClass.java -- Class used to write class information 2 about serialized objects. 3 Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 39 40 package java.io; 41 42 import gnu.java.io.NullOutputStream; 43 import gnu.java.lang.reflect.TypeSignature; 44 import gnu.java.security.action.SetAccessibleAction; 45 import gnu.java.security.provider.Gnu; 46 47 import java.lang.reflect.Constructor; 48 import java.lang.reflect.Field; 49 import java.lang.reflect.Member; 50 import java.lang.reflect.Method; 51 import java.lang.reflect.Modifier; 52 import java.lang.reflect.Proxy; 53 import java.security.AccessController; 54 import java.security.DigestOutputStream; 55 import java.security.MessageDigest; 56 import java.security.NoSuchAlgorithmException; 57 import java.security.PrivilegedAction; 58 import java.security.Security; 59 import java.util.Arrays; 60 import java.util.Comparator; 61 import java.util.Hashtable; 62 63 /** 64 * @author Tom Tromey (tromey@redhat.com) 65 * @author Jeroen Frijters (jeroen@frijters.net) 66 * @author Guilhem Lavaux (guilhem@kaffe.org) 67 * @author Michael Koch (konqueror@gmx.de) 68 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 69 */ 70 public class ObjectStreamClass implements Serializable 71 { 72 static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0]; 73 74 /** 75 * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. 76 * If <code>cl</code> is null, or is not <code>Serializable</code>, 77 * null is returned. <code>ObjectStreamClass</code>'s are memorized; 78 * later calls to this method with the same class will return the 79 * same <code>ObjectStreamClass</code> object and no recalculation 80 * will be done. 81 * 82 * Warning: If this class contains an invalid serialPersistentField arrays 83 * lookup will not throw anything. However {@link #getFields()} will return 84 * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an 85 * {@link java.io.InvalidClassException}. 86 * 87 * @see java.io.Serializable 88 */ lookup(Class<?> cl)89 public static ObjectStreamClass lookup(Class<?> cl) 90 { 91 if (cl == null) 92 return null; 93 if (! (Serializable.class).isAssignableFrom(cl)) 94 return null; 95 96 return lookupForClassObject(cl); 97 } 98 99 /** 100 * This lookup for internal use by ObjectOutputStream. Suppose 101 * we have a java.lang.Class object C for class A, though A is not 102 * serializable, but it's okay to serialize C. 103 */ lookupForClassObject(Class cl)104 static ObjectStreamClass lookupForClassObject(Class cl) 105 { 106 if (cl == null) 107 return null; 108 109 ObjectStreamClass osc = classLookupTable.get(cl); 110 111 if (osc != null) 112 return osc; 113 else 114 { 115 osc = new ObjectStreamClass(cl); 116 classLookupTable.put(cl, osc); 117 return osc; 118 } 119 } 120 121 /** 122 * Returns the name of the class that this 123 * <code>ObjectStreamClass</code> represents. 124 * 125 * @return the name of the class. 126 */ getName()127 public String getName() 128 { 129 return name; 130 } 131 132 /** 133 * Returns the class that this <code>ObjectStreamClass</code> 134 * represents. Null could be returned if this 135 * <code>ObjectStreamClass</code> was read from an 136 * <code>ObjectInputStream</code> and the class it represents cannot 137 * be found or loaded. 138 * 139 * @see java.io.ObjectInputStream 140 */ forClass()141 public Class<?> forClass() 142 { 143 return clazz; 144 } 145 146 /** 147 * Returns the serial version stream-unique identifier for the class 148 * represented by this <code>ObjectStreamClass</code>. This SUID is 149 * either defined by the class as <code>static final long 150 * serialVersionUID</code> or is calculated as specified in 151 * Javasoft's "Object Serialization Specification" XXX: add reference 152 * 153 * @return the serial version UID. 154 */ getSerialVersionUID()155 public long getSerialVersionUID() 156 { 157 return uid; 158 } 159 160 /** 161 * Returns the serializable (non-static and non-transient) Fields 162 * of the class represented by this ObjectStreamClass. The Fields 163 * are sorted by name. 164 * If fields were obtained using serialPersistentFields and this array 165 * is faulty then the returned array of this method will be empty. 166 * 167 * @return the fields. 168 */ getFields()169 public ObjectStreamField[] getFields() 170 { 171 ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; 172 System.arraycopy(fields, 0, copy, 0, fields.length); 173 return copy; 174 } 175 176 // XXX doc 177 // Can't do binary search since fields is sorted by name and 178 // primitiveness. getField(String name)179 public ObjectStreamField getField (String name) 180 { 181 for (int i = 0; i < fields.length; i++) 182 if (fields[i].getName().equals(name)) 183 return fields[i]; 184 return null; 185 } 186 187 /** 188 * Returns a textual representation of this 189 * <code>ObjectStreamClass</code> object including the name of the 190 * class it represents as well as that class's serial version 191 * stream-unique identifier. 192 * 193 * @see #getSerialVersionUID() 194 * @see #getName() 195 */ toString()196 public String toString() 197 { 198 return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; 199 } 200 201 // Returns true iff the class that this ObjectStreamClass represents 202 // has the following method: 203 // 204 // private void writeObject (ObjectOutputStream) 205 // 206 // This method is used by the class to override default 207 // serialization behavior. hasWriteMethod()208 boolean hasWriteMethod() 209 { 210 return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; 211 } 212 213 // Returns true iff the class that this ObjectStreamClass represents 214 // implements Serializable but does *not* implement Externalizable. isSerializable()215 boolean isSerializable() 216 { 217 return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; 218 } 219 220 221 // Returns true iff the class that this ObjectStreamClass represents 222 // implements Externalizable. isExternalizable()223 boolean isExternalizable() 224 { 225 return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; 226 } 227 228 // Returns true iff the class that this ObjectStreamClass represents 229 // implements Externalizable. isEnum()230 boolean isEnum() 231 { 232 return (flags & ObjectStreamConstants.SC_ENUM) != 0; 233 } 234 235 // Returns the <code>ObjectStreamClass</code> that represents the 236 // class that is the superclass of the class this 237 // <code>ObjectStreamClass</code> represents. If the superclass is 238 // not Serializable, null is returned. getSuper()239 ObjectStreamClass getSuper() 240 { 241 return superClass; 242 } 243 244 /** 245 * returns an array of ObjectStreamClasses that represent the super 246 * classes of the class represented by this and the class 247 * represented by this itself in order from most super to this. 248 * ObjectStreamClass[0] is the highest superclass of this that is 249 * serializable. 250 * 251 * The result of consecutive calls this hierarchy() will be the same 252 * array instance. 253 * 254 * @return an array of ObjectStreamClass representing the 255 * super-class hierarchy of serializable classes. 256 */ hierarchy()257 ObjectStreamClass[] hierarchy() 258 { 259 ObjectStreamClass[] result = hierarchy; 260 if (result == null) 261 { 262 int d = 0; 263 264 for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) 265 d++; 266 267 result = new ObjectStreamClass[d]; 268 269 for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) 270 { 271 result[--d] = osc; 272 } 273 274 hierarchy = result; 275 } 276 return result; 277 } 278 279 /** 280 * Cache for hierarchy() result. 281 */ 282 private ObjectStreamClass[] hierarchy = null; 283 284 // Returns an integer that consists of bit-flags that indicate 285 // properties of the class represented by this ObjectStreamClass. 286 // The bit-flags that could be present are those defined in 287 // ObjectStreamConstants that begin with `SC_' getFlags()288 int getFlags() 289 { 290 return flags; 291 } 292 293 ObjectStreamClass(String name, long uid, byte flags, ObjectStreamField[] fields)294 ObjectStreamClass(String name, long uid, byte flags, 295 ObjectStreamField[] fields) 296 { 297 this.name = name; 298 this.uid = uid; 299 this.flags = flags; 300 this.fields = fields; 301 } 302 303 /** 304 * This method builds the internal description corresponding to a Java Class. 305 * As the constructor only assign a name to the current ObjectStreamClass instance, 306 * that method sets the serial UID, chose the fields which will be serialized, 307 * and compute the position of the fields in the serialized stream. 308 * 309 * @param cl The Java class which is used as a reference for building the descriptor. 310 * @param superClass The descriptor of the super class for this class descriptor. 311 * @throws InvalidClassException if an incompatibility between computed UID and 312 * already set UID is found. 313 */ setClass(Class cl, ObjectStreamClass superClass)314 void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException 315 {hierarchy = null; 316 this.clazz = cl; 317 318 cacheMethods(); 319 320 long class_uid = getClassUID(cl); 321 if (uid == 0) 322 uid = class_uid; 323 else 324 { 325 // Check that the actual UID of the resolved class matches the UID from 326 // the stream. Mismatches for array classes are ignored. 327 if (!cl.isArray() && uid != class_uid) 328 { 329 String msg = cl + 330 ": Local class not compatible: stream serialVersionUID=" 331 + uid + ", local serialVersionUID=" + class_uid; 332 throw new InvalidClassException (msg); 333 } 334 } 335 336 isProxyClass = clazz != null && Proxy.isProxyClass(clazz); 337 this.superClass = superClass; 338 calculateOffsets(); 339 340 try 341 { 342 ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); 343 344 if (exportedFields == null) 345 return; 346 347 ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; 348 int i, j, k; 349 350 /* We now check the import fields against the exported fields. 351 * There should not be contradiction (e.g. int x and String x) 352 * but extra virtual fields can be added to the class. 353 */ 354 355 Arrays.sort(exportedFields); 356 357 i = 0; j = 0; k = 0; 358 while (i < fields.length && j < exportedFields.length) 359 { 360 int comp = fields[i].compareTo(exportedFields[j]); 361 362 if (comp < 0) 363 { 364 newFieldList[k] = fields[i]; 365 fields[i].setPersistent(false); 366 fields[i].setToSet(false); 367 i++; 368 } 369 else if (comp > 0) 370 { 371 /* field not found in imported fields. We add it 372 * in the list of supported fields. 373 */ 374 newFieldList[k] = exportedFields[j]; 375 newFieldList[k].setPersistent(true); 376 newFieldList[k].setToSet(false); 377 try 378 { 379 newFieldList[k].lookupField(clazz); 380 newFieldList[k].checkFieldType(); 381 } 382 catch (NoSuchFieldException _) 383 { 384 } 385 j++; 386 } 387 else 388 { 389 try 390 { 391 exportedFields[j].lookupField(clazz); 392 exportedFields[j].checkFieldType(); 393 } 394 catch (NoSuchFieldException _) 395 { 396 } 397 398 if (!fields[i].getType().equals(exportedFields[j].getType())) 399 throw new InvalidClassException 400 ("serialPersistentFields must be compatible with" + 401 " imported fields (about " + fields[i].getName() + ")"); 402 newFieldList[k] = fields[i]; 403 fields[i].setPersistent(true); 404 i++; 405 j++; 406 } 407 k++; 408 } 409 410 if (i < fields.length) 411 for (;i<fields.length;i++,k++) 412 { 413 fields[i].setPersistent(false); 414 fields[i].setToSet(false); 415 newFieldList[k] = fields[i]; 416 } 417 else 418 if (j < exportedFields.length) 419 for (;j<exportedFields.length;j++,k++) 420 { 421 exportedFields[j].setPersistent(true); 422 exportedFields[j].setToSet(false); 423 newFieldList[k] = exportedFields[j]; 424 } 425 426 fields = new ObjectStreamField[k]; 427 System.arraycopy(newFieldList, 0, fields, 0, k); 428 } 429 catch (NoSuchFieldException ignore) 430 { 431 return; 432 } 433 catch (IllegalAccessException ignore) 434 { 435 return; 436 } 437 } 438 setSuperclass(ObjectStreamClass osc)439 void setSuperclass (ObjectStreamClass osc) 440 { 441 superClass = osc; 442 hierarchy = null; 443 } 444 calculateOffsets()445 void calculateOffsets() 446 { 447 int i; 448 ObjectStreamField field; 449 primFieldSize = 0; 450 int fcount = fields.length; 451 for (i = 0; i < fcount; ++ i) 452 { 453 field = fields[i]; 454 455 if (! field.isPrimitive()) 456 break; 457 458 field.setOffset(primFieldSize); 459 switch (field.getTypeCode()) 460 { 461 case 'B': 462 case 'Z': 463 ++ primFieldSize; 464 break; 465 case 'C': 466 case 'S': 467 primFieldSize += 2; 468 break; 469 case 'I': 470 case 'F': 471 primFieldSize += 4; 472 break; 473 case 'D': 474 case 'J': 475 primFieldSize += 8; 476 break; 477 } 478 } 479 480 for (objectFieldCount = 0; i < fcount; ++ i) 481 fields[i].setOffset(objectFieldCount++); 482 } 483 findMethod(Method[] methods, String name, Class[] params, Class returnType, boolean mustBePrivate)484 private Method findMethod(Method[] methods, String name, Class[] params, 485 Class returnType, boolean mustBePrivate) 486 { 487 outer: 488 for (int i = 0; i < methods.length; i++) 489 { 490 final Method m = methods[i]; 491 int mods = m.getModifiers(); 492 if (Modifier.isStatic(mods) 493 || (mustBePrivate && !Modifier.isPrivate(mods))) 494 { 495 continue; 496 } 497 498 if (m.getName().equals(name) 499 && m.getReturnType() == returnType) 500 { 501 Class[] mp = m.getParameterTypes(); 502 if (mp.length == params.length) 503 { 504 for (int j = 0; j < mp.length; j++) 505 { 506 if (mp[j] != params[j]) 507 { 508 continue outer; 509 } 510 } 511 AccessController.doPrivileged(new SetAccessibleAction(m)); 512 return m; 513 } 514 } 515 } 516 return null; 517 } 518 inSamePackage(Class c1, Class c2)519 private static boolean inSamePackage(Class c1, Class c2) 520 { 521 String name1 = c1.getName(); 522 String name2 = c2.getName(); 523 524 int id1 = name1.lastIndexOf('.'); 525 int id2 = name2.lastIndexOf('.'); 526 527 // Handle the default package 528 if (id1 == -1 || id2 == -1) 529 return id1 == id2; 530 531 String package1 = name1.substring(0, id1); 532 String package2 = name2.substring(0, id2); 533 534 return package1.equals(package2); 535 } 536 537 final static Class[] noArgs = new Class[0]; 538 findAccessibleMethod(String name, Class from)539 private static Method findAccessibleMethod(String name, Class from) 540 { 541 for (Class c = from; c != null; c = c.getSuperclass()) 542 { 543 try 544 { 545 Method res = c.getDeclaredMethod(name, noArgs); 546 int mods = res.getModifiers(); 547 548 if (c == from 549 || Modifier.isProtected(mods) 550 || Modifier.isPublic(mods) 551 || (! Modifier.isPrivate(mods) && inSamePackage(c, from))) 552 { 553 AccessController.doPrivileged(new SetAccessibleAction(res)); 554 return res; 555 } 556 } 557 catch (NoSuchMethodException e) 558 { 559 } 560 } 561 562 return null; 563 } 564 565 /** 566 * Helper routine to check if a class was loaded by boot or 567 * application class loader. Classes for which this is not the case 568 * should not be cached since caching prevent class file garbage 569 * collection. 570 * 571 * @param cl a class 572 * 573 * @return true if cl was loaded by boot or application class loader, 574 * false if cl was loaded by a user class loader. 575 */ loadedByBootOrApplicationClassLoader(Class cl)576 private static boolean loadedByBootOrApplicationClassLoader(Class cl) 577 { 578 ClassLoader l = cl.getClassLoader(); 579 return 580 ( l == null /* boot loader */ ) 581 || (l == ClassLoader.getSystemClassLoader() /* application loader */); 582 } 583 584 static Hashtable methodCache = new Hashtable(); 585 586 static final Class[] readObjectSignature = { ObjectInputStream.class }; 587 static final Class[] writeObjectSignature = { ObjectOutputStream.class }; 588 cacheMethods()589 private void cacheMethods() 590 { 591 Class cl = forClass(); 592 Method[] cached = (Method[]) methodCache.get(cl); 593 if (cached == null) 594 { 595 cached = new Method[4]; 596 Method[] methods = cl.getDeclaredMethods(); 597 598 cached[0] = findMethod(methods, "readObject", 599 readObjectSignature, 600 Void.TYPE, true); 601 cached[1] = findMethod(methods, "writeObject", 602 writeObjectSignature, 603 Void.TYPE, true); 604 605 // readResolve and writeReplace can be in parent classes, as long as they 606 // are accessible from this class. 607 cached[2] = findAccessibleMethod("readResolve", cl); 608 cached[3] = findAccessibleMethod("writeReplace", cl); 609 610 /* put in cache if classes not loaded by user class loader. 611 * For a user class loader, the cache may otherwise grow 612 * without limit. 613 */ 614 if (loadedByBootOrApplicationClassLoader(cl)) 615 methodCache.put(cl,cached); 616 } 617 readObjectMethod = cached[0]; 618 writeObjectMethod = cached[1]; 619 readResolveMethod = cached[2]; 620 writeReplaceMethod = cached[3]; 621 } 622 ObjectStreamClass(Class cl)623 private ObjectStreamClass(Class cl) 624 { 625 uid = 0; 626 flags = 0; 627 isProxyClass = Proxy.isProxyClass(cl); 628 629 clazz = cl; 630 cacheMethods(); 631 name = cl.getName(); 632 setFlags(cl); 633 setFields(cl); 634 // to those class nonserializable, its uid field is 0 635 if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) 636 uid = getClassUID(cl); 637 superClass = lookup(cl.getSuperclass()); 638 } 639 640 641 // Sets bits in flags according to features of CL. setFlags(Class cl)642 private void setFlags(Class cl) 643 { 644 if ((java.io.Externalizable.class).isAssignableFrom(cl)) 645 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; 646 else if ((java.io.Serializable.class).isAssignableFrom(cl)) 647 // only set this bit if CL is NOT Externalizable 648 flags |= ObjectStreamConstants.SC_SERIALIZABLE; 649 650 if (writeObjectMethod != null) 651 flags |= ObjectStreamConstants.SC_WRITE_METHOD; 652 653 if (cl.isEnum() || cl == Enum.class) 654 flags |= ObjectStreamConstants.SC_ENUM; 655 } 656 657 /* GCJ LOCAL */ 658 // FIXME: This is a workaround for a fairly obscure bug that happens 659 // when reading a Proxy and then writing it back out again. The 660 // result is that the ObjectStreamClass doesn't have its fields set, 661 // generating a NullPointerException. Rather than this kludge we 662 // should probably fix the real bug, but it would require a fairly 663 // radical reorganization to do so. ensureFieldsSet(Class cl)664 final void ensureFieldsSet(Class cl) 665 { 666 if (! fieldsSet) 667 setFields(cl); 668 } 669 /* END GCJ LOCAL */ 670 671 672 // Sets fields to be a sorted array of the serializable fields of 673 // clazz. setFields(Class cl)674 private void setFields(Class cl) 675 { 676 /* GCJ LOCAL */ 677 fieldsSet = true; 678 /* END GCJ LOCAL */ 679 680 SetAccessibleAction setAccessible = new SetAccessibleAction(); 681 682 if (!isSerializable() || isExternalizable() || isEnum()) 683 { 684 fields = NO_FIELDS; 685 return; 686 } 687 688 try 689 { 690 final Field f = 691 cl.getDeclaredField("serialPersistentFields"); 692 setAccessible.setMember(f); 693 AccessController.doPrivileged(setAccessible); 694 int modifiers = f.getModifiers(); 695 696 if (Modifier.isStatic(modifiers) 697 && Modifier.isFinal(modifiers) 698 && Modifier.isPrivate(modifiers)) 699 { 700 fields = getSerialPersistentFields(cl); 701 if (fields != null) 702 { 703 ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length]; 704 System.arraycopy(fields, 0, fieldsName, 0, fields.length); 705 706 Arrays.sort (fieldsName, new Comparator() { 707 public int compare(Object o1, Object o2) 708 { 709 ObjectStreamField f1 = (ObjectStreamField)o1; 710 ObjectStreamField f2 = (ObjectStreamField)o2; 711 712 return f1.getName().compareTo(f2.getName()); 713 } 714 }); 715 716 for (int i=1; i < fields.length; i++) 717 { 718 if (fieldsName[i-1].getName().equals(fieldsName[i].getName())) 719 { 720 fields = INVALID_FIELDS; 721 return; 722 } 723 } 724 725 Arrays.sort (fields); 726 // Retrieve field reference. 727 for (int i=0; i < fields.length; i++) 728 { 729 try 730 { 731 fields[i].lookupField(cl); 732 } 733 catch (NoSuchFieldException _) 734 { 735 fields[i].setToSet(false); 736 } 737 } 738 739 calculateOffsets(); 740 return; 741 } 742 } 743 } 744 catch (NoSuchFieldException ignore) 745 { 746 } 747 catch (IllegalAccessException ignore) 748 { 749 } 750 751 int num_good_fields = 0; 752 Field[] all_fields = cl.getDeclaredFields(); 753 754 int modifiers; 755 // set non-serializable fields to null in all_fields 756 for (int i = 0; i < all_fields.length; i++) 757 { 758 modifiers = all_fields[i].getModifiers(); 759 if (Modifier.isTransient(modifiers) 760 || Modifier.isStatic(modifiers)) 761 all_fields[i] = null; 762 else 763 num_good_fields++; 764 } 765 766 // make a copy of serializable (non-null) fields 767 fields = new ObjectStreamField[ num_good_fields ]; 768 for (int from = 0, to = 0; from < all_fields.length; from++) 769 if (all_fields[from] != null) 770 { 771 final Field f = all_fields[from]; 772 setAccessible.setMember(f); 773 AccessController.doPrivileged(setAccessible); 774 fields[to] = new ObjectStreamField(all_fields[from]); 775 to++; 776 } 777 778 Arrays.sort(fields); 779 // Make sure we don't have any duplicate field names 780 // (Sun JDK 1.4.1. throws an Internal Error as well) 781 for (int i = 1; i < fields.length; i++) 782 { 783 if(fields[i - 1].getName().equals(fields[i].getName())) 784 throw new InternalError("Duplicate field " + 785 fields[i].getName() + " in class " + cl.getName()); 786 } 787 calculateOffsets(); 788 } 789 790 static Hashtable uidCache = new Hashtable(); 791 792 // Returns the serial version UID defined by class, or if that 793 // isn't present, calculates value of serial version UID. getClassUID(Class cl)794 private long getClassUID(Class cl) 795 { 796 long result = 0; 797 Long cache = (Long) uidCache.get(cl); 798 if (cache != null) 799 result = cache.longValue(); 800 else 801 { 802 // Note that we can't use Class.isEnum() here, because that returns 803 // false for java.lang.Enum and enum value sub classes. 804 if (Enum.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl)) 805 { 806 // Spec says that enums and dynamic proxies have 807 // a serialVersionUID of 0L. 808 return 0L; 809 } 810 try 811 { 812 result = getClassUIDFromField(cl); 813 } 814 catch (NoSuchFieldException ignore) 815 { 816 try 817 { 818 result = calculateClassUID(cl); 819 } 820 catch (NoSuchAlgorithmException e) 821 { 822 throw new RuntimeException 823 ("The SHA algorithm was not found to use in computing the Serial Version UID for class " 824 + cl.getName(), e); 825 } 826 catch (IOException ioe) 827 { 828 throw new RuntimeException(ioe); 829 } 830 } 831 832 if (loadedByBootOrApplicationClassLoader(cl)) 833 uidCache.put(cl,Long.valueOf(result)); 834 } 835 return result; 836 } 837 838 /** 839 * Search for a serialVersionUID field in the given class and read 840 * its value. 841 * 842 * @return the contents of the serialVersionUID field 843 * 844 * @throws NoSuchFieldException if such a field does not exist or is 845 * not static, not final, not of type Long or not accessible. 846 */ getClassUIDFromField(Class cl)847 long getClassUIDFromField(Class cl) 848 throws NoSuchFieldException 849 { 850 long result; 851 852 try 853 { 854 // Use getDeclaredField rather than getField, since serialVersionUID 855 // may not be public AND we only want the serialVersionUID of this 856 // class, not a superclass or interface. 857 final Field suid = cl.getDeclaredField("serialVersionUID"); 858 SetAccessibleAction setAccessible = new SetAccessibleAction(suid); 859 AccessController.doPrivileged(setAccessible); 860 int modifiers = suid.getModifiers(); 861 862 if (Modifier.isStatic(modifiers) 863 && Modifier.isFinal(modifiers) 864 && suid.getType() == Long.TYPE) 865 result = suid.getLong(null); 866 else 867 throw new NoSuchFieldException(); 868 } 869 catch (IllegalAccessException ignore) 870 { 871 throw new NoSuchFieldException(); 872 } 873 874 return result; 875 } 876 877 /** 878 * Calculate class serial version UID for a class that does not 879 * define serialVersionUID: 880 * 881 * @param cl a class 882 * 883 * @return the calculated serial varsion UID. 884 * 885 * @throws NoSuchAlgorithmException if SHA algorithm not found 886 * 887 * @throws IOException if writing to the DigestOutputStream causes 888 * an IOException. 889 */ calculateClassUID(Class cl)890 long calculateClassUID(Class cl) 891 throws NoSuchAlgorithmException, IOException 892 { 893 long result; 894 MessageDigest md; 895 try 896 { 897 md = MessageDigest.getInstance("SHA"); 898 } 899 catch (NoSuchAlgorithmException e) 900 { 901 // If a provider already provides SHA, use it; otherwise, use this. 902 Gnu gnuProvider = new Gnu(); 903 Security.addProvider(gnuProvider); 904 md = MessageDigest.getInstance("SHA"); 905 } 906 907 DigestOutputStream digest_out = 908 new DigestOutputStream(nullOutputStream, md); 909 DataOutputStream data_out = new DataOutputStream(digest_out); 910 911 data_out.writeUTF(cl.getName()); 912 913 int modifiers = cl.getModifiers(); 914 // just look at interesting bits 915 modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL 916 | Modifier.INTERFACE | Modifier.PUBLIC); 917 data_out.writeInt(modifiers); 918 919 // Pretend that an array has no interfaces, because when array 920 // serialization was defined (JDK 1.1), arrays didn't have it. 921 if (! cl.isArray()) 922 { 923 Class[] interfaces = cl.getInterfaces(); 924 Arrays.sort(interfaces, interfaceComparator); 925 for (int i = 0; i < interfaces.length; i++) 926 data_out.writeUTF(interfaces[i].getName()); 927 } 928 929 Field field; 930 Field[] fields = cl.getDeclaredFields(); 931 Arrays.sort(fields, memberComparator); 932 for (int i = 0; i < fields.length; i++) 933 { 934 field = fields[i]; 935 modifiers = field.getModifiers(); 936 if (Modifier.isPrivate(modifiers) 937 && (Modifier.isStatic(modifiers) 938 || Modifier.isTransient(modifiers))) 939 continue; 940 941 data_out.writeUTF(field.getName()); 942 data_out.writeInt(modifiers); 943 data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); 944 } 945 946 // write class initializer method if present 947 if (VMObjectStreamClass.hasClassInitializer(cl)) 948 { 949 data_out.writeUTF("<clinit>"); 950 data_out.writeInt(Modifier.STATIC); 951 data_out.writeUTF("()V"); 952 } 953 954 Constructor constructor; 955 Constructor[] constructors = cl.getDeclaredConstructors(); 956 Arrays.sort (constructors, memberComparator); 957 for (int i = 0; i < constructors.length; i++) 958 { 959 constructor = constructors[i]; 960 modifiers = constructor.getModifiers(); 961 if (Modifier.isPrivate(modifiers)) 962 continue; 963 964 data_out.writeUTF("<init>"); 965 data_out.writeInt(modifiers); 966 967 // the replacement of '/' with '.' was needed to make computed 968 // SUID's agree with those computed by JDK 969 data_out.writeUTF 970 (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); 971 } 972 973 Method method; 974 Method[] methods = cl.getDeclaredMethods(); 975 Arrays.sort(methods, memberComparator); 976 for (int i = 0; i < methods.length; i++) 977 { 978 method = methods[i]; 979 modifiers = method.getModifiers(); 980 if (Modifier.isPrivate(modifiers)) 981 continue; 982 983 data_out.writeUTF(method.getName()); 984 data_out.writeInt(modifiers); 985 986 // the replacement of '/' with '.' was needed to make computed 987 // SUID's agree with those computed by JDK 988 data_out.writeUTF 989 (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); 990 } 991 992 data_out.close(); 993 byte[] sha = md.digest(); 994 result = 0; 995 int len = sha.length < 8 ? sha.length : 8; 996 for (int i = 0; i < len; i++) 997 result += (long) (sha[i] & 0xFF) << (8 * i); 998 999 return result; 1000 } 1001 1002 /** 1003 * Returns the value of CLAZZ's private static final field named 1004 * `serialPersistentFields'. It performs some sanity checks before 1005 * returning the real array. Besides, the returned array is a clean 1006 * copy of the original. So it can be modified. 1007 * 1008 * @param clazz Class to retrieve 'serialPersistentFields' from. 1009 * @return The content of 'serialPersistentFields'. 1010 */ 1011 private ObjectStreamField[] getSerialPersistentFields(Class clazz) 1012 throws NoSuchFieldException, IllegalAccessException 1013 { 1014 ObjectStreamField[] fieldsArray = null; 1015 ObjectStreamField[] o; 1016 1017 // Use getDeclaredField rather than getField for the same reason 1018 // as above in getDefinedSUID. 1019 Field f = clazz.getDeclaredField("serialPersistentFields"); 1020 f.setAccessible(true); 1021 1022 int modifiers = f.getModifiers(); 1023 if (!(Modifier.isStatic(modifiers) && 1024 Modifier.isFinal(modifiers) && 1025 Modifier.isPrivate(modifiers))) 1026 return null; 1027 1028 o = (ObjectStreamField[]) f.get(null); 1029 1030 if (o == null) 1031 return null; 1032 1033 fieldsArray = new ObjectStreamField[ o.length ]; 1034 System.arraycopy(o, 0, fieldsArray, 0, o.length); 1035 1036 return fieldsArray; 1037 } 1038 1039 /** 1040 * Returns a new instance of the Class this ObjectStreamClass corresponds 1041 * to. 1042 * Note that this should only be used for Externalizable classes. 1043 * 1044 * @return A new instance. 1045 */ 1046 Externalizable newInstance() throws InvalidClassException 1047 { 1048 synchronized(this) 1049 { 1050 if (constructor == null) 1051 { 1052 try 1053 { 1054 final Constructor c = clazz.getConstructor(new Class[0]); 1055 1056 AccessController.doPrivileged(new PrivilegedAction() 1057 { 1058 public Object run() 1059 { 1060 c.setAccessible(true); 1061 return null; 1062 } 1063 }); 1064 1065 constructor = c; 1066 } 1067 catch(NoSuchMethodException x) 1068 { 1069 throw new InvalidClassException(clazz.getName(), 1070 "No public zero-argument constructor"); 1071 } 1072 } 1073 } 1074 1075 try 1076 { 1077 return (Externalizable)constructor.newInstance(); 1078 } 1079 catch(Exception x) 1080 { 1081 throw (InvalidClassException) 1082 new InvalidClassException(clazz.getName(), 1083 "Unable to instantiate").initCause(x); 1084 } 1085 } 1086 1087 public static final ObjectStreamField[] NO_FIELDS = {}; 1088 1089 private static Hashtable<Class,ObjectStreamClass> classLookupTable 1090 = new Hashtable<Class,ObjectStreamClass>(); 1091 private static final NullOutputStream nullOutputStream = new NullOutputStream(); 1092 private static final Comparator interfaceComparator = new InterfaceComparator(); 1093 private static final Comparator memberComparator = new MemberComparator(); 1094 private static final 1095 Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; 1096 1097 private ObjectStreamClass superClass; 1098 private Class<?> clazz; 1099 private String name; 1100 private long uid; 1101 private byte flags; 1102 1103 // this field is package protected so that ObjectInputStream and 1104 // ObjectOutputStream can access it directly 1105 ObjectStreamField[] fields; 1106 1107 // these are accessed by ObjectIn/OutputStream 1108 int primFieldSize = -1; // -1 if not yet calculated 1109 int objectFieldCount; 1110 1111 Method readObjectMethod; 1112 Method readResolveMethod; 1113 Method writeReplaceMethod; 1114 Method writeObjectMethod; 1115 boolean realClassIsSerializable; 1116 boolean realClassIsExternalizable; 1117 ObjectStreamField[] fieldMapping; 1118 Constructor firstNonSerializableParentConstructor; 1119 private Constructor constructor; // default constructor for Externalizable 1120 1121 boolean isProxyClass = false; 1122 1123 /* GCJ LOCAL */ 1124 // True after setFields() has been called 1125 private boolean fieldsSet = false; 1126 /* END GCJ LOCAL */ 1127 1128 // This is probably not necessary because this class is special cased already 1129 // but it will avoid showing up as a discrepancy when comparing SUIDs. 1130 private static final long serialVersionUID = -6120832682080437368L; 1131 1132 1133 // interfaces are compared only by name 1134 private static final class InterfaceComparator implements Comparator 1135 { 1136 public int compare(Object o1, Object o2) 1137 { 1138 return ((Class) o1).getName().compareTo(((Class) o2).getName()); 1139 } 1140 } 1141 1142 1143 // Members (Methods and Constructors) are compared first by name, 1144 // conflicts are resolved by comparing type signatures 1145 private static final class MemberComparator implements Comparator 1146 { 1147 public int compare(Object o1, Object o2) 1148 { 1149 Member m1 = (Member) o1; 1150 Member m2 = (Member) o2; 1151 1152 int comp = m1.getName().compareTo(m2.getName()); 1153 1154 if (comp == 0) 1155 return TypeSignature.getEncodingOfMember(m1). 1156 compareTo(TypeSignature.getEncodingOfMember(m2)); 1157 else 1158 return comp; 1159 } 1160 } 1161 } 1162