ObjectStreamClass.java 22.1 KB
Newer Older
Tom Tromey committed
1 2
/* ObjectStreamClass.java -- Class used to write class information
   about serialized objects.
Mark Wielaard committed
3
   Copyright (C) 1998, 1999, 2000, 2001, 2003  Free Software Foundation, Inc.
Tom Tromey committed
4

5
   This file is part of GNU Classpath.
Tom Tromey committed
6

7 8 9 10
   GNU Classpath is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.
Tom Tromey committed
11
 
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   GNU Classpath is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Classpath; see the file COPYING.  If not, write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.

   Linking this library statically or dynamically with other modules is
   making a combined work based on this library.  Thus, the terms and
   conditions of the GNU General Public License cover the whole
   combination.

   As a special exception, the copyright holders of this library give you
   permission to link this library with independent modules to produce an
   executable, regardless of the license terms of these independent
   modules, and to copy and distribute the resulting executable under
   terms of your choice, provided that you also meet, for each linked
   independent module, the terms and conditions of the license of that
   module.  An independent module is a module which is not derived from
   or based on this library.  If you modify this library, you may extend
   this exception to your version of the library, but you are not
   obligated to do so.  If you do not wish to do so, delete this
   exception statement from your version. */
Tom Tromey committed
38 39 40 41 42 43 44 45 46


package java.io;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
Mark Wielaard committed
47
import java.lang.reflect.Proxy;
Tom Tromey committed
48 49 50
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Warren Levy committed
51
import java.security.Security;
Tom Tromey committed
52 53 54 55 56 57
import java.util.Arrays;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Vector;
import gnu.java.io.NullOutputStream;
import gnu.java.lang.reflect.TypeSignature;
Warren Levy committed
58
import gnu.java.security.provider.Gnu;
Tom Tromey committed
59 60 61 62 63


public class ObjectStreamClass implements Serializable
{
  /**
64 65 66 67 68 69 70 71 72 73
   * Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
   * If <code>cl</code> is null, or is not <code>Serializable</code>,
   * null is returned.  <code>ObjectStreamClass</code>'s are memorized;
   * later calls to this method with the same class will return the
   * same <code>ObjectStreamClass</code> object and no recalculation
   * will be done.
   *
   * @see java.io.Serializable
   */
  public static ObjectStreamClass lookup(Class cl)
Tom Tromey committed
74 75 76
  {
    if (cl == null)
      return null;
77
    if (! (Serializable.class).isAssignableFrom(cl))
78
      return null;
Tom Tromey committed
79

80
    return lookupForClassObject(cl);
Mark Wielaard committed
81 82 83 84 85 86 87
  }

  /**
   * This lookup for internal use by ObjectOutputStream.  Suppose
   * we have a java.lang.Class object C for class A, though A is not
   * serializable, but it's okay to serialize C.
   */
88
  static ObjectStreamClass lookupForClassObject(Class cl)
Mark Wielaard committed
89 90 91 92
  {
    if (cl == null)
      return null;

93
    ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl);
Tom Tromey committed
94 95 96 97

    if (osc != null)
      return osc;
    else
98 99 100 101 102
      {
	osc = new ObjectStreamClass(cl);
	classLookupTable.put(cl, osc);
	return osc;
      }
Tom Tromey committed
103 104 105 106
  }


  /**
107 108 109 110
   * Returns the name of the class that this
   * <code>ObjectStreamClass</code> represents.
   */
  public String getName()
Tom Tromey committed
111 112 113 114 115 116
  {
    return name;
  }


  /**
117 118 119 120 121 122 123 124 125
   * Returns the class that this <code>ObjectStreamClass</code>
   * represents.  Null could be returned if this
   * <code>ObjectStreamClass</code> was read from an
   * <code>ObjectInputStream</code> and the class it represents cannot
   * be found or loaded.
   *
   * @see java.io.ObjectInputStream
   */
  public Class forClass()
Tom Tromey committed
126 127 128 129 130 131
  {
    return clazz;
  }


  /**
132 133 134 135 136 137 138
   * Returns the serial version stream-unique identifier for the class
   * represented by this <code>ObjectStreamClass</code>.  This SUID is
   * either defined by the class as <code>static final long
   * serialVersionUID</code> or is calculated as specified in
   * Javasoft's "Object Serialization Specification" XXX: add reference
   */
  public long getSerialVersionUID()
