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