1 /*
   2  * Copyright (c) 2001, 2012, 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  * Licensed Materials - Property of IBM
  27  * RMI-IIOP v1.0
  28  * Copyright IBM Corp. 1998 1999  All Rights Reserved
  29  *
  30  */
  31 
  32 package com.sun.corba.se.impl.orbutil;
  33 
  34 import java.security.MessageDigest;
  35 import java.security.NoSuchAlgorithmException;
  36 import java.security.DigestOutputStream;
  37 import java.security.AccessController;
  38 import java.security.PrivilegedExceptionAction;
  39 import java.security.PrivilegedActionException;
  40 import java.security.PrivilegedAction;
  41 
  42 import java.lang.reflect.Modifier;
  43 import java.lang.reflect.Array;
  44 import java.lang.reflect.Field;
  45 import java.lang.reflect.Member;
  46 import java.lang.reflect.Method;
  47 import java.lang.reflect.Constructor;
  48 import java.lang.reflect.Proxy;
  49 import java.lang.reflect.InvocationTargetException;
  50 
  51 import java.io.IOException;
  52 import java.io.DataOutputStream;
  53 import java.io.ByteArrayOutputStream;
  54 import java.io.InvalidClassException;
  55 import java.io.Serializable;
  56 
  57 import java.util.Arrays;
  58 import java.util.Comparator;
  59 import java.util.Hashtable;
  60 
  61 import org.omg.CORBA.ValueMember;
  62 
  63 import com.sun.corba.se.impl.io.ValueUtility;
  64 import com.sun.corba.se.impl.io.ObjectStreamClass;
  65 
  66 /**
  67  * This is duplicated here to preserve the JDK 1.3.1FCS behavior
  68  * of calculating the OMG hash code incorrectly when serialPersistentFields
  69  * is used, but some of the fields no longer exist in the class itself.
  70  *
  71  * We have to duplicate it since we aren't allowed to modify the
  72  * com.sun.corba.se.impl.io version further, and can't make it
  73  * public outside of its package for security reasons.
  74  */
  75 /**
  76  * A ObjectStreamClass_1_3_1 describes a class that can be serialized to a stream
  77  * or a class that was serialized to a stream.  It contains the name
  78  * and the serialVersionUID of the class.
  79  * <br>
  80  * The ObjectStreamClass_1_3_1 for a specific class loaded in this Java VM can
  81  * be found using the lookup method.
  82  *
  83  * @author  Roger Riggs
  84  * @since   JDK1.1
  85  */
  86 public class ObjectStreamClass_1_3_1 implements java.io.Serializable {
  87 
  88     public static final long kDefaultUID = -1;
  89 
  90     private static Object noArgsList[] = {};
  91     private static Class noTypesList[] = {};
  92 
  93     private static Hashtable translatedFields;
  94 
  95     /** Find the descriptor for a class that can be serialized.  Null
  96      * is returned if the specified class does not implement
  97      * java.io.Serializable or java.io.Externalizable.
  98      */
  99     static final ObjectStreamClass_1_3_1 lookup(Class cl)
 100     {
 101         ObjectStreamClass_1_3_1 desc = lookupInternal(cl);
 102         if (desc.isSerializable() || desc.isExternalizable())
 103             return desc;
 104         return null;
 105     }
 106 
 107     /*
 108      * Find the class descriptor for the specified class.
 109      * Package access only so it can be called from ObjectIn/OutStream.
 110      */
 111     static ObjectStreamClass_1_3_1 lookupInternal(Class cl)
 112     {
 113         /* Synchronize on the hashtable so no two threads will do
 114          * this at the same time.
 115          */
 116         ObjectStreamClass_1_3_1 desc = null;
 117         synchronized (descriptorFor) {
 118             /* Find the matching descriptor if it already known */
 119             desc = findDescriptorFor(cl);
 120             if (desc != null) {
 121                 return desc;
 122             }
 123 
 124                 /* Check if it's serializable */
 125                 boolean serializable = classSerializable.isAssignableFrom(cl);
 126                 /* If the class is only Serializable,
 127                  * lookup the descriptor for the superclass.
 128                  */
 129                 ObjectStreamClass_1_3_1 superdesc = null;
 130                 if (serializable) {
 131                     Class superclass = cl.getSuperclass();
 132                     if (superclass != null)
 133                         superdesc = lookup(superclass);
 134                 }
 135 
 136                 /* Check if its' externalizable.
 137                  * If it's Externalizable, clear the serializable flag.
 138                  * Only one or the other may be set in the protocol.
 139                  */
 140                 boolean externalizable = false;
 141                 if (serializable) {
 142                     externalizable =
 143                         ((superdesc != null) && superdesc.isExternalizable()) ||
 144                         classExternalizable.isAssignableFrom(cl);
 145                     if (externalizable) {
 146                         serializable = false;
 147                     }
 148                 }
 149 
 150             /* Create a new version descriptor,
 151              * it put itself in the known table.
 152              */
 153             desc = new ObjectStreamClass_1_3_1(cl, superdesc,
 154                                          serializable, externalizable);
 155         }
 156         desc.init();
 157         return desc;
 158     }
 159 
 160     /**
 161      * The name of the class described by this descriptor.
 162      */
 163     public final String getName() {
 164         return name;
 165     }
 166 
 167     /**
 168      * Return the serialVersionUID for this class.
 169      * The serialVersionUID defines a set of classes all with the same name
 170      * that have evolved from a common root class and agree to be serialized
 171      * and deserialized using a common format.
 172      */
 173     public static final long getSerialVersionUID( java.lang.Class clazz) {
 174         ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
 175         if( theosc != null )
 176         {
 177                 return theosc.getSerialVersionUID( );
 178         }
 179         return 0;
 180     }
 181 
 182     /**
 183      * Return the serialVersionUID for this class.
 184      * The serialVersionUID defines a set of classes all with the same name
 185      * that have evolved from a common root class and agree to be serialized
 186      * and deserialized using a common format.
 187      */
 188     public final long getSerialVersionUID() {
 189         return suid;
 190     }
 191 
 192     /**
 193      * Return the serialVersionUID string for this class.
 194      * The serialVersionUID defines a set of classes all with the same name
 195      * that have evolved from a common root class and agree to be serialized
 196      * and deserialized using a common format.
 197      */
 198     public final String getSerialVersionUIDStr() {
 199         if (suidStr == null)
 200             suidStr = Long.toHexString(suid).toUpperCase();
 201         return suidStr;
 202     }
 203 
 204     /**
 205      * Return the actual (computed) serialVersionUID for this class.
 206      */
 207     public static final long getActualSerialVersionUID( java.lang.Class clazz )
 208     {
 209         ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
 210         if( theosc != null )
 211         {
 212                 return theosc.getActualSerialVersionUID( );
 213         }
 214         return 0;
 215     }
 216 
 217     /**
 218      * Return the actual (computed) serialVersionUID for this class.
 219      */
 220     public final long getActualSerialVersionUID() {
 221         return actualSuid;
 222     }
 223 
 224     /**
 225      * Return the actual (computed) serialVersionUID for this class.
 226      */
 227     public final String getActualSerialVersionUIDStr() {
 228         if (actualSuidStr == null)
 229             actualSuidStr = Long.toHexString(actualSuid).toUpperCase();
 230         return actualSuidStr;
 231     }
 232 
 233     /**
 234      * Return the class in the local VM that this version is mapped to.
 235      * Null is returned if there is no corresponding local class.
 236      */
 237     public final Class forClass() {
 238         return ofClass;
 239     }
 240 
 241     /**
 242      * Return an array of the fields of this serializable class.
 243      * @return an array containing an element for each persistent
 244      * field of this class. Returns an array of length zero if
 245      * there are no fields.
 246      * @since JDK1.2
 247      */
 248     public ObjectStreamField[] getFields() {
 249         // Return a copy so the caller can't change the fields.
 250         if (fields.length > 0) {
 251             ObjectStreamField[] dup = new ObjectStreamField[fields.length];
 252             System.arraycopy(fields, 0, dup, 0, fields.length);
 253             return dup;
 254         } else {
 255             return fields;
 256         }
 257     }
 258 
 259     public boolean hasField(ValueMember field){
 260 
 261         for (int i = 0; i < fields.length; i++){
 262             try{
 263                 if (fields[i].getName().equals(field.name)) {
 264 
 265                     if (fields[i].getSignature().equals(ValueUtility.getSignature(field)))
 266                         return true;
 267                 }
 268             }
 269             catch(Throwable t){}
 270         }
 271         return false;
 272     }
 273 
 274     /* Avoid unnecessary allocations. */
 275     final ObjectStreamField[] getFieldsNoCopy() {
 276         return fields;
 277     }
 278 
 279     /**
 280      * Get the field of this class by name.
 281      * @return The ObjectStreamField object of the named field or null if there
 282      * is no such named field.
 283      */
 284     public final ObjectStreamField getField(String name) {
 285         /* Binary search of fields by name.
 286          */
 287         for (int i = fields.length-1; i >= 0; i--) {
 288             if (name.equals(fields[i].getName())) {
 289                 return fields[i];
 290             }
 291         }
 292         return null;
 293     }
 294 
 295     public Serializable writeReplace(Serializable value) {
 296         if (writeReplaceObjectMethod != null) {
 297             try {
 298                 return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList);
 299             }
 300             catch(Throwable t) {
 301                 throw new RuntimeException(t.getMessage());
 302             }
 303         }
 304         else return value;
 305     }
 306 
 307     public Object readResolve(Object value) {
 308         if (readResolveObjectMethod != null) {
 309             try {
 310                 return readResolveObjectMethod.invoke(value,noArgsList);
 311             }
 312             catch(Throwable t) {
 313                 throw new RuntimeException(t.getMessage());
 314             }
 315         }
 316         else return value;
 317     }
 318 
 319     /**
 320      * Return a string describing this ObjectStreamClass_1_3_1.
 321      */
 322     public final String toString() {
 323         StringBuffer sb = new StringBuffer();
 324 
 325         sb.append(name);
 326         sb.append(": static final long serialVersionUID = ");
 327         sb.append(Long.toString(suid));
 328         sb.append("L;");
 329         return sb.toString();
 330     }
 331 
 332     /*
 333      * Create a new ObjectStreamClass_1_3_1 from a loaded class.
 334      * Don't call this directly, call lookup instead.
 335      */
 336     private ObjectStreamClass_1_3_1(java.lang.Class cl, ObjectStreamClass_1_3_1 superdesc,
 337                               boolean serial, boolean extern)
 338     {
 339         ofClass = cl;           /* created from this class */
 340 
 341         if (Proxy.isProxyClass(cl)) {
 342             forProxyClass = true;
 343         }
 344 
 345         name = cl.getName();
 346         superclass = superdesc;
 347         serializable = serial;
 348         if (!forProxyClass) {
 349             // proxy classes are never externalizable
 350             externalizable = extern;
 351         }
 352 
 353         /*
 354          * Enter this class in the table of known descriptors.
 355          * Otherwise, when the fields are read it may recurse
 356          * trying to find the descriptor for itself.
 357          */
 358         insertDescriptorFor(this);
 359 
 360         /*
 361          * The remainder of initialization occurs in init(), which is called
 362          * after the lock on the global class descriptor table has been
 363          * released.
 364          */
 365     }
 366 
 367     /*
 368      * Initialize class descriptor.  This method is only invoked on class
 369      * descriptors created via calls to lookupInternal().  This method is kept
 370      * separate from the ObjectStreamClass_1_3_1 constructor so that lookupInternal
 371      * does not have to hold onto a global class descriptor table lock while the
 372      * class descriptor is being initialized (see bug 4165204).
 373      */
 374 
 375 
 376     private void init() {
 377       synchronized (lock) {
 378 
 379         final Class cl = ofClass;
 380 
 381         if (fields != null) // already initialized
 382                 return;
 383 
 384 
 385         if (!serializable ||
 386             externalizable ||
 387             forProxyClass ||
 388             name.equals("java.lang.String")) {
 389             fields = NO_FIELDS;
 390         } else if (serializable) {
 391 
 392             /* Ask for permission to override field access checks.
 393              */
 394             AccessController.doPrivileged(new PrivilegedAction() {
 395                 public Object run() {
 396                 /* Fill in the list of persistent fields.
 397                  * If it is declared, use the declared serialPersistentFields.
 398                  * Otherwise, extract the fields from the class itself.
 399                  */
 400                 try {
 401                     Field pf = cl.getDeclaredField("serialPersistentFields");
 402                     // serial bug 7; the serialPersistentFields were not
 403                     // being read and stored as Accessible bit was not set
 404                     pf.setAccessible(true);
 405                     // serial bug 7; need to find if the field is of type
 406                     // java.io.ObjectStreamField
 407                     java.io.ObjectStreamField[] f =
 408                            (java.io.ObjectStreamField[])pf.get(cl);
 409                     int mods = pf.getModifiers();
 410                     if ((Modifier.isPrivate(mods)) &&
 411                         (Modifier.isStatic(mods)) &&
 412                         (Modifier.isFinal(mods)))
 413                     {
 414                         fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl));
 415                     }
 416                 } catch (NoSuchFieldException e) {
 417                     fields = null;
 418                 } catch (IllegalAccessException e) {
 419                     fields = null;
 420                 } catch (IllegalArgumentException e) {
 421                     fields = null;
 422                 } catch (ClassCastException e) {
 423                     /* Thrown if a field serialPersistentField exists
 424                      * but it is not of type ObjectStreamField.
 425                      */
 426                     fields = null;
 427                 }
 428 
 429 
 430                 if (fields == null) {
 431                     /* Get all of the declared fields for this
 432                      * Class. setAccessible on all fields so they
 433                      * can be accessed later.  Create a temporary
 434                      * ObjectStreamField array to hold each
 435                      * non-static, non-transient field. Then copy the
 436                      * temporary array into an array of the correct
 437                      * size once the number of fields is known.
 438                      */
 439                     Field[] actualfields = cl.getDeclaredFields();
 440 
 441                     int numFields = 0;
 442                     ObjectStreamField[] tempFields =
 443                         new ObjectStreamField[actualfields.length];
 444                     for (int i = 0; i < actualfields.length; i++) {
 445                         int modifiers = actualfields[i].getModifiers();
 446                         if (!Modifier.isStatic(modifiers) &&
 447                             !Modifier.isTransient(modifiers)) {
 448                             tempFields[numFields++] =
 449                                 new ObjectStreamField(actualfields[i]);
 450                         }
 451                     }
 452                     fields = new ObjectStreamField[numFields];
 453                     System.arraycopy(tempFields, 0, fields, 0, numFields);
 454 
 455                 } else {
 456                     // For each declared persistent field, look for an actual
 457                     // reflected Field. If there is one, make sure it's the correct
 458                     // type and cache it in the ObjectStreamClass_1_3_1 for that field.
 459                     for (int j = fields.length-1; j >= 0; j--) {
 460                         try {
 461                             Field reflField = cl.getDeclaredField(fields[j].getName());
 462                             if (fields[j].getType() == reflField.getType()) {
 463                                 // reflField.setAccessible(true);
 464                                 fields[j].setField(reflField);
 465                             }
 466                         } catch (NoSuchFieldException e) {
 467                             // Nothing to do
 468                         }
 469                     }
 470                 }
 471                 return null;
 472             }
 473             });
 474 
 475             if (fields.length > 1)
 476                 Arrays.sort(fields);
 477 
 478             /* Set up field data for use while writing using the API api. */
 479             computeFieldInfo();
 480         }
 481 
 482         /* Get the serialVersionUID from the class.
 483          * It uses the access override mechanism so make sure
 484          * the field objects is only used here.
 485          *
 486          * NonSerializable classes have a serialVerisonUID of 0L.
 487          */
 488          if (isNonSerializable()) {
 489              suid = 0L;
 490          } else {
 491              // Lookup special Serializable members using reflection.
 492              AccessController.doPrivileged(new PrivilegedAction() {
 493                 public Object run() {
 494                 if (forProxyClass) {
 495                     // proxy classes always have serialVersionUID of 0L
 496                     suid = 0L;
 497                 } else {
 498                     try {
 499                         final Field f = cl.getDeclaredField("serialVersionUID");
 500                         int mods = f.getModifiers();
 501                     // SerialBug 5:  static final SUID should be read
 502                         if (Modifier.isStatic(mods) &&
 503                             Modifier.isFinal(mods) ) {
 504                             f.setAccessible(true);
 505                             suid = f.getLong(cl);
 506                             // get rid of native code
 507                             // suid = getSerialVersionUIDField(cl);
 508                     // SerialBug 2: should be computed after writeObject
 509                     // actualSuid = computeStructuralUID(cl);
 510                         } else {
 511                             suid = ObjectStreamClass.getSerialVersionUID(cl);
 512                             // SerialBug 2: should be computed after writeObject
 513                             // actualSuid = computeStructuralUID(cl);
 514                         }
 515                     } catch (NoSuchFieldException ex) {
 516                         suid = ObjectStreamClass.getSerialVersionUID(cl);
 517                         // SerialBug 2: should be computed after writeObject
 518                         // actualSuid = computeStructuralUID(cl);
 519                     } catch (IllegalAccessException ex) {
 520                         suid = ObjectStreamClass.getSerialVersionUID(cl);
 521                     }
 522                 }
 523 
 524 
 525                 try {
 526                     writeReplaceObjectMethod = cl.getDeclaredMethod("writeReplace", noTypesList);
 527                     if (Modifier.isStatic(writeReplaceObjectMethod.getModifiers())) {
 528                         writeReplaceObjectMethod = null;
 529                     } else {
 530                         writeReplaceObjectMethod.setAccessible(true);
 531                     }
 532 
 533                 } catch (NoSuchMethodException e2) {
 534 
 535                 }
 536 
 537                 try {
 538                     readResolveObjectMethod = cl.getDeclaredMethod("readResolve", noTypesList);
 539                     if (Modifier.isStatic(readResolveObjectMethod.getModifiers())) {
 540                        readResolveObjectMethod = null;
 541                     } else {
 542                        readResolveObjectMethod.setAccessible(true);
 543                     }
 544 
 545                 } catch (NoSuchMethodException e2) {
 546 
 547                 }
 548 
 549                 /* Cache lookup of writeObject and readObject for
 550                  * Serializable classes. (Do not lookup for
 551                  * Externalizable)
 552                  */
 553 
 554                 if (serializable && !forProxyClass) {
 555 
 556                     /* Look for the writeObject method
 557                      * Set the accessible flag on it here. ObjectOutputStream
 558                      * will call it as necessary.
 559                      */
 560                     try {
 561                       Class[] args = {java.io.ObjectOutputStream.class};
 562                       writeObjectMethod = cl.getDeclaredMethod("writeObject", args);
 563                       hasWriteObjectMethod = true;
 564                       int mods = writeObjectMethod.getModifiers();
 565 
 566                       // Method must be private and non-static
 567                       if (!Modifier.isPrivate(mods) ||
 568                         Modifier.isStatic(mods)) {
 569                         writeObjectMethod = null;
 570                         hasWriteObjectMethod = false;
 571                       }
 572 
 573                     } catch (NoSuchMethodException e) {
 574                     }
 575 
 576                     /* Look for the readObject method
 577                      * set the access override and save the reference for
 578                      * ObjectInputStream so it can all the method directly.
 579                      */
 580                     try {
 581                       Class[] args = {java.io.ObjectInputStream.class};
 582                       readObjectMethod = cl.getDeclaredMethod("readObject", args);
 583                       int mods = readObjectMethod.getModifiers();
 584 
 585                       // Method must be private and non-static
 586                       if (!Modifier.isPrivate(mods) ||
 587                         Modifier.isStatic(mods)) {
 588                         readObjectMethod = null;
 589                       }
 590                     } catch (NoSuchMethodException e) {
 591                     }
 592                     // Compute the structural UID.  This must be done after the
 593                     // calculation for writeObject.  Fixed 4/20/2000, eea1
 594                     // SerialBug 2: to have correct value in RepId
 595                 }
 596                 return null;
 597             }
 598           });
 599         }
 600 
 601         actualSuid = computeStructuralUID(this, cl);
 602       }
 603 
 604     }
 605 
 606     /*
 607      * Create an empty ObjectStreamClass_1_3_1 for a class about to be read.
 608      * This is separate from read so ObjectInputStream can assign the
 609      * wire handle early, before any nested ObjectStreamClass_1_3_1 might
 610      * be read.
 611      */
 612     ObjectStreamClass_1_3_1(String n, long s) {
 613         name = n;
 614         suid = s;
 615         superclass = null;
 616     }
 617 
 618     private static Object[] translateFields(Object objs[])
 619         throws NoSuchFieldException {
 620         try{
 621             java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs;
 622             Object translation[] = null;
 623 
 624             if (translatedFields == null)
 625                 translatedFields = new Hashtable();
 626 
 627             translation = (Object[])translatedFields.get(fields);
 628 
 629             if (translation != null)
 630                 return translation;
 631             else {
 632                 Class osfClass = com.sun.corba.se.impl.orbutil.ObjectStreamField.class;
 633 
 634                 translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length);
 635                 Object arg[] = new Object[2];
 636                 Class types[] = {String.class, Class.class};
 637                 Constructor constructor = osfClass.getDeclaredConstructor(types);
 638                 for (int i = fields.length -1; i >= 0; i--){
 639                     arg[0] = fields[i].getName();
 640                     arg[1] = fields[i].getType();
 641 
 642                     translation[i] = constructor.newInstance(arg);
 643                 }
 644                 translatedFields.put(fields, translation);
 645 
 646             }
 647 
 648             return (Object[])translation;
 649         }
 650         catch(Throwable t){
 651             throw new NoSuchFieldException();
 652         }
 653     }
 654 
 655     /* Compare the base class names of streamName and localName.
 656      *
 657      * @return  Return true iff the base class name compare.
 658      * @parameter streamName    Fully qualified class name.
 659      * @parameter localName     Fully qualified class name.
 660      * @parameter pkgSeparator  class names use either '.' or '/'.
 661      *
 662      * Only compare base class name to allow package renaming.
 663      */
 664     static boolean compareClassNames(String streamName,
 665                                      String localName,
 666                                      char pkgSeparator) {
 667         /* compare the class names, stripping off package names. */
 668         int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
 669         if (streamNameIndex < 0)
 670             streamNameIndex = 0;
 671 
 672         int localNameIndex = localName.lastIndexOf(pkgSeparator);
 673         if (localNameIndex < 0)
 674             localNameIndex = 0;
 675 
 676         return streamName.regionMatches(false, streamNameIndex,
 677                                         localName, localNameIndex,
 678                                         streamName.length() - streamNameIndex);
 679     }
 680 
 681     /*
 682      * Compare the types of two class descriptors.
 683      * They match if they have the same class name and suid
 684      */
 685     final boolean typeEquals(ObjectStreamClass_1_3_1 other) {
 686         return (suid == other.suid) &&
 687             compareClassNames(name, other.name, '.');
 688     }
 689 
 690     /*
 691      * Return the superclass descriptor of this descriptor.
 692      */
 693     final void setSuperclass(ObjectStreamClass_1_3_1 s) {
 694         superclass = s;
 695     }
 696 
 697     /*
 698      * Return the superclass descriptor of this descriptor.
 699      */
 700     final ObjectStreamClass_1_3_1 getSuperclass() {
 701         return superclass;
 702     }
 703 
 704     /*
 705      * Return whether the class has a writeObject method
 706      */
 707     final boolean hasWriteObject() {
 708         return hasWriteObjectMethod;
 709     }
 710 
 711     final boolean isCustomMarshaled() {
 712         return (hasWriteObject() || isExternalizable());
 713     }
 714 
 715     /*
 716      * Return true if all instances of 'this' Externalizable class
 717      * are written in block-data mode from the stream that 'this' was read
 718      * from. <p>
 719      *
 720      * In JDK 1.1, all Externalizable instances are not written
 721      * in block-data mode.
 722      * In JDK 1.2, all Externalizable instances, by default, are written
 723      * in block-data mode and the Externalizable instance is terminated with
 724      * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
 725      * instances.
 726      *
 727      * IMPLEMENTATION NOTE:
 728      *   This should have been a mode maintained per stream; however,
 729      *   for compatibility reasons, it was only possible to record
 730      *   this change per class. All Externalizable classes within
 731      *   a given stream should either have this mode enabled or
 732      *   disabled. This is enforced by not allowing the PROTOCOL_VERSION
 733      *   of a stream to he changed after any objects have been written.
 734      *
 735      * @see ObjectOutputStream#useProtocolVersion
 736      * @see ObjectStreamConstants#PROTOCOL_VERSION_1
 737      * @see ObjectStreamConstants#PROTOCOL_VERSION_2
 738      *
 739      * @since JDK 1.2
 740      */
 741     boolean hasExternalizableBlockDataMode() {
 742         return hasExternalizableBlockData;
 743     }
 744 
 745     /*
 746      * Return the ObjectStreamClass_1_3_1 of the local class this one is based on.
 747      */
 748     final ObjectStreamClass_1_3_1 localClassDescriptor() {
 749         return localClassDesc;
 750     }
 751 
 752     /*
 753      * Get the Serializability of the class.
 754      */
 755     boolean isSerializable() {
 756         return serializable;
 757     }
 758 
 759     /*
 760      * Get the externalizability of the class.
 761      */
 762     boolean isExternalizable() {
 763         return externalizable;
 764     }
 765 
 766     boolean isNonSerializable() {
 767         return ! (externalizable || serializable);
 768     }
 769 
 770     /*
 771      * Calculate the size of the array needed to store primitive data and the
 772      * number of object references to read when reading from the input
 773      * stream.
 774      */
 775     private void computeFieldInfo() {
 776         primBytes = 0;
 777         objFields = 0;
 778 
 779         for (int i = 0; i < fields.length; i++ ) {
 780             switch (fields[i].getTypeCode()) {
 781             case 'B':
 782             case 'Z':
 783                 primBytes += 1;
 784                 break;
 785             case 'C':
 786             case 'S':
 787                 primBytes += 2;
 788                 break;
 789 
 790             case 'I':
 791             case 'F':
 792                 primBytes += 4;
 793                 break;
 794             case 'J':
 795             case 'D' :
 796                 primBytes += 8;
 797                 break;
 798 
 799             case 'L':
 800             case '[':
 801                 objFields += 1;
 802                 break;
 803             }
 804         }
 805     }
 806 
 807     private static long computeStructuralUID(ObjectStreamClass_1_3_1 osc, Class cl) {
 808         ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
 809 
 810         long h = 0;
 811         try {
 812 
 813             if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
 814                 (cl.isInterface())){
 815                 return 0;
 816             }
 817 
 818             if (java.io.Externalizable.class.isAssignableFrom(cl)) {
 819                 return 1;
 820             }
 821 
 822             MessageDigest md = MessageDigest.getInstance("SHA");
 823             DigestOutputStream mdo = new DigestOutputStream(devnull, md);
 824             DataOutputStream data = new DataOutputStream(mdo);
 825 
 826             // Get SUID of parent
 827             Class parent = cl.getSuperclass();
 828             if ((parent != null))
 829             // SerialBug 1; acc. to spec the one for
 830             // java.lang.object
 831             // should be computed and put
 832             //     && (parent != java.lang.Object.class))
 833             {
 834                                 //data.writeLong(computeSerialVersionUID(null,parent));
 835                 data.writeLong(computeStructuralUID(lookup(parent), parent));
 836             }
 837 
 838             if (osc.hasWriteObject())
 839                 data.writeInt(2);
 840             else
 841                 data.writeInt(1);
 842 
 843             /* Sort the field names to get a deterministic order */
 844             // Field[] field = ObjectStreamClass_1_3_1.getDeclaredFields(cl);
 845 
 846             ObjectStreamField[] fields = osc.getFields();
 847 
 848             // Must make sure that the Field array we allocate
 849             // below is exactly the right size.  Bug fix for
 850             // 4397133.
 851             int numNonNullFields = 0;
 852             for (int i = 0; i < fields.length; i++)
 853                 if (fields[i].getField() != null)
 854                     numNonNullFields++;
 855 
 856             Field [] field = new java.lang.reflect.Field[numNonNullFields];
 857             for (int i = 0, fieldNum = 0; i < fields.length; i++) {
 858                 if (fields[i].getField() != null) {
 859                     field[fieldNum++] = fields[i].getField();
 860                 }
 861             }
 862 
 863             if (field.length > 1)
 864                 Arrays.sort(field, compareMemberByName);
 865 
 866             for (int i = 0; i < field.length; i++) {
 867                 Field f = field[i];
 868 
 869                                 /* Include in the hash all fields except those that are
 870                                  * transient
 871                                  */
 872                 int m = f.getModifiers();
 873                 //Serial 6
 874                 //if (Modifier.isTransient(m) || Modifier.isStatic(m))
 875                 // spec reference 00-01-06.pdf, 1.3.5.6, states non-static
 876                 // non-transient, public fields are mapped to Java IDL.
 877                 //
 878                 // Here's the quote from the first paragraph:
 879                 // Java non-static non-transient public fields are mapped to
 880                 // OMG IDL public data members, and other Java fields are
 881                 // not mapped.
 882 
 883                 // if (Modifier.isTransient(m) || Modifier.isStatic(m))
 884                 //     continue;
 885 
 886                 data.writeUTF(f.getName());
 887                 data.writeUTF(getSignature(f.getType()));
 888             }
 889 
 890             /* Compute the hash value for this class.
 891              * Use only the first 64 bits of the hash.
 892              */
 893             data.flush();
 894             byte hasharray[] = md.digest();
 895             // int minimum = Math.min(8, hasharray.length);
 896             // SerialBug 3: SHA computation is wrong; for loop reversed
 897             //for (int i = minimum; i > 0; i--)
 898             for (int i = 0; i < Math.min(8, hasharray.length); i++) {
 899                 h += (long)(hasharray[i] & 255) << (i * 8);
 900             }
 901         } catch (IOException ignore) {
 902             /* can't happen, but be deterministic anyway. */
 903             h = -1;
 904         } catch (NoSuchAlgorithmException complain) {
 905             throw new SecurityException(complain.getMessage());
 906         }
 907         return h;
 908     }
 909 
 910     /**
 911      * Compute the JVM signature for the class.
 912      */
 913     static String getSignature(Class clazz) {
 914         String type = null;
 915         if (clazz.isArray()) {
 916             Class cl = clazz;
 917             int dimensions = 0;
 918             while (cl.isArray()) {
 919                 dimensions++;
 920                 cl = cl.getComponentType();
 921             }
 922             StringBuffer sb = new StringBuffer();
 923             for (int i = 0; i < dimensions; i++) {
 924                 sb.append("[");
 925             }
 926             sb.append(getSignature(cl));
 927             type = sb.toString();
 928         } else if (clazz.isPrimitive()) {
 929             if (clazz == Integer.TYPE) {
 930                 type = "I";
 931             } else if (clazz == Byte.TYPE) {
 932                 type = "B";
 933             } else if (clazz == Long.TYPE) {
 934                 type = "J";
 935             } else if (clazz == Float.TYPE) {
 936                 type = "F";
 937             } else if (clazz == Double.TYPE) {
 938                 type = "D";
 939             } else if (clazz == Short.TYPE) {
 940                 type = "S";
 941             } else if (clazz == Character.TYPE) {
 942                 type = "C";
 943             } else if (clazz == Boolean.TYPE) {
 944                 type = "Z";
 945             } else if (clazz == Void.TYPE) {
 946                 type = "V";
 947             }
 948         } else {
 949             type = "L" + clazz.getName().replace('.', '/') + ";";
 950         }
 951         return type;
 952     }
 953 
 954     /*
 955      * Compute the JVM method descriptor for the method.
 956      */
 957     static String getSignature(Method meth) {
 958         StringBuffer sb = new StringBuffer();
 959 
 960         sb.append("(");
 961 
 962         Class[] params = meth.getParameterTypes(); // avoid clone
 963         for (int j = 0; j < params.length; j++) {
 964             sb.append(getSignature(params[j]));
 965         }
 966         sb.append(")");
 967         sb.append(getSignature(meth.getReturnType()));
 968         return sb.toString();
 969     }
 970 
 971     /*
 972      * Compute the JVM constructor descriptor for the constructor.
 973      */
 974     static String getSignature(Constructor cons) {
 975         StringBuffer sb = new StringBuffer();
 976 
 977         sb.append("(");
 978 
 979         Class[] params = cons.getParameterTypes(); // avoid clone
 980         for (int j = 0; j < params.length; j++) {
 981             sb.append(getSignature(params[j]));
 982         }
 983         sb.append(")V");
 984         return sb.toString();
 985     }
 986 
 987     /*
 988      * Cache of Class -> ClassDescriptor Mappings.
 989      */
 990     static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
 991 
 992     /*
 993      * findDescriptorFor a Class.  This looks in the cache for a
 994      * mapping from Class -> ObjectStreamClass mappings.  The hashCode
 995      * of the Class is used for the lookup since the Class is the key.
 996      * The entries are extended from java.lang.ref.SoftReference so the
 997      * gc will be able to free them if needed.
 998      */
 999     private static ObjectStreamClass_1_3_1 findDescriptorFor(Class cl) {
1000 
1001         int hash = cl.hashCode();
1002         int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1003         ObjectStreamClassEntry e;
1004         ObjectStreamClassEntry prev;
1005 
1006         /* Free any initial entries whose refs have been cleared */
1007         while ((e = descriptorFor[index]) != null && e.get() == null) {
1008             descriptorFor[index] = e.next;
1009         }
1010 
1011         /* Traverse the chain looking for a descriptor with ofClass == cl.
1012          * unlink entries that are unresolved.
1013          */
1014         prev = e;
1015         while (e != null ) {
1016             ObjectStreamClass_1_3_1 desc = (ObjectStreamClass_1_3_1)(e.get());
1017             if (desc == null) {
1018                 // This entry has been cleared,  unlink it
1019                 prev.next = e.next;
1020             } else {
1021                 if (desc.ofClass == cl)
1022                     return desc;
1023                 prev = e;
1024             }
1025             e = e.next;
1026         }
1027         return null;
1028     }
1029 
1030     /*
1031      * insertDescriptorFor a Class -> ObjectStreamClass_1_3_1 mapping.
1032      */
1033     private static void insertDescriptorFor(ObjectStreamClass_1_3_1 desc) {
1034         // Make sure not already present
1035         if (findDescriptorFor(desc.ofClass) != null) {
1036             return;
1037         }
1038 
1039         int hash = desc.ofClass.hashCode();
1040         int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1041         ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
1042         e.next = descriptorFor[index];
1043         descriptorFor[index] = e;
1044     }
1045 
1046     private static Field[] getDeclaredFields(final Class clz) {
1047         return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
1048             public Object run() {
1049                 return clz.getDeclaredFields();
1050             }
1051         });
1052     }
1053 
1054 
1055     /*
1056      * The name of this descriptor
1057      */
1058     private String name;
1059 
1060     /*
1061      * The descriptor of the supertype.
1062      */
1063     private ObjectStreamClass_1_3_1 superclass;
1064 
1065     /*
1066      * Flags for Serializable and Externalizable.
1067      */
1068     private boolean serializable;
1069     private boolean externalizable;
1070 
1071     /*
1072      * Array of persistent fields of this class, sorted by
1073      * type and name.
1074      */
1075     private ObjectStreamField[] fields;
1076 
1077     /*
1078      * Class that is a descriptor for in this virtual machine.
1079      */
1080     private Class ofClass;
1081 
1082     /*
1083      * True if descriptor for a proxy class.
1084      */
1085     boolean forProxyClass;
1086 
1087 
1088     /*
1089      * SerialVersionUID for this class.
1090      */
1091     private long suid = kDefaultUID;
1092     private String suidStr = null;
1093 
1094     /*
1095      * Actual (computed) SerialVersionUID for this class.
1096      */
1097     private long actualSuid = kDefaultUID;
1098     private String actualSuidStr = null;
1099 
1100     /*
1101      * The total number of bytes of primitive fields.
1102      * The total number of object fields.
1103      */
1104     int primBytes;
1105     int objFields;
1106 
1107     /* Internal lock object. */
1108     private Object lock = new Object();
1109 
1110     /* True if this class has/had a writeObject method */
1111     private boolean hasWriteObjectMethod;
1112 
1113     /* In JDK 1.1, external data was not written in block mode.
1114      * As of JDK 1.2, external data is written in block data mode. This
1115      * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
1116      *
1117      * @since JDK 1.2
1118      */
1119     private boolean hasExternalizableBlockData;
1120     Method writeObjectMethod;
1121     Method readObjectMethod;
1122     private transient Method writeReplaceObjectMethod;
1123     private transient Method readResolveObjectMethod;
1124 
1125     /*
1126      * ObjectStreamClass_1_3_1 that this one was built from.
1127      */
1128     private ObjectStreamClass_1_3_1 localClassDesc;
1129 
1130     /* Get the private static final field for serial version UID */
1131     // private static native long getSerialVersionUIDField(Class cl);
1132 
1133     /* The Class Object for java.io.Serializable */
1134     private static Class classSerializable = null;
1135     private static Class classExternalizable = null;
1136 
1137     /*
1138      * Resolve java.io.Serializable at load time.
1139      */
1140     static {
1141         try {
1142             classSerializable = Class.forName("java.io.Serializable");
1143             classExternalizable = Class.forName("java.io.Externalizable");
1144         } catch (Throwable e) {
1145             System.err.println("Could not load java.io.Serializable or java.io.Externalizable.");
1146         }
1147     }
1148 
1149     /** use serialVersionUID from JDK 1.1. for interoperability */
1150     private static final long serialVersionUID = -6120832682080437368L;
1151 
1152     /**
1153      * Set serialPersistentFields of a Serializable class to this value to
1154      * denote that the class has no Serializable fields.
1155      */
1156     public static final ObjectStreamField[] NO_FIELDS =
1157         new ObjectStreamField[0];
1158 
1159     /*
1160      * Entries held in the Cache of known ObjectStreamClass_1_3_1 objects.
1161      * Entries are chained together with the same hash value (modulo array size).
1162      */
1163     private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference
1164     {
1165         ObjectStreamClassEntry(ObjectStreamClass_1_3_1 c) {
1166             //super(c);
1167             this.c = c;
1168         }
1169         ObjectStreamClassEntry next;
1170 
1171         public Object get()
1172         {
1173             return c;
1174         }
1175         private ObjectStreamClass_1_3_1 c;
1176     }
1177 
1178     /*
1179      * Comparator object for Classes and Interfaces
1180      */
1181     private static Comparator compareClassByName =
1182         new CompareClassByName();
1183 
1184     private static class CompareClassByName implements Comparator {
1185         public int compare(Object o1, Object o2) {
1186             Class c1 = (Class)o1;
1187             Class c2 = (Class)o2;
1188             return (c1.getName()).compareTo(c2.getName());
1189         }
1190     }
1191 
1192     /*
1193      * Comparator object for Members, Fields, and Methods
1194      */
1195     private static Comparator compareMemberByName =
1196         new CompareMemberByName();
1197 
1198     private static class CompareMemberByName implements Comparator {
1199         public int compare(Object o1, Object o2) {
1200             String s1 = ((Member)o1).getName();
1201             String s2 = ((Member)o2).getName();
1202 
1203             if (o1 instanceof Method) {
1204                 s1 += getSignature((Method)o1);
1205                 s2 += getSignature((Method)o2);
1206             } else if (o1 instanceof Constructor) {
1207                 s1 += getSignature((Constructor)o1);
1208                 s2 += getSignature((Constructor)o2);
1209             }
1210             return s1.compareTo(s2);
1211         }
1212     }
1213 
1214     /* It is expensive to recompute a method or constructor signature
1215        many times, so compute it only once using this data structure. */
1216     private static class MethodSignature implements Comparator {
1217         Member member;
1218         String signature;      // cached parameter signature
1219 
1220         /* Given an array of Method or Constructor members,
1221            return a sorted array of the non-private members.*/
1222         /* A better implementation would be to implement the returned data
1223            structure as an insertion sorted link list.*/
1224         static MethodSignature[] removePrivateAndSort(Member[] m) {
1225             int numNonPrivate = 0;
1226             for (int i = 0; i < m.length; i++) {
1227                 if (! Modifier.isPrivate(m[i].getModifiers())) {
1228                     numNonPrivate++;
1229                 }
1230             }
1231             MethodSignature[] cm = new MethodSignature[numNonPrivate];
1232             int cmi = 0;
1233             for (int i = 0; i < m.length; i++) {
1234                 if (! Modifier.isPrivate(m[i].getModifiers())) {
1235                     cm[cmi] = new MethodSignature(m[i]);
1236                     cmi++;
1237                 }
1238             }
1239             if (cmi > 0)
1240                 Arrays.sort(cm, cm[0]);
1241             return cm;
1242         }
1243 
1244         /* Assumes that o1 and o2 are either both methods
1245            or both constructors.*/
1246         public int compare(Object o1, Object o2) {
1247             /* Arrays.sort calls compare when o1 and o2 are equal.*/
1248             if (o1 == o2)
1249                 return 0;
1250 
1251             MethodSignature c1 = (MethodSignature)o1;
1252             MethodSignature c2 = (MethodSignature)o2;
1253 
1254             int result;
1255             if (isConstructor()) {
1256                 result = c1.signature.compareTo(c2.signature);
1257             } else { // is a Method.
1258                 result = c1.member.getName().compareTo(c2.member.getName());
1259                 if (result == 0)
1260                     result = c1.signature.compareTo(c2.signature);
1261             }
1262             return result;
1263         }
1264 
1265         final private boolean isConstructor() {
1266             return member instanceof Constructor;
1267         }
1268         private MethodSignature(Member m) {
1269             member = m;
1270             if (isConstructor()) {
1271                 signature = ObjectStreamClass_1_3_1.getSignature((Constructor)m);
1272             } else {
1273                 signature = ObjectStreamClass_1_3_1.getSignature((Method)m);
1274             }
1275         }
1276     }
1277 }