Tom Tromey committed
139 140 141 142 143 144 145 146 147
  {
    return uid;
  }


  // Returns the serializable (non-static and non-transient) Fields
  // of the class represented by this ObjectStreamClass.  The Fields
  // are sorted by name.
  // XXX doc
148
  public ObjectStreamField[] getFields()
Tom Tromey committed
149 150
  {
    ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
151
    System.arraycopy(fields, 0, copy, 0, fields.length);
Tom Tromey committed
152 153 154 155 156 157 158 159 160
    return copy;
  }


  // XXX doc
  // Can't do binary search since fields is sorted by name and
  // primitiveness.
  public ObjectStreamField getField (String name)
  {
161 162
    for (int i = 0; i < fields.length; i++)
      if (fields[i].getName().equals(name))
Tom Tromey committed
163 164 165 166 167 168
	return fields[i];
    return null;
  }


  /**
169 170 171 172 173 174 175 176
   * Returns a textual representation of this
   * <code>ObjectStreamClass</code> object including the name of the
   * class it represents as well as that class's serial version
   * stream-unique identifier.
   *
   * @see #getSerialVersionUID()
   * @see #getName()
   */
177
  public String toString()
Tom Tromey committed
178 179 180 181 182 183 184 185 186 187 188
  {
    return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
  }


  // Returns true iff the class that this ObjectStreamClass represents
  // has the following method:
  //
  // private void writeObject (ObjectOutputStream)
  //
  // This method is used by the class to override default
189
  // serialization behavior.
190
  boolean hasWriteMethod()
Tom Tromey committed
191 192 193 194 195 196
  {
    return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
  }


  // Returns true iff the class that this ObjectStreamClass represents
197 198 199 200 201 202
  // has the following method:
  //
  // private void readObject (ObjectOutputStream)
  //
  // This method is used by the class to override default
  // serialization behavior.
203
  boolean hasReadMethod()
204
  {
205
    try
206
      {
207 208 209
	Class[] readObjectParams = { ObjectInputStream.class };
	forClass().getDeclaredMethod("readObject", readObjectParams);
	return true;
210
      }
211
    catch (NoSuchMethodException e)
212
      {
213
	return false;
214 215 216 217 218
      }
  }


  // Returns true iff the class that this ObjectStreamClass represents
Tom Tromey committed
219
  // implements Serializable but does *not* implement Externalizable.
220
  boolean isSerializable()
Tom Tromey committed
221 222 223 224 225 226 227
  {
    return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
  }


  // Returns true iff the class that this ObjectStreamClass represents
  // implements Externalizable.
228
  boolean isExternalizable()
Tom Tromey committed
229 230 231 232 233 234 235
  {
    return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
  }


  // Returns the <code>ObjectStreamClass</code> that represents the
  // class that is the superclass of the class this
Anthony Green committed
236
  // <code>ObjectStreamClass</code> represents.  If the superclass is
Tom Tromey committed
237
  // not Serializable, null is returned.
238
  ObjectStreamClass getSuper()
Tom Tromey committed
239 240 241 242 243 244 245 246 247
  {
    return superClass;
  }


  // returns an array of ObjectStreamClasses that represent the super
  // classes of CLAZZ and CLAZZ itself in order from most super to
  // CLAZZ.  ObjectStreamClass[0] is the highest superclass of CLAZZ
  // that is serializable.
248
  static ObjectStreamClass[] getObjectStreamClasses(Class clazz)
Tom Tromey committed
249
  {
250
    ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
Tom Tromey committed
251 252 253 254 255

    if (osc == null)
      return new ObjectStreamClass[0];
    else
      {
256
	Vector oscs = new Vector();
Tom Tromey committed
257

258 259 260 261 262 263 264 265
	while (osc != null)
	  {
	    oscs.addElement (osc);
	    osc = osc.getSuper();
	  }

	int count = oscs.size();
	ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
Tom Tromey committed
266

267 268
	for (int i = count - 1; i >= 0; i--)
	  sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i);
Tom Tromey committed
269

270 271
	return sorted_oscs;
      }
Tom Tromey committed
272 273 274 275 276 277 278
  }


  // Returns an integer that consists of bit-flags that indicate
  // properties of the class represented by this ObjectStreamClass.
  // The bit-flags that could be present are those defined in
  // ObjectStreamConstants that begin with `SC_'
279
  int getFlags()
Tom Tromey committed
280 281 282 283 284
  {
    return flags;
  }


285 286
  ObjectStreamClass(String name, long uid, byte flags,
		    ObjectStreamField[] fields)
