Source for java.io.ObjectStreamClass

   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:    */
  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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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.
 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:    */
 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.
 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.
 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.
 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.
 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.
 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:    */
 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_'
 288:   int getFlags()
 289:   {
 290:     return flags;
 291:   }
 292: 
 293: 
 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:    */
 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: 
 439:   void setSuperclass (ObjectStreamClass osc)
 440:   {
 441:     superClass = osc;
 442:     hierarchy = null;
 443:   }
 444: 
 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: 
 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: 
 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: 
 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:    */
 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: 
 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: 
 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.
 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.
 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.
 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.
 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:    */
 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:    */
 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: }