Tom Tromey committed
287 288 289 290 291 292 293
  {
    this.name = name;
    this.uid = uid;
    this.flags = flags;
    this.fields = fields;
  }

294 295 296 297 298 299 300 301 302 303 304
  /**
   * This method builds the internal description corresponding to a Java Class.
   * As the constructor only assign a name to the current ObjectStreamClass instance,
   * that method sets the serial UID, chose the fields which will be serialized,
   * and compute the position of the fields in the serialized stream.
   *
   * @param cl The Java class which is used as a reference for building the descriptor.
   * @param superClass The descriptor of the super class for this class descriptor.
   * @throws InvalidClassException if an incompatibility between computed UID and
   * already set UID is found.
   */
305
  void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
Tom Tromey committed
306
  {
307
    this.clazz = cl;
Mark Wielaard committed
308

309
    long class_uid = getClassUID(cl);
310
    if (uid == 0)
Mark Wielaard committed
311 312
      uid = class_uid;
    else
313
      {
Mark Wielaard committed
314 315 316 317 318 319 320 321 322
	// Check that the actual UID of the resolved class matches the UID from 
	// the stream.    
	if (uid != class_uid)
	  {
	    String msg = cl + 
	      ": Local class not compatible: stream serialVersionUID="
	      + uid + ", local serialVersionUID=" + class_uid;
	    throw new InvalidClassException (msg);
	  }
323
      }
Mark Wielaard committed
324

325
    isProxyClass = clazz != null && Proxy.isProxyClass(clazz);
326
    this.superClass = superClass;
327 328
    calculateOffsets();
    
329 330
    try
      {
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);  

	if (exportedFields == null)
	  return;

	ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
	int i, j, k;

	/* We now check the import fields against the exported fields.
	 * There should not be contradiction (e.g. int x and String x)
	 * but extra virtual fields can be added to the class.
	 */

	Arrays.sort(exportedFields);

	i = 0; j = 0; k = 0;
	while (i < fields.length && j < exportedFields.length)
	  {
	    int comp = fields[i].getName().compareTo(exportedFields[j].getName());

	    if (comp < 0)
	      {
		newFieldList[k] = fields[i];
		fields[i].setPersistent(false);
		fields[i].setToSet(false);
		i++;
	      }
	    else if (comp > 0)
	      {
		/* field not found in imported fields. We add it
		 * in the list of supported fields.
		 */
		newFieldList[k] = exportedFields[j];
		newFieldList[k].setPersistent(true);
		newFieldList[k].setToSet(false);
		j++;
	      }
	    else
	      {
		if (!fields[i].getType().equals(exportedFields[j].getType()))
		  throw new InvalidClassException
		    ("serialPersistentFields must be compatible with" +
		     " imported fields (about " + fields[i].getName() + ")");
		newFieldList[k] = fields[i];
		fields[i].setPersistent(true);
		i++;
		j++;
	      }
	    k++;
	  }

	if (i < fields.length)
	  for (;i<fields.length;i++,k++)
	    {
	      fields[i].setPersistent(false);
	      fields[i].setToSet(false);
	      newFieldList[k] = fields[i];
	    }
	else
	  if (j < exportedFields.length)
	    for (;j<exportedFields.length;j++,k++)
	      {
		exportedFields[j].setPersistent(true);
		exportedFields[j].setToSet(false);
		newFieldList[k] = exportedFields[j];
	      }
	
	fields = new ObjectStreamField[k];
	System.arraycopy(newFieldList, 0, fields, 0, k);
400 401 402
      }
    catch (NoSuchFieldException ignore)
      {
403
	return;
404 405 406
      }
    catch (IllegalAccessException ignore)
      {
407
	return;
408
      }
Tom Tromey committed
409 410 411 412 413 414 415
  }

  void setSuperclass (ObjectStreamClass osc)
  {
    superClass = osc;
  }

416
  void calculateOffsets()
Tom Tromey committed
417 418 419 420 421 422 423
  {
    int i;
    ObjectStreamField field;
    primFieldSize = 0;
    int fcount = fields.length;
    for (i = 0; i < fcount; ++ i)
      {
424 425 426
	field = fields[i];

	if (! field.isPrimitive())
Tom Tromey committed
427
	  break;
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448

	field.setOffset(primFieldSize);
	switch (field.getTypeCode())
	  {
	  case 'B':
	  case 'Z':
	    ++ primFieldSize;
	    break;
	  case 'C':
	  case 'S':
	    primFieldSize += 2;
	    break;
	  case 'I':
	  case 'F':
	    primFieldSize += 4;
	    break;
	  case 'D':
	  case 'J':
	    primFieldSize += 8;
	    break;
	  }
Tom Tromey committed
449 450 451
      }

    for (objectFieldCount = 0; i < fcount; ++ i)
452
      fields[i].setOffset(objectFieldCount++);
Tom Tromey committed
453 454 455
  }


456
  private ObjectStreamClass(Class cl)
Tom Tromey committed
457 458 459
  {
    uid = 0;
    flags = 0;
460
    isProxyClass = Proxy.isProxyClass(cl);
Tom Tromey committed
461 462

    clazz = cl;
463 464 465
    name = cl.getName();
    setFlags(cl);
    setFields(cl);
Mark Wielaard committed
466
    // to those class nonserializable, its uid field is 0
467 468 469
    if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
      uid = getClassUID(cl);
    superClass = lookup(cl.getSuperclass());
Tom Tromey committed
470 471 472 473
  }


  // Sets bits in flags according to features of CL.
474
  private void setFlags(Class cl)
Tom Tromey committed
475
  {
476
    if ((java.io.Externalizable.class).isAssignableFrom(cl))
Tom Tromey committed
477
      flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
478
    else if ((java.io.Serializable.class).isAssignableFrom(cl))
Tom Tromey committed
479 480 481 482
      // only set this bit if CL is NOT Externalizable
      flags |= ObjectStreamConstants.SC_SERIALIZABLE;

    try
483 484 485 486 487 488 489 490 491 492 493 494 495
      {
	Method writeMethod = cl.getDeclaredMethod("writeObject",
						  writeMethodArgTypes);
	int modifiers = writeMethod.getModifiers();

	if (writeMethod.getReturnType() == Void.TYPE
	    && Modifier.isPrivate(modifiers)
	    && !Modifier.isStatic(modifiers))
	  flags |= ObjectStreamConstants.SC_WRITE_METHOD;
      }
    catch(NoSuchMethodException oh_well)
      {
      }
Tom Tromey committed
496 497 498 499 500
  }


  // Sets fields to be a sorted array of the serializable fields of
  // clazz.
501
  private void setFields(Class cl)
Tom Tromey committed
502
  {
503 504 505 506 507
    if (!isSerializable() || isExternalizable())
      {
	fields = NO_FIELDS;
	return;
      }
Tom Tromey committed
508 509 510

    try
      {
511 512 513 514 515 516 517 518
	Field serialPersistentFields =
	  cl.getDeclaredField("serialPersistentFields");
	serialPersistentFields.setAccessible(true);
	int modifiers = serialPersistentFields.getModifiers();

	if (Modifier.isStatic(modifiers)
	    && Modifier.isFinal(modifiers)
	    && Modifier.isPrivate(modifiers))
519
	  {
520 521 522 523 524 525 526
	    fields = getSerialPersistentFields(cl);
	    if (fields != null)
	      {
		Arrays.sort (fields);
		calculateOffsets();
		return;
	      }
527
	  }
Tom Tromey committed
528 529
      }
    catch (NoSuchFieldException ignore)
530 531
      {
      }
532 533 534
    catch (IllegalAccessException ignore)
      {
      }
Tom Tromey committed
535 536

    int num_good_fields = 0;
537
    Field[] all_fields = cl.getDeclaredFields();
Tom Tromey committed
538 539 540

    int modifiers;
    // set non-serializable fields to null in all_fields
541 542 543 544 545 546 547 548 549
    for (int i = 0; i < all_fields.length; i++)
      {
	modifiers = all_fields[i].getModifiers();
	if (Modifier.isTransient(modifiers)
	    || Modifier.isStatic(modifiers))
	  all_fields[i] = null;
	else
	  num_good_fields++;
      }
Tom Tromey committed
550 551 552

    // make a copy of serializable (non-null) fields
    fields = new ObjectStreamField[ num_good_fields ];
553
    for (int from = 0, to = 0; from < all_fields.length; from++)
Tom Tromey committed
554
      if (all_fields[from] != null)
555 556 557 558 559
	{
	  Field f = all_fields[from];
	  fields[to] = new ObjectStreamField(f.getName(), f.getType());
	  to++;
	}
Tom Tromey committed
560

561 562
    Arrays.sort(fields);
    calculateOffsets();
Tom Tromey committed
563 564
  }

565
  // Returns the serial version UID defined by class, or if that
Tom Tromey committed
566
  // isn't present, calculates value of serial version UID.
567
  private long getClassUID(Class cl)
Tom Tromey committed
568 569
  {
    try
570 571 572 573 574 575 576 577 578 579 580 581 582
      {
	// Use getDeclaredField rather than getField, since serialVersionUID
	// may not be public AND we only want the serialVersionUID of this
	// class, not a superclass or interface.
	Field suid = cl.getDeclaredField("serialVersionUID");
	suid.setAccessible(true);
	int modifiers = suid.getModifiers();

	if (Modifier.isStatic(modifiers)
	    && Modifier.isFinal(modifiers)
	    && suid.getType() == Long.TYPE)
	  return suid.getLong(null);
      }
Tom Tromey committed
583
    catch (NoSuchFieldException ignore)
584 585
      {
      }
586
    catch (IllegalAccessException ignore)
587 588
      {
      }
Tom Tromey committed
589 590 591

    // cl didn't define serialVersionUID, so we have to compute it
    try
592 593 594 595 596 597 598 599 600 601 602 603 604
      {
	MessageDigest md;
	try 
	  {
	    md = MessageDigest.getInstance("SHA");
	  }
	catch (NoSuchAlgorithmException e)
	  {
	    // If a provider already provides SHA, use it; otherwise, use this.
	    Gnu gnuProvider = new Gnu();
	    Security.addProvider(gnuProvider);
	    md = MessageDigest.getInstance("SHA");
	  }
Tom Tromey committed
605

606 607 608
	DigestOutputStream digest_out =
	  new DigestOutputStream(nullOutputStream, md);
	DataOutputStream data_out = new DataOutputStream(digest_out);
Mark Wielaard committed
609

610
	data_out.writeUTF(cl.getName());
Tom Tromey committed
611

612 613 614 615 616
	int modifiers = cl.getModifiers();
	// just look at interesting bits
	modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
				 | Modifier.INTERFACE | Modifier.PUBLIC);
	data_out.writeInt(modifiers);
Tom Tromey committed
617

618 619 620 621 622 623 624 625 626
	// Pretend that an array has no interfaces, because when array
	// serialization was defined (JDK 1.1), arrays didn't have it.
	if (! cl.isArray())
	  {
	    Class[] interfaces = cl.getInterfaces();
	    Arrays.sort(interfaces, interfaceComparator);
	    for (int i = 0; i < interfaces.length; i++)
	      data_out.writeUTF(interfaces[i].getName());
	  }
Tom Tromey committed
627

628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
	Field field;
	Field[] fields = cl.getDeclaredFields();
	Arrays.sort(fields, memberComparator);
	for (int i = 0; i < fields.length; i++)
	  {
	    field = fields[i];
	    modifiers = field.getModifiers();
	    if (Modifier.isPrivate(modifiers)
		&& (Modifier.isStatic(modifiers)
		    || Modifier.isTransient(modifiers)))
	      continue;

	    data_out.writeUTF(field.getName());
	    data_out.writeInt(modifiers);
	    data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
	  }
Tom Tromey committed
644

645 646 647 648 649 650 651
	// write class initializer method if present
	if (VMObjectStreamClass.hasClassInitializer(cl))
	  {
	    data_out.writeUTF("<clinit>");
	    data_out.writeInt(Modifier.STATIC);
	    data_out.writeUTF("()V");
	  }
Tom Tromey committed
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
	Constructor constructor;
	Constructor[] constructors = cl.getDeclaredConstructors();
	Arrays.sort (constructors, memberComparator);
	for (int i = 0; i < constructors.length; i++)
	  {
	    constructor = constructors[i];
	    modifiers = constructor.getModifiers();
	    if (Modifier.isPrivate(modifiers))
	      continue;

	    data_out.writeUTF("<init>");
	    data_out.writeInt(modifiers);

	    // the replacement of '/' with '.' was needed to make computed
	    // SUID's agree with those computed by JDK
	    data_out.writeUTF 
	      (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
	  }
Tom Tromey committed
671

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
	Method method;
	Method[] methods = cl.getDeclaredMethods();
	Arrays.sort(methods, memberComparator);
	for (int i = 0; i < methods.length; i++)
	  {
	    method = methods[i];
	    modifiers = method.getModifiers();
	    if (Modifier.isPrivate(modifiers))
	      continue;

	    data_out.writeUTF(method.getName());
	    data_out.writeInt(modifiers);

	    // the replacement of '/' with '.' was needed to make computed
	    // SUID's agree with those computed by JDK
	    data_out.writeUTF
	      (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
	  }
Tom Tromey committed
690

691 692 693 694 695 696
	data_out.close();
	byte[] sha = md.digest();
	long result = 0;
	int len = sha.length < 8 ? sha.length : 8;
	for (int i = 0; i < len; i++)
	  result += (long) (sha[i] & 0xFF) << (8 * i);
Tom Tromey committed
697

698 699
	return result;
      }
Tom Tromey committed
700
    catch (NoSuchAlgorithmException e)
701 702 703 704 705
      {
	throw new RuntimeException
	  ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
	   + cl.getName(), e);
      }
Tom Tromey committed
706
    catch (IOException ioe)
707 708 709
      {
	throw new RuntimeException(ioe);
      }
Tom Tromey committed
710 711
  }

712 713 714 715 716 717 718 719 720
  /**
   * Returns the value of CLAZZ's private static final field named
   * `serialPersistentFields'. It performs some sanity checks before
   * returning the real array. Besides, the returned array is a clean
   * copy of the original. So it can be modified.
   *
   * @param clazz Class to retrieve 'serialPersistentFields' from.
   * @return The content of 'serialPersistentFields'.
   */
721
  private ObjectStreamField[] getSerialPersistentFields(Class clazz) 
722
    throws NoSuchFieldException, IllegalAccessException
Tom Tromey committed
723
  {
724 725 726
    ObjectStreamField[] fieldsArray = null;
    ObjectStreamField[] o;

727 728 729 730
    // Use getDeclaredField rather than getField for the same reason
    // as above in getDefinedSUID.
    Field f = clazz.getDeclaredField("serialPersistentFields");
    f.setAccessible(true);
731 732

    int modifiers = f.getModifiers();
733 734 735
    if (!(Modifier.isStatic(modifiers) &&
	  Modifier.isFinal(modifiers) &&
	  Modifier.isPrivate(modifiers)))
736 737 738 739 740 741 742
      return null;
    
    o = (ObjectStreamField[]) f.get(null);
    
    if (o == null)
      return null;

743
    fieldsArray = new ObjectStreamField[ o.length ];
744 745 746
    System.arraycopy(o, 0, fieldsArray, 0, o.length);
    
    return fieldsArray;
Tom Tromey committed
747 748 749 750
  }

  public static final ObjectStreamField[] NO_FIELDS = {};

751 752 753 754
  private static Hashtable classLookupTable = new Hashtable();
  private static final NullOutputStream nullOutputStream = new NullOutputStream();
  private static final Comparator interfaceComparator = new InterfaceComparator();
  private static final Comparator memberComparator = new MemberComparator();
Tom Tromey committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
  private static final
    Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };

  private ObjectStreamClass superClass;
  private Class clazz;
  private String name;
  private long uid;
  private byte flags;

  // this field is package protected so that ObjectInputStream and
  // ObjectOutputStream can access it directly
  ObjectStreamField[] fields;

  // these are accessed by ObjectIn/OutputStream
  int primFieldSize = -1;  // -1 if not yet calculated
  int objectFieldCount;
771

Mark Wielaard committed
772 773
  boolean isProxyClass = false;

774 775 776
  // This is probably not necessary because this class is special cased already
  // but it will avoid showing up as a discrepancy when comparing SUIDs.
  private static final long serialVersionUID = -6120832682080437368L;
Mark Wielaard committed
777

Tom Tromey committed
778 779 780 781 782 783
}


// interfaces are compared only by name
class InterfaceComparator implements Comparator
{
784
  public int compare(Object o1, Object o2)
Tom Tromey committed
785
  {
786
    return ((Class) o1).getName().compareTo(((Class) o2).getName());
Tom Tromey committed
787 788 789 790 791 792 793 794
  }
}


// Members (Methods and Constructors) are compared first by name,
// conflicts are resolved by comparing type signatures
class MemberComparator implements Comparator
{
795
  public int compare(Object o1, Object o2)
Tom Tromey committed
796
  {
797 798
    Member m1 = (Member) o1;
    Member m2 = (Member) o2;
Tom Tromey committed
799

800
    int comp = m1.getName().compareTo(m2.getName());
Tom Tromey committed
801 802

    if (comp == 0)
803 804
      return TypeSignature.getEncodingOfMember(m1).
	compareTo(TypeSignature.getEncodingOfMember(m2));
Tom Tromey committed
805 806 807 808
    else
      return comp;
  }
}